]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup
authorHarlan Stenn <stenn@ntp.org>
Fri, 29 Nov 2013 06:12:37 +0000 (06:12 +0000)
committerHarlan Stenn <stenn@ntp.org>
Fri, 29 Nov 2013 06:12:37 +0000 (06:12 +0000)
bk: 529830555QlZI0fUjPAgBBlcoj8kcA

107 files changed:
ChangeLog
Makefile.am
bootstrap
configure.ac
ntpd/Makefile.am
ntpdate/Makefile.am
ntpdc/Makefile.am
ntpq/Makefile.am
ntpsnmpd/Makefile.am
scripts/Makefile.am
scripts/README
scripts/build/Makefile.am [new file with mode: 0755]
scripts/build/UpdatePoint [moved from scripts/UpdatePoint with 98% similarity]
scripts/build/VersionName [moved from scripts/VersionName with 100% similarity]
scripts/build/addChangeLogTag [moved from scripts/addChangeLogTag with 91% similarity]
scripts/build/check--help [moved from scripts/check--help with 100% similarity]
scripts/build/checkChangeLog [moved from scripts/checkChangeLog with 100% similarity]
scripts/build/fixautomakedepsmagic [moved from scripts/fixautomakedepsmagic with 100% similarity, mode: 0755]
scripts/build/genChangeLogTag [moved from scripts/genChangeLogTag with 72% similarity]
scripts/build/genCommitLog [moved from scripts/genCommitLog with 100% similarity]
scripts/build/genver [moved from scripts/genver with 96% similarity]
scripts/build/mkver.in [moved from scripts/mkver.in with 100% similarity]
scripts/calc_tickadj.in [deleted file]
scripts/calc_tickadj/Makefile.am [new file with mode: 0644]
scripts/calc_tickadj/calc_tickadj-opts [new file with mode: 0644]
scripts/calc_tickadj/calc_tickadj-opts.def [new file with mode: 0644]
scripts/calc_tickadj/calc_tickadj.in [new file with mode: 0644]
scripts/calc_tickadj/calc_tickadj.texi [new file with mode: 0644]
scripts/checktime.in [deleted file]
scripts/deprecated/freq_adj.in [moved from scripts/freq_adj.in with 100% similarity]
scripts/deprecated/hpadjtime.sh [moved from scripts/hpadjtime.sh with 100% similarity]
scripts/deprecated/html2man.in [moved from scripts/html2man.in with 100% similarity]
scripts/deprecated/ntp-close [moved from scripts/ntp-close with 100% similarity]
scripts/deprecated/ntp-groper [moved from scripts/ntp-groper with 100% similarity]
scripts/deprecated/ntp-restart [moved from scripts/ntp-restart with 100% similarity]
scripts/deprecated/ntp-status [moved from scripts/ntp-status with 100% similarity]
scripts/lib/Makefile.am [new file with mode: 0644]
scripts/lib/NTP/Mode6/Packet.pm [new file with mode: 0644]
scripts/lib/NTP/Util.pm [new file with mode: 0644]
scripts/ntp-wait.in [deleted file]
scripts/ntp-wait.mdoc.in [deleted file]
scripts/ntp-wait/Makefile.am [new file with mode: 0644]
scripts/ntp-wait/invoke-ntp-wait.menu [moved from scripts/invoke-ntp-wait.menu with 100% similarity]
scripts/ntp-wait/invoke-ntp-wait.texi [moved from scripts/invoke-ntp-wait.texi with 100% similarity]
scripts/ntp-wait/ntp-wait-opts [new file with mode: 0644]
scripts/ntp-wait/ntp-wait-opts.def [moved from scripts/ntp-wait-opts.def with 51% similarity]
scripts/ntp-wait/ntp-wait.1ntp-waitman [moved from scripts/ntp-wait.1ntp-waitman with 100% similarity]
scripts/ntp-wait/ntp-wait.1ntp-waitmdoc [moved from scripts/ntp-wait.1ntp-waitmdoc with 100% similarity]
scripts/ntp-wait/ntp-wait.html [moved from scripts/ntp-wait.html with 100% similarity]
scripts/ntp-wait/ntp-wait.in [new file with mode: 0644]
scripts/ntp-wait/ntp-wait.man.in [moved from scripts/ntp-wait.man.in with 100% similarity]
scripts/ntp-wait/ntp-wait.mdoc.in [new file with mode: 0644]
scripts/ntp-wait/ntp-wait.texi [moved from scripts/ntp-wait.texi with 79% similarity]
scripts/ntpsweep.in [deleted file]
scripts/ntpsweep/Makefile.am [new file with mode: 0644]
scripts/ntpsweep/ntpsweep-opts [new file with mode: 0644]
scripts/ntpsweep/ntpsweep-opts.def [new file with mode: 0644]
scripts/ntpsweep/ntpsweep.in [new file with mode: 0644]
scripts/ntpsweep/ntpsweep.mdoc.in [new file with mode: 0644]
scripts/ntpsweep/ntpsweep.texi [new file with mode: 0644]
scripts/ntptrace.in [deleted file]
scripts/ntptrace/Makefile.am [new file with mode: 0644]
scripts/ntptrace/ntptrace-opts [new file with mode: 0644]
scripts/ntptrace/ntptrace-opts.def [new file with mode: 0644]
scripts/ntptrace/ntptrace.in [new file with mode: 0755]
scripts/ntptrace/ntptrace.mdoc.in [new file with mode: 0644]
scripts/ntptrace/ntptrace.texi [new file with mode: 0644]
scripts/plot_summary-opts [new file with mode: 0644]
scripts/plot_summary-opts.def [new file with mode: 0644]
scripts/plot_summary.in
scripts/rc/README [new file with mode: 0644]
scripts/rc/ntpd [new file with mode: 0644]
scripts/rc/ntpwait [new file with mode: 0644]
scripts/rc/rc.d/TIMESYNC [new file with mode: 0644]
scripts/rc/rc.d/ntpd [new file with mode: 0644]
scripts/rc/rc.d/ntpwait [new file with mode: 0644]
scripts/rc1/postinstall
scripts/rc1/preinstall
scripts/rc1/preremove
scripts/rc1/prototype
scripts/rc1/xntp
scripts/rc2/local.ntpd
scripts/summary-opts [new file with mode: 0644]
scripts/summary-opts.def [new file with mode: 0644]
scripts/summary.in
scripts/t/1_common.t [new file with mode: 0644]
scripts/t/calc_tickadj.t [new file with mode: 0644]
scripts/t/lib/Test/Command.pm [new file with mode: 0644]
scripts/t/lib/Test/Mode6/Server.pm [new file with mode: 0644]
scripts/t/lib/Test/Util.pm [new file with mode: 0644]
scripts/t/ntp-wait.t [new file with mode: 0644]
scripts/t/ntpsweep.t [new file with mode: 0644]
scripts/t/ntptrace.t [new file with mode: 0644]
scripts/t/plot_summary.t [new file with mode: 0644]
scripts/t/summary.t [new file with mode: 0644]
scripts/t/util.t [new file with mode: 0644]
sntp/Makefile.am
sntp/ag-tpl/Mdoc.pm [new file with mode: 0644]
sntp/ag-tpl/agman-cmd.tpl [new file with mode: 0644]
sntp/ag-tpl/agmdoc-cmd.tpl [new file with mode: 0644]
sntp/ag-tpl/cmd-doc.tlib [new file with mode: 0644]
sntp/ag-tpl/mdoc-synopsis.tlib [new file with mode: 0644]
sntp/ag-tpl/mdoc2man [new file with mode: 0755]
sntp/ag-tpl/mdoc2texi
sntp/ag-tpl/perlopt.tpl [new file with mode: 0644]
sntp/m4/define_dir.m4
util/Makefile.am

index 3fe035d41ca984645941f418246cd6e5ed645c2c..d35d801ee0d6be9889509321d5c279481d14907e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+* Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup.
 (4.2.7p390) 2013/09/26 Released by Harlan Stenn <stenn@ntp.org>
 * [Bug 2482] Cleanup of droproot and jail support for Solaris.
 (4.2.7p389) 2013/09/24 Released by Harlan Stenn <stenn@ntp.org>
index 3a6102eb75f75979d6d2d27ef3b150db3331804b..97ac28a01afca11ecdf958394b7f17a93292fa21 100644 (file)
@@ -80,9 +80,9 @@ COPYRIGHT-please: $(srcdir)/COPYRIGHT
           Rather than determine our $(srcdir) from sntp/Makefile.am \
           COPYRIGHT-please serves as a fixed target.
 
-$(srcdir)/.checkChangeLog: $(srcdir)/ChangeLog $(srcdir)/scripts/checkChangeLog
+$(srcdir)/.checkChangeLog: $(srcdir)/ChangeLog $(srcdir)/scripts/build/checkChangeLog
        cd $(srcdir) && \
-       ./scripts/checkChangeLog
+       ./scripts/build/checkChangeLog
 
 sntp/built-sources-only: FRC.sntp
        @cd sntp && $(MAKE) $(AM_MAKEFLAGS) built-sources-only
@@ -113,7 +113,7 @@ CommitLog: FRC.CommitLog
        cd $(srcdir)                                    \
        && $(PATH_TEST) -e CommitLog                    \
                -a SCCS/s.ChangeSet -ot CommitLog       \
-       || scripts/genCommitLog
+       || scripts/build/genCommitLog
 
 libtool: $(LIBTOOL_DEPS)
        ./config.status --recheck
index 71ecb88d3e4f5de1be241ab6a85bfe0ac3890c31..92625d68da4dd7a37203ddc277b3d673e469a9ec 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -29,7 +29,8 @@
 
 set -e
 
-(cd sntp  && ../scripts/genver) || { echo scripts/genver failed ; exit 1; }
+(cd sntp  && ../scripts/build/genver) || { 
+    echo scripts/build/genver failed ; exit 1; }
 
 # autoreconf says:
 # The environment variables AUTOCONF, AUTOHEADER, AUTOMAKE, ACLOCAL,
index 4297760ccd5914cb56d7a1b12b8c06ff0814f1c1..ee5d0f1d8112a03bd3e0e6eb2b892bde61436298 100644 (file)
@@ -4237,21 +4237,40 @@ AC_CONFIG_FILES([ntpq/Makefile])
 AC_CONFIG_FILES([ntpsnmpd/Makefile])
 AC_CONFIG_FILES([parseutil/Makefile])
 AC_CONFIG_FILES([scripts/Makefile])
-AC_CONFIG_FILES([scripts/calc_tickadj],        [chmod +x scripts/calc_tickadj])
-AC_CONFIG_FILES([scripts/checktime],   [chmod +x scripts/checktime])
-AC_CONFIG_FILES([scripts/freq_adj],    [chmod +x scripts/freq_adj])
-AC_CONFIG_FILES([scripts/html2man],    [chmod +x scripts/html2man])
-AC_CONFIG_FILES([scripts/mkver],       [chmod +x scripts/mkver])
-AC_CONFIG_FILES([scripts/ntp-wait],    [chmod +x scripts/ntp-wait])
-AC_CONFIG_FILES([scripts/ntpsweep],    [chmod +x scripts/ntpsweep])
-AC_CONFIG_FILES([scripts/ntptrace],    [chmod +x scripts/ntptrace])
+AC_CONFIG_FILES([scripts/lib/Makefile])
+AC_CONFIG_FILES([scripts/calc_tickadj/calc_tickadj], [chmod +x scripts/calc_tickadj/calc_tickadj])
+AC_CONFIG_FILES([scripts/calc_tickadj/Makefile])
+AC_CONFIG_FILES([scripts/build/mkver], [chmod +x scripts/build/mkver])
+AC_CONFIG_FILES([scripts/ntp-wait/ntp-wait],   [chmod +x scripts/ntp-wait/ntp-wait])
+AC_CONFIG_FILES([scripts/ntp-wait/Makefile])
+AC_CONFIG_FILES([scripts/ntpsweep/ntpsweep],   [chmod +x scripts/ntpsweep/ntpsweep])
+AC_CONFIG_FILES([scripts/ntpsweep/Makefile])
+AC_CONFIG_FILES([scripts/ntptrace/ntptrace],   [chmod +x scripts/ntptrace/ntptrace])
+AC_CONFIG_FILES([scripts/ntptrace/Makefile])
 AC_CONFIG_FILES([scripts/ntpver],      [chmod +x scripts/ntpver])
 AC_CONFIG_FILES([scripts/plot_summary],        [chmod +x scripts/plot_summary])
 AC_CONFIG_FILES([scripts/summary],     [chmod +x scripts/summary])
+AC_CONFIG_FILES([scripts/build/Makefile])
 AC_CONFIG_FILES([tests/Makefile])
 AC_CONFIG_FILES([tests/libntp/Makefile])
 AC_CONFIG_FILES([util/Makefile])
 
+perllibdir="${datadir}/ntp/lib"
+AC_DEFINE_DIR([PERLLIBDIR], [perllibdir], [data dir])
+
+calc_tickadj_opts="scripts/calc_tickadj/calc_tickadj-opts"
+AC_SUBST_FILE([calc_tickadj_opts])
+ntp_wait_opts="scripts/ntp-wait/ntp-wait-opts"
+AC_SUBST_FILE([ntp_wait_opts])
+ntpsweep_opts="scripts/ntpsweep/ntpsweep-opts"
+AC_SUBST_FILE([ntpsweep_opts])
+ntptrace_opts="scripts/ntptrace/ntptrace-opts"
+AC_SUBST_FILE([ntptrace_opts])
+summary_opts="scripts/summary-opts"
+AC_SUBST_FILE([summary_opts])
+plot_summary_opts="scripts/plot_summary-opts"
+AC_SUBST_FILE([plot_summary_opts])
+
 AC_CONFIG_SUBDIRS([sntp])
 
 AC_OUTPUT
index f97e49b545898bee15b0eb3260a9d4cba7eec37e..f16e256c64e394d3f07966007d172a0df769f0a3 100644 (file)
@@ -400,7 +400,7 @@ $(srcdir)/invoke-ntpd.menu: $(srcdir)/invoke-ntpd.texi
 
 $(srcdir)/invoke-ntpd.texi: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpd-opts.def
-       $(top_srcdir)/scripts/check--help $@
+       $(top_srcdir)/scripts/build/check--help $@
 
 $(PROGRAMS): $(LDADD)
 
@@ -421,7 +421,7 @@ $(top_srcdir)/sntp/scm-rev:
        cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
 
 version.c: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/sntp/scm-rev
-       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpd
+       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpd
 
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
index 3b38d3eb0f06526793804d918f5f43bc54c41104..774cf297884a2a0a211d236bd0a9694948d73e6e 100644 (file)
@@ -29,7 +29,7 @@ $(top_srcdir)/sntp/scm-rev:
        cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
 
 version.c: $(ntpdate_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev
-       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpdate
+       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpdate
        
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
index a084382980b7d9af326ebbd4de180d4e4102ebfb..116a6cf4b6f8c88025ae753d22ca141905d58168 100644 (file)
@@ -113,7 +113,7 @@ $(srcdir)/invoke-ntpdc.menu: $(srcdir)/invoke-ntpdc.texi
        
 $(srcdir)/invoke-ntpdc.texi: $(srcdir)/ntpdc-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpdc-opts.def
-       $(top_srcdir)/scripts/check--help $@
+       $(top_srcdir)/scripts/build/check--help $@
 
 $(srcdir)/ntpdc.html: $(srcdir)/invoke-ntpdc.menu $(srcdir)/invoke-ntpdc.texi $(srcdir)/ntpdc.texi $(top_srcdir)/sntp/include/version.texi
        cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntpdc.html ntpdc.texi || true )
@@ -134,7 +134,7 @@ $(top_srcdir)/sntp/scm-rev:
        cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
 
 version.c: $(ntpdc_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev
-       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpdc
+       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpdc
        
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
index da460a1cc219854f83cb0513c4fddb350c639640..9fac3f5df177cfbf85f14d4f4dd49b8b330f441d 100644 (file)
@@ -106,7 +106,7 @@ $(srcdir)/invoke-ntpq.menu: $(srcdir)/invoke-ntpq.texi
        
 $(srcdir)/invoke-ntpq.texi: $(srcdir)/ntpq-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpq-opts.def
-       $(top_srcdir)/scripts/check--help $@
+       $(top_srcdir)/scripts/build/check--help $@
 
 $(srcdir)/ntpq.html: $(srcdir)/ntpq.texi $(top_srcdir)/sntp/include/version.texi
        cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpq.html ntpq.texi || true )
@@ -119,7 +119,7 @@ $(top_srcdir)/sntp/scm-rev:
        cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
 
 version.c: $(ntpq_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev
-       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpq
+       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpq
        
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
index 016824bb71d861423d40199ab3122987ed0b5694..dcaf5f0b020d4202eb6187ba95d44d228b17fae0 100644 (file)
@@ -105,7 +105,7 @@ $(srcdir)/invoke-ntpsnmpd.menu: $(srcdir)/invoke-ntpsnmpd.texi
        
 $(srcdir)/invoke-ntpsnmpd.texi: $(srcdir)/ntpsnmpd-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpsnmpd-opts.def
-       $(top_srcdir)/scripts/check--help $@
+       $(top_srcdir)/scripts/build/check--help $@
 
 $(srcdir)/ntpsnmpd.html: $(srcdir)/ntpsnmpd.texi $(top_srcdir)/sntp/include/version.texi
        cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpsnmpd.html ntpsnmpd.texi || true )
index 8b629292f0f87cafcaab7f3eec0aaa95c6556afc..3834f0678d1468ab178323f24e1218999b58373e 100644 (file)
@@ -1,8 +1,9 @@
+SUBDIRS = build calc_tickadj ntp-wait ntpsweep ntptrace lib
+
 NULL=
 
 man1_MANS=
 man8_MANS=
-man_MANS=      ntp-wait.$(NTP_WAIT_MS)
 
 DISTCLEANFILES=        config.log $(man_MANS)
 
@@ -17,97 +18,35 @@ std_def_list =                                                      \
        $(top_srcdir)/sntp/include/version.def                  \
        $(NULL)
 
-EXTRA_SCRIPTS =        ntp-wait ntptrace
-bin_SCRIPTS =  $(NTP_WAIT_DB) $(NTPTRACE_DB)
-libexec_SCRIPTS =      $(NTP_WAIT_DL) $(NTPTRACE_DL)
-sbin_SCRIPTS = $(NTP_WAIT_DS) $(NTPTRACE_DS)
-
-html_DATA=                             \
-       $(srcdir)/ntp-wait.html         \
-       $(NULL)
-
 noinst_SCRIPTS =               \
-       calc_tickadj            \
-       checktime               \
-       freq_adj                \
-       html2man                \
-       mkver                   \
-       ntpsweep                \
        ntpver                  \
        plot_summary            \
        summary                 \
        $(NULL)
 
-noinst_DATA=                           \
-       $(srcdir)/invoke-ntp-wait.menu  \
-       $(srcdir)/invoke-ntp-wait.texi  \
-       $(srcdir)/ntp-wait.man.in       \
-       $(srcdir)/ntp-wait.mdoc.in      \
-       $(NULL)
-
 install-data-local: install-html
 
 EXTRA_DIST =                   \
-       check--help             \
-       checkChangeLog          \
-       fixautomakedepsmagic    \
-       genCommitLog            \
-       genver                  \
-       hpadjtime.sh            \
-       invoke-ntp-wait.menu    \
-       invoke-ntp-wait.texi    \
+       deprecated              \
        monitoring              \
-       ntp-close               \
-       ntp-groper              \
-       ntp-restart             \
-       ntp-status              \
-       ntp-wait.1ntp-waitman   \
-       ntp-wait.1ntp-waitmdoc  \
-       ntp-wait.man.in         \
-       ntp-wait.mdoc.in        \
-       ntp-wait.html           \
-       ntp-wait.texi           \
-       ntp-wait-opts.def       \
-       rc1                     \
-       rc2                     \
+       rc                      \
        stats                   \
-       UpdatePoint             \
-       VersionName             \
        $(NULL)
 
-###
-
-$(srcdir)/ntp-wait.1ntp-waitman: $(srcdir)/ntp-wait-opts.def $(std_def_list)
-       $(run_ag) -DMAN_SECTION=1ntp-waitman -Tagman-cmd.tpl ntp-wait-opts.def
-
-$(srcdir)/ntp-wait.man.in: $(srcdir)/ntp-wait.1ntp-waitman $(top_srcdir)/sntp/scripts/mansec2subst.sed
-       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitman > $(srcdir)/ntp-wait.man.in+
-       mv $(srcdir)/ntp-wait.man.in+ $(srcdir)/ntp-wait.man.in
-
-###
-
-$(srcdir)/ntp-wait.1ntp-waitmdoc: $(srcdir)/ntp-wait-opts.def $(std_def_list)
-       $(run_ag) -DMAN_SECTION=1ntp-waitmdoc -Tagmdoc-cmd.tpl ntp-wait-opts.def
+OPTTPL = ../sntp/ag-tpl/perlopt.tpl
 
-$(srcdir)/ntp-wait.mdoc.in: $(srcdir)/ntp-wait.1ntp-waitmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
-       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitmdoc > $(srcdir)/ntp-wait.mdoc.in+
-       mv $(srcdir)/ntp-wait.mdoc.in+ $(srcdir)/ntp-wait.mdoc.in
+##
 
-###
+plot_summary: plot_summary-opts
 
-ntp-wait.$(NTP_WAIT_MS): $(srcdir)/ntp-wait.$(MANTAGFMT).in $(top_builddir)/config.status
-       $(top_builddir)/config.status --file=ntp-wait.$(NTP_WAIT_MS)+:$(srcdir)/ntp-wait.$(MANTAGFMT).in
-       mv ntp-wait.$(NTP_WAIT_MS)+ ntp-wait.$(NTP_WAIT_MS)
+plot_summary-opts: plot_summary-opts.def $(OPTTPL)
+       $(run_ag) plot_summary-opts.def 
 
-###
+##
 
-$(srcdir)/invoke-ntp-wait.menu: $(srcdir)/invoke-ntp-wait.texi
-       @: do-nothing action to avoid default SCCS get, .menu built with .texi
+summary: summary-opts
 
-$(srcdir)/invoke-ntp-wait.texi: $(srcdir)/ntp-wait-opts.def $(std_def_list)
-       $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntp-wait-opts.def
-       -$(top_srcdir)/../scripts/check--help $@
+summary-opts: summary-opts.def $(OPTTPL)
+       $(run_ag) summary-opts.def
 
-$(srcdir)/ntp-wait.html: $(srcdir)/invoke-ntp-wait.menu $(srcdir)/invoke-ntp-wait.texi $(srcdir)/ntp-wait.texi $(top_srcdir)/sntp/include/version.texi
-       cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntp-wait.html ntp-wait.texi || true )
 
index 673ddb4492e20c366744dd619527c91be06385be..9f70a6362888d675165f7f498c8e8af2fcb30332 100644 (file)
@@ -6,38 +6,26 @@ files in the parent directory for directions on how to use these files.
 
 calc_tickadj   Calculates "optimal" value for tick given ntp.drift file
 
-freq_adj       Calculates and optionally sets the clock frequency
-               based on ntp.drift .  For FreeBSD systems.
-
-mkver.in       script to create new version numbers for all sources
-
 monitoring      directory containing perl scripts useful for monitoring
                 operations
 
-rc1            start/stop scripts for NTP
-
-rc2            start/stop script for NTP
-
-ntp-close      find public stratum 2 servers that don't respond
-
-ntp-groper     script useful for reaching out and rattling the cages of
-               NTP peers to see if animals are inside the bars
-
-ntp-restart    script useful for killing and restarting the NTP daemon
+rc             start/stop scripts for NTP
 
 ntp-wait       Blocks until ntpd is in state 4 (synchronized).
-               Hopefully useful at boot time, to delay the boot sequence
+               Useful at boot time, to delay the boot sequence
                until after "ntpd -g" has set the time.
 
 ntpsweep       prints per host given in <file> the NTP stratum level, the
                clock offset in seconds, the daemon version, the operating
                system and the processor.
 
-ntpver         What version of the NTP daemon is running?
+ntptrace        Trace ntp peers of a server up to stratum 1.
 
 stats          directory containing awk and shell scripts useful for
                maintaining statistics summaries of clockstats, loopstats
                and peerstats files
 
-support         directory containing shell and perl scripts useful for
-                configuration and monitoring of NTP subnets
+summary         Generate summary files out of stat files produced by NTP
+                daemon.
+
+plot_summary    Plot summaries generated by summary script.
diff --git a/scripts/build/Makefile.am b/scripts/build/Makefile.am
new file mode 100755 (executable)
index 0000000..cf0a44a
--- /dev/null
@@ -0,0 +1,13 @@
+run_ag=                cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)"      \
+               autogen -L ../sntp/include -L ../sntp/ag-tpl
+
+noinst_SCRIPTS = mkver
+
+EXTRA_DIST =                   \
+       check--help             \
+       checkChangeLog          \
+       fixautomakedepsmagic    \
+       genCommitLog            \
+       genver                  \
+       UpdatePoint             \
+       VersionName             
similarity index 98%
rename from scripts/UpdatePoint
rename to scripts/build/UpdatePoint
index 309cdccb657e975445e7a39106fe4093652c4ce0..e3c5957dc6bc232df3e0ed6477675664ddaa2900 100755 (executable)
@@ -306,7 +306,7 @@ case "$newrcpoint" in
 esac
 
 printf "Previous version: "
-scripts/VersionName
+scripts/build/VersionName
 
 # apply packageinfo.sh changes
 
@@ -321,11 +321,11 @@ case "$test" in
  0)
     mv packageinfo.sh+ packageinfo.sh
     printf "Updated version: "
-    scripts/VersionName
+    scripts/build/VersionName
     ;;
  *)
     printf "Next version would be: "
-    scripts/VersionName -p ./packageinfo.sh+
+    scripts/build/VersionName -p ./packageinfo.sh+
     rm packageinfo.sh+
     ;;
 esac
similarity index 91%
rename from scripts/addChangeLogTag
rename to scripts/build/addChangeLogTag
index 0687ae8e9a3178dd900ed09e0b0fc47b1c5bec65..abbeae13893f2c0b4de118c23e0f29c13c566228 100755 (executable)
@@ -2,7 +2,7 @@
 
 set -e
 
-clt=`./scripts/genChangeLogTag`
+clt=`./scripts/build/genChangeLogTag`
 first=`line < ChangeLog`
 
 case "$first" in
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from scripts/fixautomakedepsmagic
rename to scripts/build/fixautomakedepsmagic
similarity index 72%
rename from scripts/genChangeLogTag
rename to scripts/build/genChangeLogTag
index c5a1e2f777393593e4c6952b0aaab33679ebd120..177a141c250eede4832066d894d8a1aafbbe4e42 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 
-dversion=`./scripts/VersionName`
+dversion=`./scripts/build/VersionName`
 tag=`date +"(${dversion}) %Y/%m/%d Released by Harlan Stenn <stenn@ntp.org>"`
 
 echo $tag
similarity index 96%
rename from scripts/genver
rename to scripts/build/genver
index 2b3d162107f12c86abad164448ed218af95f799d..c07324392cb0d3e349dea6dca3a9945febda12b3 100755 (executable)
@@ -35,7 +35,7 @@ set -e
 
 . ../packageinfo.sh
 
-dversion=`../scripts/VersionName -p ../packageinfo.sh`
+dversion=`../scripts/build/VersionName -p ../packageinfo.sh`
 
 set +e
 
similarity index 100%
rename from scripts/mkver.in
rename to scripts/build/mkver.in
diff --git a/scripts/calc_tickadj.in b/scripts/calc_tickadj.in
deleted file mode 100644 (file)
index 32eae12..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#! @PATH_PERL@
-#
-# drift of 104.8576 -> +1 tick.  Base  of 10000 ticks.
-#
-# 970306 HMS Deal with nanoseconds.  Fix sign of adjustments.
-
-$df="/etc/ntp.drift";
-# Assumes a 100Hz box with "tick" of 10000
-# Someday, we might call "tickadj" for better values...
-$base=10000;   # tick: 1,000,000 / HZ
-$cvt=104.8576; # 2 ** 20 / $base
-$v1=0.;
-$v2="";
-
-if (open(DF, $df))
-  {
-    if ($_=<DF>)
-      {
-        ($v1, $v2) = split;
-      }
-
-    while ($v1 < 0)
-      {
-        $v1 += $cvt;
-        $base--;
-      }
-
-    while ($v1 > $cvt)
-      {
-        $v1 -= $cvt;
-        $base++;
-      }
-  }
-
-printf("%.3f (drift)\n", $v1);
-
-printf("%d usec; %d nsec\n", $base, ($base + ($v1/$cvt)) * 1000);
-
diff --git a/scripts/calc_tickadj/Makefile.am b/scripts/calc_tickadj/Makefile.am
new file mode 100644 (file)
index 0000000..fbdea57
--- /dev/null
@@ -0,0 +1,79 @@
+run_ag=                cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)"      \
+               autogen -L ../../sntp/include -L ../../sntp/ag-tpl \
+               --writable
+
+EXTRA_DIST =   \
+       $(srcdir)/calc_tickadj.in \
+       $(srcdir)/calc_tickadj  \
+       $(srcdir)/calc_tickadj.1calc_tickadjman \
+       $(srcdir)/calc_tickadj.1calc_tickadjmdoc        \
+       $(srcdir)/calc_tickadj.texi     \
+       $(srcdir)/calc_tickadj.html     \
+       $(srcdir)/calc_tickadj.1                \
+       $(srcdir)/calc_tickadj-opts.def \
+       $(srcdir)/calc_tickadj-opts     \
+       $(srcdir)/invoke-calc_tickadj.texi      \
+       $(srcdir)/invoke-calc_tickadj.menu
+
+noinst_SCRIPTS = calc_tickadj
+
+noinst_DATA = \
+       $(srcdir)/calc_tickadj.in \
+       $(srcdir)/calc_tickadj.1calc_tickadjman \
+       $(srcdir)/calc_tickadj.1calc_tickadjmdoc        \
+       $(srcdir)/calc_tickadj.texi     \
+       $(srcdir)/calc_tickadj.html     \
+       $(srcdir)/calc_tickadj.1                \
+       $(srcdir)/calc_tickadj-opts.def \
+       $(srcdir)/calc_tickadj-opts     \
+       $(srcdir)/invoke-calc_tickadj.texi      \
+       $(srcdir)/invoke-calc_tickadj.menu
+
+calc_tickadj: $(srcdir)/calc_tickadj-opts
+
+$(srcdir)/calc_tickadj-opts: $(srcdir)/calc_tickadj-opts.def $(srcdir)/../../sntp/ag-tpl/perlopt.tpl
+       $(run_ag) calc_tickadj-opts.def
+
+clean-local: extra-clean
+
+extra-clean:
+       rm calc_tickadj
+       rm *.html *man *mdoc *.1 invoke-calc_tickadj.* calc_tickadj.*.in
+
+### Nroff
+
+$(srcdir)/calc_tickadj.1calc_tickadjman: $(srcdir)/calc_tickadj-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1calc_tickadjman -Tagman-cmd.tpl calc_tickadj-opts.def
+
+$(srcdir)/calc_tickadj.man.in: $(srcdir)/calc_tickadj.1calc_tickadjman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/calc_tickadj.1calc_tickadjman > $(srcdir)/calc_tickadj.man.in+
+       mv $(srcdir)/calc_tickadj.man.in+ $(srcdir)/calc_tickadj.man.in
+
+### Mdoc
+
+$(srcdir)/calc_tickadj.1calc_tickadjmdoc: $(srcdir)/calc_tickadj-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1calc_tickadjmdoc -Tagmdoc-cmd.tpl calc_tickadj-opts.def
+
+$(srcdir)/calc_tickadj.mdoc.in: $(srcdir)/calc_tickadj.1calc_tickadjmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/calc_tickadj.1calc_tickadjmdoc > $(srcdir)/calc_tickadj.mdoc.in+
+       mv $(srcdir)/calc_tickadj.mdoc.in+ $(srcdir)/calc_tickadj.mdoc.in
+
+### Manpage
+
+$(srcdir)/calc_tickadj.1: $(srcdir)/calc_tickadj.$(MANTAGFMT).in $(top_builddir)/config.status
+       $(top_builddir)/config.status --file=calc_tickadj.1+:$(srcdir)/calc_tickadj.$(MANTAGFMT).in
+       mv calc_tickadj.1+ calc_tickadj.1
+
+### Texinfo
+
+$(srcdir)/invoke-calc_tickadj.menu: $(srcdir)/invoke-calc_tickadj.texi
+       @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-calc_tickadj.texi: calc_tickadj $(srcdir)/calc_tickadj-opts.def $(std_def_list)
+       $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section calc_tickadj-opts.def
+       $(top_srcdir)/scripts/build/check--help $@
+
+### HTML
+
+$(srcdir)/calc_tickadj.html: $(srcdir)/invoke-calc_tickadj.menu $(srcdir)/invoke-calc_tickadj.texi $(srcdir)/calc_tickadj.texi
+       cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o calc_tickadj.html calc_tickadj.texi || true )
diff --git a/scripts/calc_tickadj/calc_tickadj-opts b/scripts/calc_tickadj/calc_tickadj-opts
new file mode 100644 (file)
index 0000000..c547a8e
--- /dev/null
@@ -0,0 +1,60 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'drift-file' => '/etc/ntp/drift',
+        'tick' => '',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'drift-file|d=s', 'tick|t=i',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+calc_tickadj - Calculates "optimal" value for tick given ntp drift file.
+USAGE: calc_tickadj [ -<flag> [<val>] | --<name>[{=| }<val>] ]... 
+
+    -d, --drift-file=str         Ntp drift file to use
+    -t, --tick=num               Tick value of this host
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
diff --git a/scripts/calc_tickadj/calc_tickadj-opts.def b/scripts/calc_tickadj/calc_tickadj-opts.def
new file mode 100644 (file)
index 0000000..5c965b2
--- /dev/null
@@ -0,0 +1,55 @@
+AutoGen Definitions perlopt;
+#include autogen-version.def
+
+prog-name  = calc_tickadj;
+prog-title = 'Calculates "optimal" value for tick given ntp drift file.';
+long-opts;
+gnu-usage;
+
+flag = {
+    name        = drift-file;
+    value       = d;
+    arg-type    = string;
+    arg-default = '/etc/ntp/drift';
+    descrip     = 'Ntp drift file to use';
+    doc         = 'Use the specified drift file for calculations';
+};
+
+flag = {
+    name        = tick;
+    value       = t;
+    arg-type    = number;
+    descrip     = 'Tick value of this host';
+    doc         = 'The current tick which to adjustment will be calculated';
+};
+
+doc-section = {
+    ds-type     = 'DESCRIPTION';
+    ds-format   = 'texi';
+    ds-text     = <<- _EndOfDoc
+The @code{calc_tickadj} script uses provided ntp drift file to generate optimal
+tick value. Generally, ntpd can do better job if the drift value is the
+smallest possible number. 
+
+The example output of 
+@example
+$ ./calc_tickadj
+81.699 (drift)
+9999 usec; 9999779 nsec
+$ cat /etc/ntp/drift
+-23.159
+@end example
+
+means the following. If tick on that box is 10,000, by making the value 9999
+we'll shift the box from its current drift of -23.159 to a drift of 81.699, and
+in doing so we'll speed the clock up a little every second instead of slowing
+the clock down a little.
+
+If 'tick' on that box is 10,000,000 then by setting it to 9999779 the drift
+value will be somewhere around 0.0.
+
+@code{calc_tickadj} tries to determine the the tick value by using
+@code{tickadj} program from ntp package. If this doesn't work you can specify
+current tick manually on command line.
+       _EndOfDoc;
+};
diff --git a/scripts/calc_tickadj/calc_tickadj.in b/scripts/calc_tickadj/calc_tickadj.in
new file mode 100644 (file)
index 0000000..38b826e
--- /dev/null
@@ -0,0 +1,59 @@
+#! @PATH_PERL@ -w
+#
+# drift of 104.8576 -> +1 tick.  Base  of 10000 ticks.
+#
+# 970306 HMS Deal with nanoseconds.  Fix sign of adjustments.
+package calc_tickadj;
+use strict;
+
+exit run(@ARGV) unless caller;
+
+sub run {
+    my $opts;
+    if (!processOptions(\@_, $opts)) {
+        usage(1);
+    };
+    my $drift_file = $opts->{'drift-file'};
+    my $tick       = $opts->{'tick'};
+
+    if (!$tick) {
+        my ($fl) = `tickadj`;
+        if (defined $fl && $fl =~ /(?:KERNEL|PRESET)?\s*tick\s+=\s+(\d+)/) {
+            $tick = $1;
+        }
+        else {
+            die "Could not get tick value, try manually with -t/--tick\n";
+        }
+    }
+
+    # Drift file is in PPM where Milion is actually 2**20
+    my $cvt   = (2 ** 20) / $tick;
+    my $drift = 0.;
+
+    open my $dfh, $drift_file or die "Could not open $drift_file: $!\n";
+
+    $drift = <$dfh>; 
+
+    close $dfh;
+    die "Invalid drift file value <$drift>" if $drift !~ /[+-]?\d+\.?[0-9]+/;
+
+    while ($drift < 0) {
+        $drift += $cvt;
+        $tick--;
+    }
+
+    while ($drift > $cvt) {
+        $drift -= $cvt;
+        $tick++;
+    }
+
+    printf "%.3f (drift)\n", $drift;
+    printf "%d usec; %d nsec\n", $tick, ($tick + ($drift/$cvt)) * 1000;
+
+    return 0;
+}
+
+@calc_tickadj_opts@
+
+1;
+__END__
diff --git a/scripts/calc_tickadj/calc_tickadj.texi b/scripts/calc_tickadj/calc_tickadj.texi
new file mode 100644 (file)
index 0000000..a12a626
--- /dev/null
@@ -0,0 +1,37 @@
+\input texinfo    @c -*-texinfo-*-
+@c %**start of header
+@setfilename calc_tickadj.info
+@settitle calc_tickadj User's Manual
+@include ../..//sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the use of @code{calc_tickadj}, a program from the NTP
+Project, that is used calculate optimal tick value based on given drift file.
+@end ifinfo
+
+@direntry
+* calc_tickadj: (calc_tickadj).     Calculate optimal tick value from a drift file.
+@end direntry
+
+@titlepage
+@title calc_tickadj User's Manual
+@subtitle calc_tickadj, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@node Top, calc_tickadj Description, (dir), (dir)
+@top calc_tickadj User's Manual
+
+This document describes the use of the NTP Project's @code{calc_tickadj} program.
+This document applies to version @value{VERSION} of @code{calc_tickadj}.
+
+@shortcontents
+
+@menu
+* calc_tickadj Description::       Description
+* calc_tickadj Invocation::        Invoking calc_tickadj
+@end menu
+
+@include invoke-calc_tickadj.texi
diff --git a/scripts/checktime.in b/scripts/checktime.in
deleted file mode 100644 (file)
index 2fe8f71..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#! @PATH_PERL@
-#! @PATH_PERL@ -d
-# 
-# This script compares the time of several machines with the
-# time on the local host.
-#
-# Use or modify it as you wish. 
-#
-# As the original author is only expecting 14 minutes of fame, 
-# leaving his name attached would be appreciated.
-#
-# R. Gary Cutbill <rgary@chrysalis.com>
-# 21 April 1999
-#
-$tol=2.0;
-$|=1;
-print "Time Check";
-
-open(HOSTS,"ypcat hosts.byaddr |");  # get a list of hosts from the yp server.
-
-while ($line=<HOSTS>) { # loop for each host getting the offset compared to localhost
-       ($addr,$host,$aliases)=split(/\s+/,$line,3);
-        $res=`/usr/local/bin/ntptrace -m 1 -r 1 -t 1 $host`;
-       print ".";
-       chop $res;
-        push (@results,$res);
-}
-print "\n";
-
-
-#
-# Sort the list of hosts, and print out there offsets
-# from the local host.  
-#
-@list=sort appropriately @results;
-foreach $i ( @list ) {
-
-   @dargs=split(/\s+/,$i);
-   if ( $dargs[1] eq "\*Timeout\*" ) { 
-     print "$i\n";
-     chop $dargs[0];
-     push(@down,$dargs[0]);
-   } else {
-     printf "%-25s %7s %3s %6s %10s %5s %8s %8s\n",@dargs;
-     if ( ( $dargs[4] > $tol ) || ( $dargs[4] < -$tol ) ) { 
-        chop $dargs[0];
-         push(@toofarout,$dargs[0]); }
-   }
-}
-#
-# When the above list finishes, hosts that are different by +/- $tol (two seconds)
-# are in @toofarout.  Hosts that are down are in @down. They are treated the same
-# way here, but you might want to do something different depending on your site.
-#
-# print a set of suggested rsh commands to run on the hosts that 
-# don't have "good" time.   "restartntp" is left as an excersize to the reader.
-# I usually use it to kill a running xntpd, ntpdate some server, and the start xntp
-# again.
-# 
-print "\nConsider:\n";
-foreach $i ( (@down,@toofarout) ) {
-    print "  rsh $i sudo restartntp\n";
-}
-
-
-#
-# sort the results from the list.  First by stratum, then by time deviation
-# Put hosts that didn't respond (timed out) on the bottom.
-#
-sub appropriately {
-    @af=split(/\s+/,$a);
-    @bf=split(/\s+/,$b);
-    $aba= ($af[4]<0)?-$af[4]:$af[4];
-    $abb= ($bf[4]<0)?-$bf[4]:$bf[4];
-
-        ( $af[1] ne $bf[1] ) ? $bf[1] cmp $af[1] :
-          ( ( $af[2] != $bf[2] ) ? ( $bf[2] <=> $af[2] ) :
-            ( ( $aba != $abb )  ? ( $abb <=> $aba ) : ($af[0] cmp $bf[0] ) ) );
-}
diff --git a/scripts/lib/Makefile.am b/scripts/lib/Makefile.am
new file mode 100644 (file)
index 0000000..f57bc34
--- /dev/null
@@ -0,0 +1,5 @@
+perllibdir = @PERLLIBDIR@
+
+nobase_perllib_DATA = NTP/Util.pm
+
+EXTRA_DIST = ${perllib_DATA}
diff --git a/scripts/lib/NTP/Mode6/Packet.pm b/scripts/lib/NTP/Mode6/Packet.pm
new file mode 100644 (file)
index 0000000..4b154cd
--- /dev/null
@@ -0,0 +1,222 @@
+package NTP::Mode6::Packet;
+use strict;
+use warnings;
+use Carp;
+use Exporter qw(import);
+our @EXPORT_OK = qw(OP_UNSPEC OP_READSTAT OP_READVAR OP_WRITEVAR OP_READCLOCK
+            OP_WRITECLOCK OP_SETTRAP OP_ASYNCMSG OP_CONFIGURE OP_SAVECONFIG
+            OP_READ_MRU OP_READ_ORDLIST_A OP_REQ_NONCE OP_UNSETTRAP);
+our %EXPORT_TAGS = (const => \@EXPORT_OK);
+
+use constant { 
+    OP_UNSPEC         => 0,  # unspeciffied
+    OP_READSTAT       => 1,  # read status
+    OP_READVAR        => 2,  # read variables
+    OP_WRITEVAR       => 3,  # write variables
+    OP_READCLOCK      => 4,  # read clock variables
+    OP_WRITECLOCK     => 5,  # write clock variables
+    OP_SETTRAP        => 6,  # set trap address
+    OP_ASYNCMSG       => 7,  # asynchronous message
+    OP_CONFIGURE      => 8,  # runtime configuration
+    OP_SAVECONFIG     => 9,  # save config to file
+    OP_READ_MRU       => 10, # retrieve MRU (mrulist)
+    OP_READ_ORDLIST_A => 11, # ordered list req. auth.
+    OP_REQ_NONCE      => 12, # request a client nonce
+    OP_UNSETTRAP      => 31, # unset trap
+};
+
+use constant {
+    SST_TS_UNSPEC    => 0, # unspec
+    SST_TS_ATOM      => 1, # pps
+    SST_TS_LF        => 2, # lf radio
+    SST_TS_HF        => 3, # hf radio
+    SST_TS_UHF       => 4, # uhf radio
+    SST_TS_LOCAL     => 5, # local
+    SST_TS_NTP       => 6, # ntp
+    SST_TS_UDPTIME   => 7, # other
+    SST_TS_WRSTWTCH  => 8, # wristwatch
+    SST_TS_TELEPHONE => 9, # telephone
+};
+
+use constant CTL_HEADER_LENGTH => 12;
+
+sub new {
+    my ($class, %opts) = @_;
+    my $self = {
+        version      => defined $opts{version}     ? $opts{version}     : 2,
+        leap         => defined $opts{leap}        ? $opts{leap}        : 0,
+        sequence     => defined $opts{sequence}    ? $opts{sequence}    : 0,
+        status       => defined $opts{status}      ? $opts{status}      : 0,
+        assoc_id     => defined $opts{assoc_id}    ? $opts{assoc_id}    : 0,
+        offset       => defined $opts{offset}      ? $opts{offset}      : 0,
+        error_bit    => defined $opts{is_error}    ? $opts{is_error}    : 0,
+        response_bit => defined $opts{is_response} ? $opts{is_response} : 0,
+        more_bit     => defined $opts{more}        ? $opts{more}        : 0,
+        opcode       => defined $opts{opcode}      ? $opts{opcode}      : 0,
+    };
+    my $ret = bless $self, $class;
+    if (defined $opts{data}) {
+        $self->data($opts{data});
+    }
+    else {
+        $self->data('');
+    }
+    return $ret;
+}
+
+sub version {
+    my ($self, $version) = @_;
+    $self->{version} = $version if defined $version;
+    return $self->{version};
+}
+
+sub leap {
+    my ($self, $leap) = @_;
+    $self->{leap} = 1 if $leap;
+    return $self->{leap};
+}
+
+sub assoc_id {
+    my ($self, $assoc_id) = @_;
+    $self->{assoc_id} = $assoc_id if defined $assoc_id;
+    return $self->{assoc_id};
+}
+
+sub is_response {
+    my ($self, $is_response) = @_;
+    $self->{response_bit} = 1 if defined $is_response;
+    return $self->{response_bit};
+}
+
+sub is_command {
+    my ($self, $is_command) = @_;
+    $self->{response_bit} = 0 if $is_command;
+    return !$self->{response_bit};
+}
+
+sub is_error {
+    my ($self, $is_error) = @_;
+    $self->{error_bit} = $is_error if defined $is_error;
+    return $self->{error_bit};
+}
+
+sub more {
+    my ($self, $is_more) = @_;
+    $self->{more_bit} = $is_more if defined $is_more;
+    return $self->{more_bit};
+}
+
+sub opcode {
+    my ($self, $opcode) = @_;
+    $self->{opcode} = $opcode if defined $opcode;
+    return $self->{opcode};
+}
+
+sub sequence {
+    my ($self, $sequence) = @_;
+    $self->{sequence} = $sequence if defined $sequence;
+    return $self->{sequence};
+}
+
+sub status {
+    my ($self, $status) = @_;
+    $self->{status} = $status if defined $status;
+    return $self->{status};
+}
+
+sub offset {
+    my ($self, $offset) = @_;
+    $self->{offset} = $offset if defined $offset;
+    return $self->{offset};
+}
+
+sub data_length {
+    my $self = shift;
+    return $self->{count};
+}
+
+sub data {
+    my ($self, $data) = @_;
+    if (defined $data) {
+        # TODO: prevent passing unicode?
+        $self->{count} = length $data;
+        $self->{data} = $data;
+    }
+    return $self->{data};
+}
+
+sub encode {
+    my $self = shift;
+
+    my $li_vn_mode = 0;
+    $li_vn_mode = ($self->leap() & 7) << 3;
+    $li_vn_mode |= ($self->version() & 7) << 3;
+    $li_vn_mode |= 6;
+
+    my $r_m_e_op = 0;
+    $r_m_e_op |= 0x80 if $self->is_response;
+    $r_m_e_op |= 0x40 if $self->is_error;
+    $r_m_e_op |= 0x20 if $self->more;
+    $r_m_e_op |= $self->opcode;
+
+    # Align to 32-bit boundary
+    my $padding = 0;
+    while (($self->data_length()+CTL_HEADER_LENGTH+$padding) & 3) {
+        $padding++;
+    }
+
+    my $msg = pack "CCnnnnnA*C$padding", $li_vn_mode, $r_m_e_op,
+        $self->sequence, $self->status, $self->assoc_id, $self->offset,
+        $self->data_length, $self->data, 0 x $padding;
+    return $msg;
+}
+
+sub decode {
+    my ($self, $msg) = @_;
+
+    my @res = unpack 'CCnnnnnA*', $msg;
+
+    my $li_vn_mode = shift @res;
+    my $mode = 3;
+    $mode = $li_vn_mode & 0x7;
+    croak 'Not an NTP Mode 6 packet' if $mode != 6;
+    $self->version(($li_vn_mode >> 3) & 0x7);
+    $self->leap(($li_vn_mode >> 6) & 0x3);
+
+    my $r_m_e_op = shift @res;
+    $r_m_e_op & 0x80 ? 
+        $self->is_response(1) :
+        $self->is_command(1);
+    $self->is_error(1) if $r_m_e_op & 0x40;
+    $self->more(1)     if $r_m_e_op & 0x20;
+    $self->opcode($r_m_e_op & 0x1f);
+
+    $self->sequence(shift @res);
+    $self->status(shift @res);
+    $self->assoc_id(shift @res);
+    $self->offset(shift @res);
+
+    my $count = shift @res;
+    $self->data(shift @res);
+    croak "count($count) != recieved data length(".$self->data_length.")"
+        if $self->data_length != $count;
+}
+
+sub eq {
+    my ($self, $ex) = @_;
+
+    croak 'Not a NTP::Mode6::Packet object' 
+        if ref $ex ne 'NTP::Mode6::Packet';
+
+    #TODO: move diag out of this method
+    my @diag;
+    for (grep { $_ !~ /^(data|count)$/ } keys %$self) {
+        push @diag, "$_: $self->{$_} | $ex->{$_}" 
+            if $self->{ $_ } != $ex->{ $_ };
+    }
+    push @diag, "data: $self->{data} | $ex->{data}" 
+        if $self->{data} ne $ex->{data};
+    return join "\n", @diag;
+}
+
+1;
diff --git a/scripts/lib/NTP/Util.pm b/scripts/lib/NTP/Util.pm
new file mode 100644 (file)
index 0000000..31fa575
--- /dev/null
@@ -0,0 +1,136 @@
+package NTP::Util;
+use strict;
+use warnings;
+use Exporter 'import';
+use Carp;
+
+our @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line);
+
+my $ntpq_path = 'ntpq';
+my $sntp_path = 'sntp';
+
+our $IP_AGNOSTIC;
+
+BEGIN {
+    require Socket;
+    if ($Socket::VERSION >= 1.94) {
+        Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET));
+        $IP_AGNOSTIC = 1;
+    }
+    else {
+        Socket->import(qw(inet_aton SOCK_RAW AF_INET));
+    }
+}
+
+my %obsolete_vars = (
+    phase          => 'offset',
+    rootdispersion => 'rootdisp',
+);
+
+sub ntp_read_vars {
+    my ($peer, $vars, $host) = @_;
+    my $do_all   = !@$vars;
+    my %out_vars = map {; $_ => undef } @$vars;
+
+    $out_vars{status_line} = {} if $do_all;
+
+    my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'";
+    $cmd .= " $host" if defined $host;
+    $cmd .= " |";
+
+    open my $fh, $cmd or croak "Could not start ntpq: $!";
+
+    while (<$fh>) {
+        return undef if /Connection refused/;
+
+        if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) {
+            $out_vars{status_line}{status} = $1;
+            $out_vars{status_line}{leap}   = $2;
+            $out_vars{status_line}{sync}   = $3;
+        }
+
+        while (/(\w+)=([^,]+),?\s/g) {
+            my ($var, $val) = ($1, $2);
+            $val =~ s/^"([^"]+)"$/$1/;
+            $var = $obsolete_vars{$var} if exists $obsolete_vars{$var};
+            if ($do_all) {
+                $out_vars{$var} = $val
+            }
+            else {
+                $out_vars{$var} = $val if exists $out_vars{$var};
+            }
+        }
+    }
+
+    close $fh or croak "running ntpq failed: $! (exit status $?)";
+    return \%out_vars;
+}
+
+sub do_dns {
+    my ($host) = @_;
+
+    if ($IP_AGNOSTIC) {
+        my ($err, $res);
+
+        ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW});
+        die "getaddrinfo failed: $err\n" if $err;
+
+        ($err, $res) = getnameinfo($res->{addr}, 0);
+        die "getnameinfo failed: $err\n" if $err;
+
+        return $res;
+    }
+    # Too old perl, do only ipv4
+    elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
+        return gethostbyaddr inet_aton($host), AF_INET;
+    }
+    else {
+        return;
+    }
+}
+
+sub ntp_peers {
+    my ($host) = @_;
+
+    my $cmd = "$ntpq_path -np $host |";
+
+    open my $fh, $cmd or croak "Could not start ntpq: $!";
+
+    <$fh> for 1 .. 2;
+
+    my @columns = qw(remote refid st t when poll reach delay offset jitter);
+    my @peers;
+    while (<$fh>) {
+        if (/(?:[\w\.\*-]+\s*){10}/) {
+            my $col = 0;
+            push @peers, { map {; $columns[ $col++ ] => $_ } split /(?<=.)\s+/ };
+        }
+        else {
+            #TODO return error (but not needed anywhere now)
+            warn "ERROR: $_";
+        }
+    }
+
+    close $fh or croak "running ntpq failed: $! (exit status $?)";
+    return \@peers;
+}
+
+# TODO: we don't need this but it would be nice to have all the line parsed
+sub ntp_sntp_line {
+    my ($host) = @_;
+
+    my $cmd = "$sntp_path $host |";
+    open my $fh, $cmd or croak "Could not start sntp: $!";
+
+    my ($offset, $stratum);
+    while (<$fh>) {
+        next if !/^\d{4}-\d\d-\d\d/;
+        chomp;
+        my @output = split / /;
+
+        $offset = $output[3];
+        ($stratum = pop @output) =~ s/s(\d{1,2})/$1/;
+    }
+    close $fh or croak "running sntp failed: $! (exit status $?)";
+    return ($offset, $stratum);
+}
diff --git a/scripts/ntp-wait.in b/scripts/ntp-wait.in
deleted file mode 100644 (file)
index 069365b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#! @PATH_PERL@ -w
-
-die "perl5 needed\n" unless ($] > 5);
-
-use Getopt::Std;
-
-$opt_n = 1000;                 # How many tries before we give up? (10 min+)
-$opt_s = 6;                    # Seconds to sleep between tries (6s = 10/min)
-$opt_v = 0;                    # Be verbose?
-
-getopts('n:s:v');
-
-$cmd = 'ntpq -c "rv 0"';
-
-$| = 1;                                # Autoflush output.
-
-print "Waiting for ntpd to synchronize...  " if ($opt_v);
-for ($i = 0; $i < $opt_n; ++$i) {
-    open(Q, $cmd." 2>&1 |") || die "Can't start ntpq: $!";
-    while(<Q>) {
-      chomp;
-      # the first line should be similar to:
-      # associd=0 status=0645 leap_none, sync_ntp, ...
-      if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/i) {
-       my $status = $1;
-       my $leap = $2;
-       my $sync = $3;
-       # print $_;
-       # print "status <$status>, leap <$leap>, sync <$sync>\n";
-       last if ($leap =~ /(sync|leap)_alarm/);
-       if ($leap =~ /leap_(none|((add|del)_sec))/) {
-         # We could check $sync here to make sure we like the source...
-         print "\bOK!\n" if ($opt_v);
-         exit 0;
-       }
-       print "\bUnexpected 'leap' status <$leap>\n";
-       exit 1;
-      }
-
-      if (/Connection refused/) {
-       print "\bntpd is not running!\n" if ($opt_v);
-       exit 1;
-      }
-
-      # Otherwise, we have a bigger problem.
-      print "\bUnexpected first line <$_>\n";
-      exit 1;
-    }
-    close(Q);
-    print "\b".substr("*+:.", $i % 4, 1) if ($opt_v);
-    sleep($opt_s);
-}
-print "\bNo!\nntpd did not synchronize.\n" if ($opt_v);
-exit 1;
diff --git a/scripts/ntp-wait.mdoc.in b/scripts/ntp-wait.mdoc.in
deleted file mode 100644 (file)
index 42910b0..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-.Dd September 26 2013
-.Dt NTP_WAIT @NTP_WAIT_MS@ User Commands
-.Os SunOS 5.10
-.\"  EDIT THIS FILE WITH CAUTION  (ntp-wait-opts.mdoc)
-.\"
-.\"  It has been AutoGen-ed  September 26, 2013 at 11:31:57 AM by AutoGen 5.18.1pre5
-.\"  From the definitions    ntp-wait-opts.def
-.\"  and the template file   agmdoc-cmd.tpl
-.Sh NAME
-.Nm ntp-wait
-.Nd Wait for ntpd to stabilize the system clock
-.Sh SYNOPSIS
-.Nm
-.\" Mixture of short (flag) options and long options
-.Op Fl flags
-.Op Fl flag Ar value
-.Op Fl \-option\-name Ar value
-.Pp
-All arguments must be options.
-.Pp
-.Sh DESCRIPTION
-.Nm
-will send at most
-.Ar num\-tries
-queries to
-.Xr ntpd 8 ,
-sleeping for
-.Ar secs\-between\-tries
-after each status return that says
-.Xr ntpd 8
-has not yet produced a synchronized and stable system clock.
-.Pp
-.Nm
-will do this quietly, unless the
-.Fl v
-flag is provided.
-.Sh "OPTIONS"
-.Bl -tag
-.It  \-n " \fInum\-tries\fP, " \-\- "=" \fInum\-tries\fP
-Number of times to check ntpd.
-This option takes an integer number as its argument.
-The default \fInum\-tries\fP for this option is:
-.ti +4
- 100
-.sp
-The maximum number of times we will check \fBntpd\fP to see if
-it has been able to synchronize and stabilize the system clock.
-.It  \-s " \fIsecs\-between\-tries\fP, " \-\- "=" \fIsecs\-between\-tries\fP
-How long to sleep between tries.
-This option takes an integer number as its argument.
-The default \fIsecs\-between\-tries\fP for this option is:
-.ti +4
- 6
-.sp
-We will sleep for \fIsecs\-between\-tries\fP after each query
-of \fBntpd\fP that returns "the time is not yet stable".
-.It  \-v ", " \-\-
-Be verbose.
-.sp
-By default, \fBntp\-wait\fP is silent.
-With this option, \fBntp\-wait\fP will provide status information.
-.It \-? , " \-\-help"
-Display usage information and exit.
-.It \-! , " \-\-more\-help"
-Pass the extended usage information through a pager.
-.It \-\-version "[={\fIv|c|n\fP}]"
-Output version of program and exit.  The default mode is `v', a simple
-version.  The `c' mode will print copyright information and `n' will
-print the full copyright notice.
-.El
-.Sh "OPTION PRESETS"
-Any option that is not marked as \fInot presettable\fP may be preset
-by loading values from environment variables named:
-.nf
-  \fBNTP_WAIT_<option\-name>\fP or \fBNTP_WAIT\fP
-.fi
-.ad
-cvt_prog='/usr/local/gnu/share/autogen/texi2mdoc'
-cvt_prog=`cd \`dirname "$cvt_prog"\` >/dev/null && pwd
-         `/`basename "$cvt_prog"`
-cd $tmp_dir
-test \-x "$cvt_prog" || die "'$cvt_prog' is not executable"
-{
-    list='synopsis description options option\-presets'
-    for f in $list ; do cat $f ; echo ; done
-    rm \-f $list name
-    list='implementation\-notes environment files examples exit\-status errors
-        compatibility see\-also conforming\-to history authors copyright bugs
-        notes'
-    for f in $list ; do cat $f ; echo ; done > .end\-doc
-    rm \-f $list
-    list=`ls \-1 *`' .end\-doc'
-    for f in $list ; do cat $f ; echo ; done
-    rm \-f $list
-} 1>.doc 2>/dev/null
-sed \-f .cmds .doc | /usr/local/gnu/bin/grep \-E \-v '^[       ]*$' | $cvt_prog
-.Sh "ENVIRONMENT"
-See \fBOPTION PRESETS\fP for configuration environment variables.
-.Sh "EXIT STATUS"
-One of the following exit values will be returned:
-.Bl -tag
-.It 0 " (EXIT_SUCCESS)"
-Successful program execution.
-.It 1 " (EXIT_FAILURE)"
-The operation failed or the command syntax was not valid.
-.It 70 " (EX_SOFTWARE)"
-libopts had an internal operational error.  Please report
-it to autogen\-users@lists.sourceforge.net.  Thank you.
-.El
-.Sh AUTHORS
-.An "Harlan Stenn"
-.Sh "COPYRIGHT"
-Copyright (C) 1970\-2013 The University of Delaware all rights reserved.
-This program is released under the terms of the NTP license, <http://ntp.org/license>.
-.Sh "BUGS"
-Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
-.Sh NOTES
-This document corresponds to version 4.2.7p390 of NTP.
-.Pp
-This manual page was \fIAutoGen\fP\-erated from the \fBntp\-wait\fP
-option definitions.
diff --git a/scripts/ntp-wait/Makefile.am b/scripts/ntp-wait/Makefile.am
new file mode 100644 (file)
index 0000000..b8f48ae
--- /dev/null
@@ -0,0 +1,87 @@
+man_MANS=      ntp-wait.$(NTP_WAIT_MS)
+
+bin_SCRIPTS =  $(NTP_WAIT_DB)
+libexec_SCRIPTS =      $(NTP_WAIT_DL)
+sbin_SCRIPTS = $(NTP_WAIT_DS)
+
+run_ag=                cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)"      \
+               autogen -L ../../sntp/include -L ../../sntp/ag-tpl \
+               --writable
+
+EXTRA_SCRIPTS = ntp-wait
+
+EXTRA_DIST =   \
+       $(srcdir)/ntp-wait.in \
+       $(srcdir)/ntp-wait      \
+       $(srcdir)/ntp-wait.1ntp-waitman \
+       $(srcdir)/ntp-wait.1ntp-waitmdoc        \
+       $(srcdir)/ntp-wait.texi \
+       $(srcdir)/ntp-wait.html \
+       $(srcdir)/ntp-wait.1            \
+       $(srcdir)/ntp-wait-opts.def     \
+       $(srcdir)/ntp-wait-opts \
+       $(srcdir)/invoke-ntp-wait.texi  \
+       $(srcdir)/invoke-ntp-wait.menu
+
+html_DATA = $(srcdir)/ntp-wait.html
+
+noinst_DATA = \
+       $(srcdir)/ntp-wait.in \
+       $(srcdir)/ntp-wait.1ntp-waitman \
+       $(srcdir)/ntp-wait.1ntp-waitmdoc        \
+       $(srcdir)/ntp-wait.texi \
+       $(srcdir)/ntp-wait.html \
+       $(srcdir)/ntp-wait.1            \
+       $(srcdir)/ntp-wait-opts.def     \
+       $(srcdir)/ntp-wait-opts \
+       $(srcdir)/invoke-ntp-wait.texi  \
+       $(srcdir)/invoke-ntp-wait.menu
+
+ntp-wait: $(srcdir)/ntp-wait-opts
+
+$(srcdir)/ntp-wait-opts: $(srcdir)/ntp-wait-opts.def ../../sntp/ag-tpl/perlopt.tpl
+       $(run_ag) ntp-wait-opts.def
+
+clean-local: extra-clean
+
+extra-clean:
+       rm ntp-wait
+       rm *.html *man *mdoc *.1 invoke-ntp-wait.* ntp-wait.man.in
+
+### Nroff
+
+$(srcdir)/ntp-wait.1ntp-waitman: $(srcdir)/ntp-wait-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntp-waitman -Tagman-cmd.tpl ntp-wait-opts.def
+
+$(srcdir)/ntp-wait.man.in: $(srcdir)/ntp-wait.1ntp-waitman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitman > $(srcdir)/ntp-wait.man.in+
+       mv $(srcdir)/ntp-wait.man.in+ $(srcdir)/ntp-wait.man.in
+
+### Mdoc
+
+$(srcdir)/ntp-wait.1ntp-waitmdoc: $(srcdir)/ntp-wait-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntp-waitmdoc -Tagmdoc-cmd.tpl ntp-wait-opts.def
+
+$(srcdir)/ntp-wait.mdoc.in: $(srcdir)/ntp-wait.1ntp-waitmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitmdoc > $(srcdir)/ntp-wait.mdoc.in+
+       mv $(srcdir)/ntp-wait.mdoc.in+ $(srcdir)/ntp-wait.mdoc.in
+
+### Manpage
+
+$(srcdir)/ntp-wait.1: $(srcdir)/ntp-wait.$(MANTAGFMT).in $(top_builddir)/config.status
+       $(top_builddir)/config.status --file=ntp-wait.1+:$(srcdir)/ntp-wait.$(MANTAGFMT).in
+       mv ntp-wait.1+ ntp-wait.1
+
+### Texinfo
+
+$(srcdir)/invoke-ntp-wait.menu: $(srcdir)/invoke-ntp-wait.texi
+       @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntp-wait.texi: ntp-wait $(srcdir)/ntp-wait-opts.def $(std_def_list)
+       $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntp-wait-opts.def
+       $(top_srcdir)/scripts/build/check--help $@
+
+### HTML
+
+$(srcdir)/ntp-wait.html: $(srcdir)/invoke-ntp-wait.menu $(srcdir)/invoke-ntp-wait.texi $(srcdir)/ntp-wait.texi $(top_srcdir)/sntp/include/version.texi
+       cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntp-wait.html ntp-wait.texi || true )
diff --git a/scripts/ntp-wait/ntp-wait-opts b/scripts/ntp-wait/ntp-wait-opts
new file mode 100644 (file)
index 0000000..49428d2
--- /dev/null
@@ -0,0 +1,62 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'tries' => '100',
+        'sleep' => '6',
+        'verbose' => '',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'tries|n=i', 'sleep|s=i', 'verbose|v',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+ntp-wait - Wait for ntpd to stabilize the system clock
+USAGE: ntp-wait [ -<flag> [<val>] | --<name>[{=| }<val>] ]... 
+
+    -n, --tries=num              Number of times to check ntpd
+    -s, --sleep=num              How long to sleep between tries
+    -v, --verbose                Be verbose
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
similarity index 51%
rename from scripts/ntp-wait-opts.def
rename to scripts/ntp-wait/ntp-wait-opts.def
index 72aaaa6988d59aff4c04322b33f203f0a2c8222b..e42f9f58bccb8f3021db310d3f2507fb543ed3d7 100644 (file)
@@ -1,53 +1,51 @@
 /* -*- Mode: Text -*- */
+autogen definitions perlopt;
 
-autogen definitions options;
-
-#include copyright.def
-// #include homerc.def
 #include autogen-version.def
 
 prog-name      = "ntp-wait";
 prog-title     = "Wait for ntpd to stabilize the system clock";
 package        = ntp;
+long-opts;
+gnu-usage;
 
 test-main;
 
-// #include       version.def
-
 flag = {
-  value                = n;
-  arg-type     = number;
-  arg-name     = "num-tries";
-  arg-default  = 100;
-  descrip      = "Number of times to check ntpd";
-  doc          = <<-  _EndOfDoc_
-       The maximum number of times we will check @code{ntpd} to see if
-       it has been able to synchronize and stabilize the system clock.
+    name        = tries;
+    value       = n;
+    arg-type    = number;
+    arg-default = 100;
+    descrip     = "Number of times to check ntpd";
+    doc         = <<-  _EndOfDoc_
+        The maximum number of times we will check @code{ntpd} to see if
+        it has been able to synchronize and stabilize the system clock.
        _EndOfDoc_;
 };
 
 flag = {
-  value                = s;
-  arg-type     = number;
-  arg-name     = "secs-between-tries";
-  arg-default  = 6;
-  descrip      = "How long to sleep between tries";
-  doc          = <<-  _EndOfDoc_
-       We will sleep for @file{secs-between-tries} after each query
-       of @code{ntpd} that returns "the time is not yet stable".
+    name        = sleep;
+    value       = s;
+    arg-type    = number;
+    arg-name    = "secs-between-tries";
+    arg-default = 6;
+    descrip     = "How long to sleep between tries";
+    doc         = <<-  _EndOfDoc_
+        We will sleep for @file{secs-between-tries} after each query
+        of @code{ntpd} that returns "the time is not yet stable".
        _EndOfDoc_;
 };
 
 flag = {
-  value                = v;
-  descrip      = "Be verbose";
-  doc          = <<-  _EndOfDoc_
-       By default, @code{ntp-wait} is silent.
-       With this option, @code{ntp-wait} will provide status information.
+    name    = verbose;
+    value   = v;
+    descrip = "Be verbose";
+    doc     = <<-  _EndOfDoc_
+        By default, @code{ntp-wait} is silent.
+        With this option, @code{ntp-wait} will provide status information.
        _EndOfDoc_;
 };
 
-/* explain: Additional information whenever the usage routine is invoked */
 explain = <<- _END_EXPLAIN
        _END_EXPLAIN;
 
@@ -70,6 +68,9 @@ has not yet produced a synchronized and stable system clock.
 will do this quietly, unless the
 .Fl v
 flag is provided.
+This can be useful at boot time, to delay the boot sequence until after
+.Ar ntpd -g 
+has set the time.
        _END_PROG_MDOC_DESCRIP;
 };
 
@@ -94,7 +95,7 @@ doc-section   = {
   ds-type      = 'NOTES';
   ds-format    = 'mdoc';
   ds-text      = <<- _END_MDOC_NOTES
-This document corresponds to version #VERSION# of NTP.
+This document corresponds to version @VERSION@ of NTP.
        _END_MDOC_NOTES;
 };
 
diff --git a/scripts/ntp-wait/ntp-wait.in b/scripts/ntp-wait/ntp-wait.in
new file mode 100644 (file)
index 0000000..d856b47
--- /dev/null
@@ -0,0 +1,65 @@
+#! @PATH_PERL@
+package ntp_wait;
+use 5.006_000;
+use strict;
+use warnings;
+use lib "@PERLLIBDIR@";
+use NTP::Util qw(ntp_read_vars);
+
+exit run(@ARGV) unless caller;
+
+sub run {
+    my $opts;
+    if (!processOptions(\@_, $opts)) {
+        usage(1);
+    };
+
+    my $tries   = $opts->{tries};      # How many tries before we give up? (10 min+)
+    my $sleep   = $opts->{sleep};      # Seconds to sleep between tries (6s = 10/min)
+    my $verbose = $opts->{verbose};    # Be verbose?
+
+    # Autoflush stdout
+    $| = 1;
+
+    print "Waiting for ntpd to synchronize...  " if $verbose;
+
+    for my $i (1 .. $tries) {
+        my $info = ntp_read_vars(0, []);
+
+        if (!defined $info) {
+            print "\bntpd is not running!\n" if $verbose;
+            return 1;
+        }
+
+        if (!exists $info->{status_line}{leap}) {
+            print "\bLeap status not avalaible\n";
+            return 1;
+        }
+
+        my $leap   = $info->{status_line}{leap};
+        my $sync   = $info->{status_line}{sync};
+
+        if ($leap =~ /(sync|leap)_alarm/) {
+            print "\b".(substr "*+:.", $i % 4, 1) if $verbose;
+            sleep $sleep if $i < $tries;
+            next;
+        }
+
+        if ($leap =~ /leap_(none|((add|del)_sec))/) {
+            # We could check $sync here to make sure we like the source...
+            print "\bOK!\n" if $verbose;
+            return 0;
+        }
+
+        print "\bUnexpected 'leap' status <$leap>\n";
+        return 1;
+    }
+
+    print "\bNo!\nntpd did not synchronize.\n" if $verbose;
+    return 1;
+}
+
+@ntp_wait_opts@
+
+1;
+__END__
diff --git a/scripts/ntp-wait/ntp-wait.mdoc.in b/scripts/ntp-wait/ntp-wait.mdoc.in
new file mode 100644 (file)
index 0000000..ad07ce5
--- /dev/null
@@ -0,0 +1,91 @@
+.Dd October 2 2013
+.Dt NTP_WAIT @NTP_WAIT_MS@ User Commands
+.Os FreeBSD 6.4-STABLE
+.\"  EDIT THIS FILE WITH CAUTION  (ntp-wait-opts.mdoc)
+.\"
+.\"  It has been AutoGen-ed  October  2, 2013 at 09:50:12 PM by AutoGen 5.18.1pre5
+.\"  From the definitions    ntp-wait-opts.def
+.\"  and the template file   agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntp-wait
+.Nd Wait for ntpd to stabilize the system clock
+.Sh SYNOPSIS
+.Nm
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+.Pp
+All arguments must be options.
+.Pp
+.Sh DESCRIPTION
+.Nm
+will send at most
+.Ar num\-tries
+queries to
+.Xr ntpd 8 ,
+sleeping for
+.Ar secs\-between\-tries
+after each status return that says
+.Xr ntpd 8
+has not yet produced a synchronized and stable system clock.
+.Pp
+.Nm
+will do this quietly, unless the
+.Fl v
+flag is provided.
+This can be useful at boot time, to delay the boot sequence until after
+.Ar ntpd \-g 
+has set the time.
+.Sh "OPTIONS"
+.Bl -tag
+.It  Fl n Ar number , Fl \-tries Ns = Ns Ar number 
+Number of times to check ntpd.
+This option takes an integer number as its argument.
+The default
+.Ar number
+for this option is:
+.ti +4
+ 100
+.sp
+        The maximum number of times we will check \fBntpd\fP to see if
+        it has been able to synchronize and stabilize the system clock.
+.It  Fl s Ar secs\-between\-tries , Fl \-sleep Ns = Ns Ar secs\-between\-tries 
+How long to sleep between tries.
+This option takes an integer number as its argument.
+The default
+.Ar secs\-between\-tries
+for this option is:
+.ti +4
+ 6
+.sp
+        We will sleep for \fIsecs\-between\-tries\fP after each query
+        of \fBntpd\fP that returns "the time is not yet stable".
+.It  Fl v , Fl \-verbose 
+Be verbose.
+.sp
+        By default, \fBntp\-wait\fP is silent.
+        With this option, \fBntp\-wait\fP will provide status information.
+.It Fl \&? , Fl \-help
+Display usage information and exit.
+.It Fl \&! , Fl \-more\-help
+Pass the extended usage information through a pager.
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error.  Please report
+it to autogen\-users@lists.sourceforge.net.  Thank you.
+.El
+.Sh AUTHORS
+.An "Harlan Stenn"
+.Sh NOTES
+This document corresponds to version @VERSION@ of NTP.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntp\-wait\fP
+option definitions.
similarity index 79%
rename from scripts/ntp-wait.texi
rename to scripts/ntp-wait/ntp-wait.texi
index df758bd8239fd5db41025357be7f6d3542bcad08..208639b3dff9a27d76a297c3459d31bb59ff857f 100644 (file)
@@ -2,7 +2,7 @@
 @c %**start of header
 @setfilename ntp-wait.info
 @settitle Ntp-wait User's Manual
-@include ../sntp/include/version.texi
+@include ../../sntp/include/version.texi
 @paragraphindent 2
 @c %**end of header
 
@@ -28,7 +28,6 @@ synchronize and stabilize the time on the current host.
 @c @vskip 0pt plus 1filll
 
 @node Top, ntp-wait Description, (dir), (dir)
-@top Simple Network Time Protocol User Manual
 
 This document describes the use of the NTP Project's @code{ntp-wait} program.
 
@@ -50,12 +49,4 @@ This document applies to version @value{VERSION} of @code{ntp-wait}.
 * ntp-wait Invocation::                Invoking ntp-wait
 @end menu
 
-@node ntp-wait Description
-@comment  node-name,  next,  previous,  up
-@section Description
-
-The @code{ntp-wait} program blocks until @code{ntpd} is in synchronized state.
-This can be useful at boot time, to delay the boot sequence until after
-@code{ntpd -g} has set the time.
-
 @include invoke-ntp-wait.texi
diff --git a/scripts/ntpsweep.in b/scripts/ntpsweep.in
deleted file mode 100644 (file)
index 650d2c5..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-#! @PATH_PERL@ -w
-#
-# $Id$
-#
-# DISCLAIMER
-# 
-# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.
-# 
-# Permission to use, copy, modify and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appears in all copies and
-# that both the copyright notice and this permission notice appear in
-# supporting documentation. This software is supported as is and without
-# any express or implied warranties, including, without limitation, the
-# implied warranties of merchantability and fitness for a particular
-# purpose. The name Origin B.V. must not be used to endorse or promote
-# products derived from this software without prior written permission.
-#
-# Hans Lambermont <ntpsweep@lambermont.dyndns.org>
-
-require 5.0;           # But actually tested on 5.004 ;)
-use Getopt::Long;       # GetOptions()
-use strict;
-
-my $version = 1.3;
-(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
-
-# Hardcoded paths/program names
-my $sntp = "sntp";
-my $ntpq = "ntpq";
-
-# no STDOUT buffering
-$| = 1;
-
-my ($help, $single_host, $showpeers, $maxlevel, $strip, $askversion);
-my $res = GetOptions("help!"      => \$help,
-                    "host=s"     => \$single_host,
-                    "peers!"     => \$showpeers,
-                    "maxlevel=s" => \$maxlevel,
-                    "strip=s"    => \$strip,
-                    "version!"   => \$askversion);
-
-if ($askversion) {
-    print("$version\n");
-    exit 0;
-}
-
-if ($help || ((@ARGV != 1) && !$single_host)) {
-    warn <<EOF;
-This is $program, version $version
-Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.  Disclaimer inside.
-
-Usage:
-  $program [--help|--peers|--strip <string>|--maxlevel <level>|--version] \\
-    <file>|[--host <hostname>]
-
-Description:
-  $program prints per host given in <file> the NTP stratum level, the
-  clock offset in seconds, the daemon version, the operating system and
-  the processor. Optionally recursing through all peers.
-
-Options:
---help
-    Print this short help text and exit.
---version
-    Print version ($version) and exit.
-<file>
-    Specify hosts file. File format is one hostname or ip number per line.
-    Lines beginning with # are considered as comment.
---host <hostname>
-    Speficy a single host, bypassing the need for a hosts file.
---peers
-    Recursively list all peers a host synchronizes to.
-    An '= ' before a peer means a loop. Recursion stops here.
---maxlevel <level>
-    Traverse peers up to this level (4 is a reasonable number).
---strip <string>
-    Strip <string> from hostnames.
-
-Examples:
-    $program myhosts.txt --strip .foo.com
-    $program --host some.host --peers --maxlevel 4
-EOF
-    exit 1;
-}
-
-my $hostsfile = shift;
-my (@hosts, @known_hosts);
-my (%known_host_info, %known_host_peers);
-
-sub read_hosts()
-{
-    local *HOSTS;
-    open (HOSTS, $hostsfile) ||
-       die "$program: FATAL: unable to read $hostsfile: $!\n";
-    while (<HOSTS>) {
-       next if /^\s*(#|$)/; # comment/empty
-       chomp;
-       push(@hosts, $_);
-    }
-    close(HOSTS);
-}
-
-# translate IP to hostname if possible
-sub ip2name {
-    my($ip) = @_;
-    my($addr, $name, $aliases, $addrtype, $length, @addrs);
-    $addr = pack('C4', split(/\./, $ip));
-    ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr($addr, 2);
-    if ($name) {
-        # return lower case name
-       return("\L$name");
-    } else {
-       return($ip);
-    }
-}
-
-# item_in_list($item, @list): returns 1 if $item is in @list, 0 if not
-sub item_in_list {
-    my($item, @list) = @_;
-    my($i);
-    foreach $i (@list) {
-       return 1 if ($item eq $i);
-    }
-    return 0;
-}
-
-sub scan_host($;$;$) {
-    my($host, $level, @trace) = @_;
-    my $stratum = 0;
-    my $offset = 0;
-    my $daemonversion = "";
-    my $system = "";
-    my $processor = "";
-    my @peers;
-    my $known_host = 0;
-
-    if (&item_in_list($host, @known_hosts)) {
-       $known_host = 1;
-    } else {
-       # sntp part
-        open my $sntp_pipe, "$sntp $host 2>/dev/null |" 
-            or die "Cannot open $sntp pipe: $1";
-    
-        while (<$sntp_pipe>) {
-            next if !/^\d{4}-\d\d-\d\d/;
-            my @output = split / /;
-
-            $offset = $output[3];
-            ($stratum = pop @output) =~ s/s(\d{1,2})/$1/;
-        }
-        close $sntp_pipe;
-    
-       # got answers ? If so, go on.
-       if ($stratum) {
-           # ntpq part
-           my $ntpqparams = "-c 'rv 0 processor,system,daemon_version'";
-           open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
-               die "Cannot open ntpq pipe: $!\n";
-           while (<NTPQ>) {
-               /daemon_version="(.*)"/ && do {
-                   $daemonversion = $1;
-               };
-               /system="([^"]*)"/ && do {
-                   $system = $1;
-               };
-               /processor="([^"]*)"/ && do {
-                   $processor = $1;
-               };
-           }
-           close(NTPQ);
-           
-           # Shorten daemon_version string.
-           $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//;
-           $daemonversion =~ s/version=//;
-           $daemonversion =~ s/(x|)ntpd //;
-           $daemonversion =~ s/(\(|\))//g;
-           $daemonversion =~ s/beta/b/;
-           $daemonversion =~ s/multicast/mc/;
-       
-           # Shorten system string
-           $system =~ s/UNIX\///;
-           $system =~ s/RELEASE/r/;
-           $system =~ s/CURRENT/c/;
-
-           # Shorten processor string
-           $processor =~ s/unknown//;
-       }
-    
-       # got answers ? If so, go on.
-       if ($daemonversion) {
-           # ntpq again, find out the peers this time
-           if ($showpeers) {
-               my $ntpqparams = "-pn";
-               open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
-                   die "Cannot open ntpq pipe: $!\n";
-               while (<NTPQ>) {
-                   /^No association ID's returned$/ && do {
-                       last;
-                   };
-                   /^     remote/ && do {
-                       next;
-                   };
-                   /^==/ && do {
-                       next;
-                   };
-                   /^( |x|\.|-|\+|#|\*|o)([^ ]+)/ && do {
-                       push(@peers, ip2name($2));
-                       next;
-                   };
-                   print "ERROR: $_";
-               }
-               close(NTPQ);
-           }
-       }
-    
-       # Add scanned host to known_hosts array
-       push(@known_hosts, $host);
-       if ($stratum) {
-           $known_host_info{$host} = sprintf("%2d %9.3f %-11s %-12s %s",
-               $stratum, $offset, substr($daemonversion,0,11),
-               substr($system,0,12), substr($processor,0,9));
-       } else {
-           # Stratum level 0 is consider invalid
-           $known_host_info{$host} = sprintf(" ?");
-       }
-       $known_host_peers{$host} = [@peers];
-    }
-
-    if ($stratum || $known_host) { # Valid or known host
-       my $printhost = ' ' x $level . $host;
-       # Shorten host string
-       if ($strip) {
-           $printhost =~ s/$strip//;
-       }
-       # append number of peers in brackets if requested and valid
-       if ($showpeers && ($known_host_info{$host} ne " ?")) {
-           $printhost .= " (" . @{$known_host_peers{$host}} . ")";
-       }
-       # Finally print complete host line
-       printf("%-32s %s\n",
-           substr($printhost,0,32), $known_host_info{$host});
-       if ($showpeers && (eval($maxlevel ? $level < $maxlevel : 1))) {
-           my $peer;
-           push(@trace, $host);
-           # Loop through peers
-           foreach $peer (@{$known_host_peers{$host}}) {
-               if (&item_in_list($peer, @trace)) {
-                   # we've detected a loop !
-                   $printhost = ' ' x ($level + 1) . "= " . $peer;
-                   # Shorten host string
-                   if ($strip) {
-                       $printhost =~ s/$strip//;
-                   }
-                   printf("%-32s %s\n",
-                       substr($printhost,0,32));
-               } else {
-                   if (substr($peer,0,3) ne "127") {
-                       &scan_host($peer, $level + 1, @trace);
-                   }
-               }
-           }
-       }
-    } else { # We did not get answers from this host
-       my $printhost = ' ' x $level . $host;
-       # Shorten host string
-       if ($strip) {
-           $printhost =~ s/$strip//;
-       }
-       printf("%-32s  ?\n", substr($printhost,0,32));
-    }
-}
-
-sub scan_hosts()
-{
-    my $host;
-    for $host (@hosts) {
-       my @trace;
-       push(@trace, $host);
-       scan_host($host, 0, @trace);
-    }
-}
-
-# Main program
-
-if ($single_host) {
-    push(@hosts, $single_host);
-} else {
-    &read_hosts($hostsfile);
-}
-
-# Print header
-print <<EOF;
-Host                             st offset(s) version     system       processor
---------------------------------+--+---------+-----------+------------+---------
-EOF
-
-&scan_hosts();
-
-exit 0;
diff --git a/scripts/ntpsweep/Makefile.am b/scripts/ntpsweep/Makefile.am
new file mode 100644 (file)
index 0000000..caaa327
--- /dev/null
@@ -0,0 +1,80 @@
+run_ag=                cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)"      \
+               autogen -L ../../sntp/include -L ../../sntp/ag-tpl \
+               --writable
+
+noinst_SCRIPTS = ntpsweep
+
+EXTRA_DIST =   \
+       $(srcdir)/ntpsweep.in \
+       $(srcdir)/ntpsweep.1ntpsweepman \
+       $(srcdir)/ntpsweep.1ntpsweepmdoc        \
+       $(srcdir)/ntpsweep.texi \
+       $(srcdir)/ntpsweep.html \
+       $(srcdir)/ntpsweep.1            \
+       $(srcdir)/ntpsweep-opts.def     \
+       $(srcdir)/ntpsweep-opts \
+       $(srcdir)/invoke-ntpsweep.texi  \
+       $(srcdir)/invoke-ntpsweep.menu
+
+html_DATA = $(srcdir)/ntpsweep.html
+
+noinst_DATA = \
+       $(srcdir)/ntpsweep.in \
+       $(srcdir)/ntpsweep.1ntpsweepman \
+       $(srcdir)/ntpsweep.1ntpsweepmdoc        \
+       $(srcdir)/ntpsweep.texi \
+       $(srcdir)/ntpsweep.html \
+       $(srcdir)/ntpsweep.1            \
+       $(srcdir)/ntpsweep-opts.def     \
+       $(srcdir)/ntpsweep-opts \
+       $(srcdir)/invoke-ntpsweep.texi  \
+       $(srcdir)/invoke-ntpsweep.menu
+
+ntpsweep: $(srcdir)/ntpsweep-opts
+
+$(srcdir)/ntpsweep-opts: $(srcdir)/ntpsweep-opts.def ../../sntp/ag-tpl/perlopt.tpl
+       $(run_ag) ntpsweep-opts.def
+
+clean-local: extra-clean
+
+extra-clean:
+       rm ntpsweep
+       rm *.html *man *mdoc *.1 invoke-ntpsweep.* ntpsweep.man.in
+
+### Nroff
+
+$(srcdir)/ntpsweep.1ntpsweepman: $(srcdir)/ntpsweep-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntpsweepman -Tagman-cmd.tpl ntpsweep-opts.def
+
+$(srcdir)/ntpsweep.man.in: $(srcdir)/ntpsweep.1ntpsweepman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpsweep.1ntpsweepman > $(srcdir)/ntpsweep.man.in+
+       mv $(srcdir)/ntpsweep.man.in+ $(srcdir)/ntpsweep.man.in
+
+### Mdoc
+
+$(srcdir)/ntpsweep.1ntpsweepmdoc: $(srcdir)/ntpsweep-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntpsweepmdoc -Tagmdoc-cmd.tpl ntpsweep-opts.def
+
+$(srcdir)/ntpsweep.mdoc.in: $(srcdir)/ntpsweep.1ntpsweepmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpsweep.1ntpsweepmdoc > $(srcdir)/ntpsweep.mdoc.in+
+       mv $(srcdir)/ntpsweep.mdoc.in+ $(srcdir)/ntpsweep.mdoc.in
+
+### Manpage
+
+$(srcdir)/ntpsweep.1: $(srcdir)/ntpsweep.$(MANTAGFMT).in $(top_builddir)/config.status
+       $(top_builddir)/config.status --file=ntpsweep.1+:$(srcdir)/ntpsweep.$(MANTAGFMT).in
+       mv ntpsweep.1+ ntpsweep.1
+
+### Texinfo
+
+$(srcdir)/invoke-ntpsweep.menu: $(srcdir)/invoke-ntpsweep.texi
+       @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntpsweep.texi: ntpsweep $(srcdir)/ntpsweep-opts.def $(std_def_list)
+       $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpsweep-opts.def
+       $(top_srcdir)/scripts/build/check--help $@
+
+### HTML
+
+$(srcdir)/ntpsweep.html: $(srcdir)/invoke-ntpsweep.menu $(srcdir)/invoke-ntpsweep.texi $(srcdir)/ntpsweep.texi $(top_srcdir)/sntp/include/version.texi
+       cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntpsweep.html ntpsweep.texi || true )
diff --git a/scripts/ntpsweep/ntpsweep-opts b/scripts/ntpsweep/ntpsweep-opts
new file mode 100644 (file)
index 0000000..91dcbd7
--- /dev/null
@@ -0,0 +1,72 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'host-list' => [],
+        'peers' => '',
+        'maxlevel' => '',
+        'strip' => '',
+        'host' => '',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '[hostfile]';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'host-list|l=s', 'peers|p', 'maxlevel|m=i',
+        'strip|s=s', 'host|h=s',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+ntpsweep - Print various informations about given ntp servers
+USAGE: ntpsweep [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [hostfile]
+
+    -l, --host-list=str          Host to execute actions on
+                                   - may appear multiple times
+    -p, --peers                  Recursively list all peers a host synchronizes to
+    -m, --maxlevel=num           Traverse peers up to this level (4 is a reasonable number)
+    -s, --strip=str              Strip this string from hostnames
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+
+    if ($argument && $argument =~ /^[^\[]/ && !@$args) {
+        print STDERR "Not enough arguments supplied (See --help/-?)\n";
+        exit 1;
+    }
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
diff --git a/scripts/ntpsweep/ntpsweep-opts.def b/scripts/ntpsweep/ntpsweep-opts.def
new file mode 100644 (file)
index 0000000..50dd537
--- /dev/null
@@ -0,0 +1,63 @@
+/* -*- Mode: Text -*- */
+autogen definitions perlopt;
+#include autogen-version.def
+
+prog-name      = 'ntpsweep';
+prog-title     = 'Print various informations about given ntp servers';
+package        = ntp;
+argument       = '[hostfile]';
+long-opts;
+gnu-usage;
+
+flag = {
+    name     = host-list;
+    value    = l;
+    arg-type = string;
+    max      = NOLIMIT;
+    stack-arg;
+    descrip  = 'Host to execute actions on';
+    doc      = <<- _EndOfDoc_
+            Use this option to specify the host on which this script operates.
+            May appear multiple times.
+       _EndOfDoc_;
+};
+
+flag = {
+    name    = peers;
+    value   = p;
+    descrip = 'Recursively list all peers a host synchronizes to';
+};
+
+flag = {
+    name     = maxlevel;
+    value    = m;
+    arg-type = number;
+    descrip  = 'Traverse peers up to this level (4 is a reasonable number)';
+};
+
+flag = {
+    name     = strip;
+    value    = s;
+    arg-type = string;
+    descrip  = 'Strip this string from hostnames';
+};
+
+/* Deprecated options */
+flag = {
+    name        = host;
+    value       = h;
+    arg-type    = string;
+    descrip     = 'Specify a single host';
+    deprecated;
+};
+
+doc-section    = {
+  ds-type      = 'DESCRIPTION';
+  ds-format    = 'mdoc';
+  ds-text      = <<- _END_PROG_MDOC_DESCRIP
+.Nm 
+prints per host the NTP stratum level, the clock offset in seconds, the daemon
+version, the operating system and the processor. Optionally recursing through
+all peers.
+       _END_PROG_MDOC_DESCRIP;
+};
diff --git a/scripts/ntpsweep/ntpsweep.in b/scripts/ntpsweep/ntpsweep.in
new file mode 100644 (file)
index 0000000..54e13e9
--- /dev/null
@@ -0,0 +1,203 @@
+#! @PATH_PERL@ -w
+#
+# $Id$
+#
+# DISCLAIMER
+# 
+# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.
+# 
+# Permission to use, copy, modify and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appears in all copies and
+# that both the copyright notice and this permission notice appear in
+# supporting documentation. This software is supported as is and without
+# any express or implied warranties, including, without limitation, the
+# implied warranties of merchantability and fitness for a particular
+# purpose. The name Origin B.V. must not be used to endorse or promote
+# products derived from this software without prior written permission.
+#
+# Hans Lambermont <ntpsweep@lambermont.dyndns.org>
+package ntpsweep;
+use 5.006_000;
+use strict;
+use lib "@PERLLIBDIR@";
+use NTP::Util qw(do_dns ntp_read_vars ntp_peers ntp_sntp_line);
+
+(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
+my ($showpeers, $maxlevel, $strip);
+my (%known_host_info, %known_host_peers);
+
+exit run(@ARGV) unless caller;
+
+sub run {
+    my $opts;
+    if (!processOptions(\@_, $opts) || 
+        (((@_ != 1) && !$opts->{host} && !@{$opts->{'host-list'}}))) {
+        usage(1);
+    };
+
+    # no STDOUT buffering
+    $| = 1;
+    ($showpeers, $maxlevel, $strip) = 
+        ($opts->{peers}, $opts->{maxlevel}, $opts->{strip});
+
+    my $hostsfile = shift;
+
+    # Main program
+
+    my @hosts;
+
+    if ($opts->{host}) {
+        push @hosts, $opts->{host};
+    }
+    else {
+        @hosts = read_hosts($hostsfile) if $hostsfile;
+        push @hosts, @{$opts->{'host-list'}};
+    }
+
+    # Print header
+    print <<EOF;
+Host                             st offset(s) version     system       processor
+--------------------------------+--+---------+-----------+------------+---------
+EOF
+
+    %known_host_info = ();
+    %known_host_peers = ();
+    scan_hosts(@hosts);
+
+    return 0;
+}
+
+sub scan_hosts {
+    my (@hosts) = @_;
+
+    my $host;
+    for $host (@hosts) {
+        scan_host($host, 0, $host => 1);
+    }
+}
+
+sub read_hosts {
+    my ($hostsfile) = @_;
+    my @hosts;
+
+    open my $hosts, $hostsfile 
+        or die "$program: FATAL: unable to read $hostsfile: $!\n";
+
+    while (<$hosts>) {
+        next if /^\s*(#|$)/; # comment/empty
+        chomp;
+        push @hosts, $_;
+    }
+
+    close $hosts;
+    return @hosts;
+}
+
+sub scan_host {
+    my ($host, $level, %trace) = @_;
+    my $stratum = 0;
+    my $offset = 0;
+    my $daemonversion = "";
+    my $system = "";
+    my $processor = "";
+    my @peers;
+    my $known_host = 0;
+
+    if (exists $known_host_info{$host}) {
+        $known_host = 1;
+    }
+    else {
+        ($offset, $stratum) = ntp_sntp_line($host);
+
+        # got answers ? If so, go on.
+        if ($stratum) {
+            my $vars = ntp_read_vars(0, [qw(processor system daemon_version)], $host) || {};
+            $daemonversion = $vars->{daemon_version};
+            $system        = $vars->{system};
+            $processor     = $vars->{processor};
+
+            # Shorten daemon_version string.
+            $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//;
+            $daemonversion =~ s/version=//;
+            $daemonversion =~ s/(x|)ntpd //;
+            $daemonversion =~ s/(\(|\))//g;
+            $daemonversion =~ s/beta/b/;
+            $daemonversion =~ s/multicast/mc/;
+
+            # Shorten system string
+            $system =~ s/UNIX\///;
+            $system =~ s/RELEASE/r/;
+            $system =~ s/CURRENT/c/;
+
+            # Shorten processor string
+            $processor =~ s/unknown//;
+        }
+
+        # got answers ? If so, go on.
+        if ($daemonversion) {
+            if ($showpeers) {
+                my @peers_tmp = ntp_peers($host);
+                for (@peers_tmp) {
+                    $_->{remote} =~ s/^(?: |x|\.|-|\+|#|\*|o)([^ ]+)/$1/;
+                    push @peers, $_->{remote};
+                }
+            }
+        }
+
+        # Add scanned host to known_hosts array
+        #push @known_hosts, $host;
+        if ($stratum) {
+            $known_host_info{$host} = sprintf "%2d %9.3f %-11s %-12s %s",
+                $stratum, $offset, (substr $daemonversion, 0, 11),
+                (substr $system, 0, 12), (substr $processor, 0, 9);
+        }
+        else {
+            # Stratum level 0 is consider invalid
+            $known_host_info{$host} = " ?";
+        }
+        $known_host_peers{$host} = [@peers];
+    }
+
+    if ($stratum || $known_host) { # Valid or known host
+        my $printhost = ' ' x $level . (do_dns($host) || $host);
+        # Shorten host string
+        if ($strip) {
+            $printhost =~ s/$strip//;
+        }
+        # append number of peers in brackets if requested and valid
+        if ($showpeers && ($known_host_info{$host} ne " ?")) {
+            $printhost .= " (" . @{$known_host_peers{$host}} . ")";
+        }
+        # Finally print complete host line
+        printf "%-32s %s\n",
+            (substr $printhost, 0, 32), $known_host_info{$host};
+        if ($showpeers && ($maxlevel ? $level < $maxlevel : 1)) {
+            $trace{$host} = 1;
+            # Loop through peers
+            foreach my $peer (@{$known_host_peers{$host}}) {
+                if (exists $trace{$peer}) {
+                    # we've detected a loop !
+                    $printhost = ' ' x ($level + 1) . "= " . $peer;
+                    # Shorten host string
+                    $printhost =~ s/$strip// if $strip;
+                    printf "%-32s\n", substr $printhost, 0, 32;
+                } else {
+                    if ((substr $peer, 0, 3) ne "127") {
+                        scan_host($peer, $level + 1, %trace);
+                    }
+                }
+            }
+        }
+    }
+    else { # We did not get answers from this host
+        my $printhost = ' ' x $level . (do_dns($host) || $host);
+        $printhost =~ s/$strip// if $strip;
+        printf "%-32s  ?\n", substr $printhost, 0, 32;
+    }
+}
+
+@ntpsweep_opts@
+
+1;
+__END__
diff --git a/scripts/ntpsweep/ntpsweep.mdoc.in b/scripts/ntpsweep/ntpsweep.mdoc.in
new file mode 100644 (file)
index 0000000..f6f1766
--- /dev/null
@@ -0,0 +1,71 @@
+.Dd October 2 2013
+.Dt NTPSWEEP 1ntpsweepmdoc User Commands
+.Os FreeBSD 6.4-STABLE
+.\"  EDIT THIS FILE WITH CAUTION  (ntpsweep-opts.mdoc)
+.\"
+.\"  It has been AutoGen-ed  October  2, 2013 at 09:50:21 PM by AutoGen 5.18.1pre5
+.\"  From the definitions    ntpsweep-opts.def
+.\"  and the template file   agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntpsweep
+.Nd Print various informations about given ntp servers
+.Sh SYNOPSIS
+.Nm
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+[hostfile]
+.Pp
+.Sh DESCRIPTION
+.Nm 
+prints per host the NTP stratum level, the clock offset in seconds, the daemon
+version, the operating system and the processor. Optionally recursing through
+all peers.
+.Sh "OPTIONS"
+.Bl -tag
+.It  Fl l Ar string , Fl \-host\-list Ns = Ns Ar string 
+Host to execute actions on.
+This option may appear an unlimited number of times.
+.sp
+            Use this option to specify the host on which this script operates.
+            May appear multiple times.
+.It  Fl p , Fl \-peers 
+Recursively list all peers a host synchronizes to.
+.sp
+This option has not been fully documented.
+.It  Fl m Ar number , Fl \-maxlevel Ns = Ns Ar number 
+Traverse peers up to this level (4 is a reasonable number).
+This option takes an integer number as its argument.
+.sp
+This option has not been fully documented.
+.It  Fl s Ar string , Fl \-strip Ns = Ns Ar string 
+Strip this string from hostnames.
+.sp
+This option has not been fully documented.
+.It  Fl h Ar string , Fl \-host Ns = Ns Ar string 
+Specify a single host.
+.sp
+This option has not been fully documented.
+.sp
+.B
+NOTE: THIS OPTION IS DEPRECATED
+.It Fl \&? , Fl \-help
+Display usage information and exit.
+.It Fl \&! , Fl \-more\-help
+Pass the extended usage information through a pager.
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error.  Please report
+it to autogen\-users@lists.sourceforge.net.  Thank you.
+.El
+.Sh "NOTES"
+This manual page was \fIAutoGen\fP\-erated from the \fBntpsweep\fP
+option definitions.
diff --git a/scripts/ntpsweep/ntpsweep.texi b/scripts/ntpsweep/ntpsweep.texi
new file mode 100644 (file)
index 0000000..e408065
--- /dev/null
@@ -0,0 +1,43 @@
+\input texinfo    @c -*-texinfo-*-
+@c %**start of header
+@setfilename ntpsweep.info
+@settitle ntpsweep User's Manual
+@include ../../sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the use of @code{ntpsweep},
+a program from
+the NTP Project,
+that is used to wait until @code{ntpd} has been able to
+synchronize and stabilize the time on the current host.
+@end ifinfo
+
+@direntry
+* ntpsweep: (ntpsweep).                 Wait for ntpd to synchronize and stabilize the system clock.
+@end direntry
+
+@titlepage
+@title ntpsweep User's Manual
+@subtitle ntpsweep, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@c @page
+@c @vskip 0pt plus 1filll
+
+@node Top, ntpsweep Description, (dir), (dir)
+
+This document describes the use of the NTP Project's @code{ntpsweep} program.
+
+This document applies to version @value{VERSION} of @code{ntpsweep}.
+
+@shortcontents
+
+@menu
+* ntpsweep Description::            Description
+* ntpsweep Invocation::                Invoking ntpsweep
+@end menu
+
+@include invoke-ntpsweep.texi
diff --git a/scripts/ntptrace.in b/scripts/ntptrace.in
deleted file mode 100755 (executable)
index e54ca2b..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#! @PATH_PERL@ -w
-
-# John Hay -- John.Hay@icomtek.csir.co.za / jhay@FreeBSD.org
-
-use Socket;
-use Getopt::Std;
-use vars qw($opt_n $opt_m);
-
-$ntpq = "ntpq";
-
-$Getopt::Std::STANDARD_HELP_VERSION=1;
-getopts('nm:');
-
-$dodns = 1;
-$dodns = 0 if (defined($opt_n));
-
-$max_hosts = (defined($opt_m) ? $opt_m :  99);
-$max_hosts = 0 if ( $max_hosts !~ /^\d+$/ );
-$nb_host = 1;
-
-$host = shift;
-$host ||= "127.0.0.1";
-
-for (;;) {
-       $nb_host++;
-       $rootdelay = 0;
-       $rootdispersion = 0;
-       $stratum = 255;
-       $cmd = "$ntpq -n -c rv $host";
-       open(PH, $cmd . "|") || die "failed to start command $cmd: $!";
-       while (<PH>) {
-               $stratum = $1 if (/stratum=(\d+)/);
-               $peer = $1 if (/peer=(\d+)/);
-               # Very old servers report phase and not offset.
-               $offset = $1 if (/(?:offset|phase)=([^\s,]+)/);
-               $rootdelay = $1 if (/rootdelay=([^\s,]+)/);
-               # firstly - rootdispersion, now - rootdisp
-               $rootdispersion = $1 if (/(?:rootdisp|rootdispersion)=([^\s,]+)/);
-               $refid = $1 if (/refid=([^\s,]+)/);
-       }
-       close(PH) || die "$cmd failed";
-       last if ($stratum == 255);
-       $offset /= 1000;
-       $syncdistance = ($rootdispersion + ($rootdelay / 2)) / 1000;
-       $dhost = $host;
-       # Only do lookups of IPv4 addresses. The standard lookup functions
-       # of perl only do IPv4 and I don't know if we should require extras.
-       if ($dodns && $host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
-               $iaddr = inet_aton($host);
-               $name = (gethostbyaddr($iaddr, AF_INET))[0];
-               $dhost = $name if (defined($name));
-       }
-       printf("%s: stratum %d, offset %f, synch distance %f",
-           $dhost, $stratum, $offset, $syncdistance);
-       printf(", refid '%s'", $refid) if ($stratum == 1);
-       printf("\n");
-       last if ($stratum == 0 || $stratum == 1 || $stratum == 16);
-       last if ($refid =~ /^127\.127\.\d{1,3}\.\d{1,3}$/);
-       last if ($nb_host > $max_hosts);
-
-       $cmd = "$ntpq -n -c \"rv $peer\" $host";
-       open(PH, $cmd . "|") || die "failed to start command $cmd: $!";
-       $thost = "";
-       while (<PH>) {
-               $thost = $1, last if (/srcadr=(\S+),/);
-       }
-       close(PH) || die "$cmd failed";
-       last if ($thost eq "");
-       last if ($thost =~ /^127\.127\.\d{1,3}\.\d{1,3}$/);
-       $host = $thost;
-}
-
diff --git a/scripts/ntptrace/Makefile.am b/scripts/ntptrace/Makefile.am
new file mode 100644 (file)
index 0000000..91cc97b
--- /dev/null
@@ -0,0 +1,86 @@
+man_MANS=      ntptrace.$(NTPTRACE_MS)
+
+bin_SCRIPTS =  $(NTPTRACE_DB)
+libexec_SCRIPTS =      $(NTPTRACE_DL)
+sbin_SCRIPTS = $(NTPTRACE_DS)
+
+run_ag=                cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)"      \
+               autogen -L ../../sntp/include -L ../../sntp/ag-tpl \
+               --writable
+
+EXTRA_SCRIPTS = ntptrace
+
+EXTRA_DIST =   \
+       $(srcdir)/ntptrace.in \
+       $(srcdir)/ntptrace.1ntptraceman \
+       $(srcdir)/ntptrace.1ntptracemdoc        \
+       $(srcdir)/ntptrace.texi \
+       $(srcdir)/ntptrace.html \
+       $(srcdir)/ntptrace.1            \
+       $(srcdir)/ntptrace-opts.def     \
+       $(srcdir)/ntptrace-opts \
+       $(srcdir)/invoke-ntptrace.texi  \
+       $(srcdir)/invoke-ntptrace.menu
+
+html_DATA = $(srcdir)/ntptrace.html
+
+noinst_DATA = \
+       $(srcdir)/ntptrace.in \
+       $(srcdir)/ntptrace.1ntptraceman \
+       $(srcdir)/ntptrace.1ntptracemdoc        \
+       $(srcdir)/ntptrace.texi \
+       $(srcdir)/ntptrace.html \
+       $(srcdir)/ntptrace.1            \
+       $(srcdir)/ntptrace-opts.def     \
+       $(srcdir)/ntptrace-opts \
+       $(srcdir)/invoke-ntptrace.texi  \
+       $(srcdir)/invoke-ntptrace.menu
+
+ntptrace: $(srcdir)/ntptrace-opts
+
+$(srcdir)/ntptrace-opts: $(srcdir)/ntptrace-opts.def ../../sntp/ag-tpl/perlopt.tpl
+       $(run_ag) ntptrace-opts.def
+
+clean-local: extra-clean
+
+extra-clean:
+       rm ntptrace
+       rm *.html *man *mdoc *.1 invoke-ntptrace.* ntptrace.man.in
+
+### Nroff
+
+$(srcdir)/ntptrace.1ntptraceman: $(srcdir)/ntptrace-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntptraceman -Tagman-cmd.tpl ntptrace-opts.def
+
+$(srcdir)/ntptrace.man.in: $(srcdir)/ntptrace.1ntptraceman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntptrace.1ntptraceman > $(srcdir)/ntptrace.man.in+
+       mv $(srcdir)/ntptrace.man.in+ $(srcdir)/ntptrace.man.in
+
+### Mdoc
+
+$(srcdir)/ntptrace.1ntptracemdoc: $(srcdir)/ntptrace-opts.def $(std_def_list)
+       $(run_ag) -DMAN_SECTION=1ntptracemdoc -Tagmdoc-cmd.tpl ntptrace-opts.def
+
+$(srcdir)/ntptrace.mdoc.in: $(srcdir)/ntptrace.1ntptracemdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+       sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntptrace.1ntptracemdoc > $(srcdir)/ntptrace.mdoc.in+
+       mv $(srcdir)/ntptrace.mdoc.in+ $(srcdir)/ntptrace.mdoc.in
+
+### Manpage
+
+$(srcdir)/ntptrace.1: $(srcdir)/ntptrace.$(MANTAGFMT).in $(top_builddir)/config.status
+       $(top_builddir)/config.status --file=ntptrace.1+:$(srcdir)/ntptrace.$(MANTAGFMT).in
+       mv ntptrace.1+ ntptrace.1
+
+### Texinfo
+
+$(srcdir)/invoke-ntptrace.menu: $(srcdir)/invoke-ntptrace.texi
+       @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntptrace.texi: ntptrace $(srcdir)/ntptrace-opts.def $(std_def_list)
+       $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntptrace-opts.def
+       $(top_srcdir)/scripts/build/check--help $@
+
+### HTML
+
+$(srcdir)/ntptrace.html: $(srcdir)/invoke-ntptrace.menu $(srcdir)/invoke-ntptrace.texi $(srcdir)/ntptrace.texi $(top_srcdir)/sntp/include/version.texi
+       cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntptrace.html ntptrace.texi || true )
diff --git a/scripts/ntptrace/ntptrace-opts b/scripts/ntptrace/ntptrace-opts
new file mode 100644 (file)
index 0000000..27edbdc
--- /dev/null
@@ -0,0 +1,67 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'numeric' => '',
+        'max-hosts' => '99',
+        'host' => '127.0.0.1',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '[host]';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'numeric|n', 'max-hosts|m=i', 'host|r=s',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+ntptrace - Trace peers of an NTP server
+USAGE: ntptrace [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [host]
+
+    -n, --numeric                Print IP addresses instead of hostnames
+    -m, --max-hosts=num          Maximum number of peers to trace
+    -r, --host=str               Single remote host
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+
+    if ($argument && $argument =~ /^[^\[]/ && !@$args) {
+        print STDERR "Not enough arguments supplied (See --help/-?)\n";
+        exit 1;
+    }
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
diff --git a/scripts/ntptrace/ntptrace-opts.def b/scripts/ntptrace/ntptrace-opts.def
new file mode 100644 (file)
index 0000000..5b0e585
--- /dev/null
@@ -0,0 +1,66 @@
+/* -*- Mode: Text -*- */
+autogen definitions perlopt;
+
+//#include copyright.def
+#include autogen-version.def
+
+prog-name      = 'ntptrace';
+prog-title     = 'Trace peers of an NTP server';
+argument       = '[host]';
+long-opts;
+gnu-usage;
+
+flag = {
+    name    = numeric;
+    value   = n;
+    descrip = 'Print IP addresses instead of hostnames';
+    doc     = <<- _EndOfDoc_
+       Output hosts as dotted-quad numeric format rather than converting to
+       the canonical host names.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = max-hosts;
+    value       = m;
+    arg-type    = number;
+    arg-default = 99;
+    descrip     = 'Maximum number of peers to trace';
+};
+
+flag = {
+    name        = host;
+    value       = r;
+    arg-type    = string;
+    arg-default = '127.0.0.1';
+    descrip     = 'Single remote host';
+};
+
+doc-section    = {
+  ds-type      = 'DESCRIPTION';
+  ds-format    = 'texi';
+  ds-text      = <<- _END_PROG_MDOC_DESCRIP
+@code{ntptrace} is a perl script that uses the ntpq utility program to follow
+the chain of NTP servers from a given host back to the primary time source. For
+ntptrace to work properly, each of these servers must implement the NTP Control
+and Monitoring Protocol specified in RFC 1305 and enable NTP Mode 6 packets.
+
+If given no arguments, ntptrace starts with localhost. Here is an example of
+the output from ntptrace:
+
+@example
+% ntptrace localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784 usndh.edu:
+stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB'
+@end example
+
+On each line, the fields are (left to right): the host name, the host stratum,
+the time offset between that host and the local host (as measured by
+@code{ntptrace}; this is why it is not always zero for "localhost"), the host
+synchronization distance, and (only for stratum-1 servers) the reference clock
+ID. All times are given in seconds. Note that the stratum is the server hop
+count to the primary source, while the synchronization distance is the
+estimated error relative to the primary source. These terms are precisely
+defined in RFC-1305.
+       _END_PROG_MDOC_DESCRIP;
+};
diff --git a/scripts/ntptrace/ntptrace.in b/scripts/ntptrace/ntptrace.in
new file mode 100755 (executable)
index 0000000..dfb7c48
--- /dev/null
@@ -0,0 +1,79 @@
+#! @PATH_PERL@ -w
+# John Hay -- John.Hay@icomtek.csir.co.za / jhay@FreeBSD.org
+package ntptrace;
+use 5.006_000;
+use strict;
+use lib "@PERLLIBDIR@";
+use NTP::Util qw(ntp_read_vars do_dns);
+
+exit run(@ARGV) unless caller;
+
+sub run {
+    my $opts;
+    if (!processOptions(\@_, $opts)) {
+        usage(1);
+    };
+
+    my $dodns     = $opts->{numeric} ? 0 : 1;
+    my $max_hosts = $opts->{'max-hosts'};
+    my $host      = shift || $opts->{host};
+    my $nb_host   = 0;
+
+    for (;;) {
+        $nb_host++;
+
+        my %info = get_info($host);
+        last if not %info;
+
+        my $dhost = $host;
+        if ($dodns) {
+            my $name = do_dns($host);
+            $dhost = $name if defined $name;
+        }
+
+        printf "%s: stratum %d, offset %f, synch distance %f",
+            $dhost, $info{stratum}, $info{offset}, $info{syncdistance};
+        printf ", refid '%s'", $info{refid} if $info{stratum} == 1;
+        print "\n";
+
+        last if $info{stratum} == 0 || $info{stratum} == 1 || 
+                $info{stratum} == 16;
+        last if $info{refid} =~ /^127\.127\.\d{1,3}\.\d{1,3}$/;
+        last if $nb_host == $max_hosts;
+
+        my $next_host = get_next_host($info{peer}, $host);
+        last if $next_host eq '';
+        last if $next_host  =~ /^127\.127\.\d{1,3}\.\d{1,3}$/;
+
+        $host = $next_host;
+    }
+    return 0;
+}
+
+sub get_info {
+    my ($host) = @_;
+    my ($rootdelay, $rootdisp, $info) = (0, 0);
+
+    $info = ntp_read_vars(0, [], $host);
+    return if not defined $info;
+    return if not exists $info->{stratum};
+
+    $info->{offset} /= 1000;
+    $info->{syncdistance} = ($info->{rootdisp} + ($info->{rootdelay} / 2)) / 1000;
+
+    return %$info;
+}
+
+
+sub get_next_host {
+    my ($peer, $host) = @_;
+
+    my $info = ntp_read_vars($peer, [qw(srcadr)], $host);
+    return if not defined $info;
+    return $info->{srcadr};
+}
+
+@ntptrace_opts@
+
+1;
+__END__
diff --git a/scripts/ntptrace/ntptrace.mdoc.in b/scripts/ntptrace/ntptrace.mdoc.in
new file mode 100644 (file)
index 0000000..63908a7
--- /dev/null
@@ -0,0 +1,87 @@
+.Dd October 2 2013
+.Dt NTPTRACE @NTPTRACE_MS@ User Commands
+.Os FreeBSD 6.4-STABLE
+.\"  EDIT THIS FILE WITH CAUTION  (ntptrace-opts.mdoc)
+.\"
+.\"  It has been AutoGen-ed  October  2, 2013 at 09:50:29 PM by AutoGen 5.18.1pre5
+.\"  From the definitions    ntptrace-opts.def
+.\"  and the template file   agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntptrace
+.Nd Trace peers of an NTP server
+.Sh SYNOPSIS
+.Nm
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+[host]
+.Pp
+.Sh DESCRIPTION
+\fBntptrace\fP is a perl script that uses the ntpq utility program to follow
+the chain of NTP servers from a given host back to the primary time source. For
+ntptrace to work properly, each of these servers must implement the NTP Control
+and Monitoring Protocol specified in RFC 1305 and enable NTP Mode 6 packets.
+.sp
+If given no arguments, ntptrace starts with localhost. Here is an example of
+the output from ntptrace:
+.sp
+.Bd -literal -offset indent
+% ntptrace localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784 usndh.edu:
+stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB'
+.Ed
+.sp
+On each line, the fields are (left to right): the host name, the host stratum,
+the time offset between that host and the local host (as measured by
+\fBntptrace\fP; this is why it is not always zero for "localhost"), the host
+synchronization distance, and (only for stratum\-1 servers) the reference clock
+ID. All times are given in seconds. Note that the stratum is the server hop
+count to the primary source, while the synchronization distance is the
+estimated error relative to the primary source. These terms are precisely
+defined in RFC\-1305.
+.Sh "OPTIONS"
+.Bl -tag
+.It  Fl n , Fl \-numeric 
+Print IP addresses instead of hostnames.
+.sp
+Output hosts as dotted\-quad numeric format rather than converting to
+the canonical host names.
+.It  Fl m Ar number , Fl \-max\-hosts Ns = Ns Ar number 
+Maximum number of peers to trace.
+This option takes an integer number as its argument.
+The default
+.Ar number
+for this option is:
+.ti +4
+ 99
+.sp
+This option has not been fully documented.
+.It  Fl r Ar string , Fl \-host Ns = Ns Ar string 
+Single remote host.
+The default
+.Ar string
+for this option is:
+.ti +4
+ 127.0.0.1
+.sp
+This option has not been fully documented.
+.It Fl \&? , Fl \-help
+Display usage information and exit.
+.It Fl \&! , Fl \-more\-help
+Pass the extended usage information through a pager.
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error.  Please report
+it to autogen\-users@lists.sourceforge.net.  Thank you.
+.El
+.Sh "NOTES"
+This manual page was \fIAutoGen\fP\-erated from the \fBntptrace\fP
+option definitions.
diff --git a/scripts/ntptrace/ntptrace.texi b/scripts/ntptrace/ntptrace.texi
new file mode 100644 (file)
index 0000000..b1851f2
--- /dev/null
@@ -0,0 +1,40 @@
+\input texinfo    @c -*-texinfo-*-
+@c %**start of header
+@setfilename ntptrace.info
+@settitle Ntptrace User's Manual
+@include ../../sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the use of @code{ntptrace},
+a program from the NTP Project
+@end ifinfo
+
+@direntry
+* ntptrace: (ntptrace).                 Trace the ntp server to the primary time source.
+@end direntry
+
+@titlepage
+@title ntptrace User's Manual
+@subtitle ntptrace, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@c @page
+@c @vskip 0pt plus 1filll
+
+@node Top, ntptrace Description, (dir), (dir)
+@top Simple Network Time Protocol User Manual
+
+This document describes the use of the NTP Project's @code{ntptrace} program.
+This document applies to version @value{VERSION} of @code{ntptrace}.
+
+@shortcontents
+
+@menu
+* ntptrace Description::            Description
+* ntptrace Invocation::                    Invoking ntptrace
+@end menu
+
+@include invoke-ntptrace.texi
diff --git a/scripts/plot_summary-opts b/scripts/plot_summary-opts
new file mode 100644 (file)
index 0000000..1dd327c
--- /dev/null
@@ -0,0 +1,73 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'directory' => '/tmp',
+        'identifier' => '',
+        'offset-limit' => '0.128',
+        'peer' => [],
+        'plot-term' => '',
+        'output-file' => '',
+        'dont-wait' => '',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'directory=s', 'identifier=s', 'offset-limit=f',
+        'peer=s', 'plot-term=s', 'output-file=s',
+        'dont-wait',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+plot_summary - plot statistics generated by summary script
+USAGE: plot_summary [ -<flag> [<val>] | --<name>[{=| }<val>] ]... 
+
+      , --directory=str          Where the summary files are
+      , --identifier=str         Origin of the data
+      , --offset-limit=float     Limit of absolute offset
+      , --peer=str               Peers to generate plots for
+                                   - may appear multiple times
+      , --plot-term=str          Gnuplot terminal
+      , --output-file=str        Output file
+      , --dont-wait              Don't wait for keystroke between plots
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
diff --git a/scripts/plot_summary-opts.def b/scripts/plot_summary-opts.def
new file mode 100644 (file)
index 0000000..7b52115
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- Mode: Text -*- */
+autogen definitions perlopt;
+#include autogen-version.def
+prog-name      = 'plot_summary';
+prog-title     = 'plot statistics generated by summary script';
+long-opts;
+gnu-usage;
+
+flag = {
+    name        = directory;
+    arg-type    = string;
+    arg-default = '/tmp';
+    descrip     = 'Where the summary files are';
+    doc         = <<-  _EndOfDoc_
+        The directory where the @code{plot_summary} will search for the
+        *_summary files generated by @code{summary} script.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = identifier;
+    arg-type    = string;
+    descrip     = 'Origin of the data';
+    doc         = <<-  _EndOfDoc_
+        Where does the plotted data come from, default to string "host" plus
+        current hostname
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = offset-limit;
+    arg-type    = string;
+    arg-name    = float;
+    arg-default = "0.128";
+    descrip     = 'Limit of absolute offset';
+    doc         = <<-  _EndOfDoc_
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = peer;
+    arg-type    = string;
+    stack-arg;
+    max         = NOLIMIT;
+    descrip     = 'Peers to generate plots for';
+    doc         = <<-  _EndOfDoc_
+        By default the peer_summary plots are not generated. Use this option to
+        specify list of peers if you want to generate plots for them.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = plot-term;
+    arg-type    = string;
+    descrip     = 'Gnuplot terminal';
+    doc         = <<-  _EndOfDoc_
+        This is string is passed directly to the @code{gnuplot set terminal}
+        command.  Default is @code{x11} if @code{DISPLAY} is set and
+        @code{dumb} is it's not'. See output from @code(gnuplot -e "set
+        terminal") for the list of avalaible options.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = output-file;
+    arg-type    = str;
+    descrip     = 'Output file';
+    doc         = <<-  _EndOfDoc_
+        Output file for @code{gnuplot}, default to stdout.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = dont-wait;
+    descrip     = "Don't wait for keystroke between plots";
+    doc         = <<-  _EndOfDoc_
+       _EndOfDoc_;
+};
index 3b46a0401159668e52ae618dbc650906bd391abd..3401b0d0b8b848a3cb5facc08a426a7561a29f05 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-require 5.003; # "never tested with any other version of Perl"
+package plot_summary;
+use 5.006_000;
 use strict;
-
 use Time::Local;
-use Getopt::Long;
 
-# parse command line
-my $summary_dir = "/tmp";
-my $identifier = "host " . `hostname`; # origin of these data
-chomp $identifier;                     # remove newline
-my $offset_limit = 0.128;              # limit of absolute offset
-my $output_file = "";                  # output file defaults to stdout
-my $output_file_number = 1;            # numbering of output files
-my $gnuplot_terminal = $ENV{DISPLAY} ? "x11" : "dumb";
-my $wait_after_plot = 1;
-my @peer_list = ();
+my ($identifier, $offset_limit, $gnuplot_terminal, $wait_after_plot,
+    $output_file, $output_file_number);
 
-my %options = ("directory|input-directory=s" => \$summary_dir,
-              "identifier=s" => \$identifier,
-              "offset-limit=f" => \$offset_limit,
-              "output-file=s" => \$output_file,
-              "peer=s@" => \@peer_list,
-              "plot-term|gnuplot-term=s" => \$gnuplot_terminal,
-              "wait-after-plot!" => \$wait_after_plot,
-              );
+exit run(@ARGV) unless caller;
 
-if ( !GetOptions(%options) )
-{
-    print STDERR "valid options for $0 are:\n";
-    my $opt;
-    foreach $opt (sort(keys %options)) {
-       print STDERR "\t--$opt\t(default is ";
-       if ( ref($options{$opt}) eq "ARRAY" ) {
-           print STDERR join(", ",  map { "'$_'" } @{$options{$opt}});
-       } else {
-           print STDERR "'${$options{$opt}}'";
-       }
-       print STDERR ")\n";
+sub run {
+    my $opts;
+    if (!processOptions(\@_, $opts)) {
+        usage(1);
     }
-    print STDERR "\n";
-    die;
-}
 
-chomp $identifier;
-die "illegal offset-limit: $offset_limit" unless $offset_limit > 0.0;
-$offset_limit *= 1e6;                  # scale to microseconds
+    $identifier  = $opts->{'identifier'};
+    if (!$identifier) {
+        $identifier = "host".`hostname`;
+        chomp $identifier;
+    }
+    $offset_limit       = $opts->{'offset-limit'};
+    $output_file        = $opts->{'output-file'};
+    $output_file_number = 1;
+    $gnuplot_terminal   = $opts->{'plot-terminal'}
+    || ( $ENV{DISPLAY} ? "x11" : "dumb" );
+    $wait_after_plot    = !$opts->{'dont-wait'};
+
+    die "illegal offset-limit: $offset_limit" unless $offset_limit > 0.0;
+    $offset_limit *= 1e6;                      # scale to microseconds
+
+    my $summary_dir = $opts->{'directory'};
+
+    my $loop_summary ="$summary_dir/loop_summary";
+    my $peer_summary ="$summary_dir/peer_summary";
+    my $clock_summary="$summary_dir/clock_summary";
+
+    my @peer_list   = @{$opts->{'peer'}};
+
+    do_loop($loop_summary);
+    do_peer($peer_summary, $_) for @peer_list;
+}
 
 # return the smallest value in the given list
 sub min
@@ -176,7 +171,7 @@ sub do_loop
        "Daily mean values since $first_day\\n" .
        "(Offset limit is $offset_limit microseconds)\"\n";
     print "set ylabel \"[us]\"\n";
-    print "set data style yerrorbars\n";
+    print "set style data yerrorbars\n";
     print "set multiplot\n";
     print "set size 1, 0.5\n";
     print "set lmargin 8\n";
@@ -224,7 +219,7 @@ sub do_loop
     print "set xlabel\n";
     print "set ylabel \"[us]\"\n";
     print "set origin 0, 0.5\n";
-    print "set data style linespoints\n";
+    print "set style data linespoints\n";
     print "set multiplot\n";
     print "plot $ylimit \"$out_file\" using 1:6 title \"Offset\", ";
     print "\"$out_file\" using 1:6 smooth bezier " .
@@ -296,7 +291,7 @@ sub do_peer
     print "set origin 0, 0.66\n";
     print "set title " .
        "\"Peer Summary for $peer on $identifier since $first_day\"\n";
-    print "set data style linespoints\n";
+    print "set style data linespoints\n";
     print "set ylabel \"[us]\"\n";
     print "plot \"$out_file\" using 1:3 title \"mean offset\", ";
     print "\"$out_file\" using 1:3 smooth bezier " .
@@ -328,10 +323,7 @@ sub do_peer
     unlink $out_file;
 }
 
+@plot_summary_opts@
 
-my $loop_summary ="$summary_dir/loop_summary";
-my $peer_summary ="$summary_dir/peer_summary";
-my $clock_summary="$summary_dir/clock_summary";
-
-do_loop $loop_summary;
-map { do_peer $peer_summary, $_ } @peer_list;
+1;
+__END__
diff --git a/scripts/rc/README b/scripts/rc/README
new file mode 100644 (file)
index 0000000..f5b82bc
--- /dev/null
@@ -0,0 +1,13 @@
+This directory contains some example rc scripts for ntpd.
+
+In general, ntpd should be started as soon as possible in the boot process. If
+any services require stable system clock, the ntpwait script should be run
+before them as late as possible.
+
+The rc.d contains scripts for systems using rc.d init system (originated in
+NetBSD). If a service requires stable system time, indicate it with TIMESYNC
+dependency and set ntpwait_enable to YES.
+
+For SysV init systems, you'll have to create links as /etc/rc2.d/S20ntpd and
+/etc/rc2.d/S80ntpwait yourself. (The numbers are just examples, try to give
+ntpd as much time as possible to synchronize before running ntpwait).
diff --git a/scripts/rc/ntpd b/scripts/rc/ntpd
new file mode 100644 (file)
index 0000000..9896247
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+NTPD=/usr/sbin/ntpd
+PIDFILE=/var/run/ntpd.pid
+USER=ntp
+GROUP=ntp
+NTPD_OPTS="-g -u $USER:$GROUP -p $PIDFILE"
+
+ntpd_start() {
+    if [ -r $PIDFILE ]; then
+        echo "ntpd seems to be already running under pid `cat $PIDFILE`."
+        echo "Delete $PIDFILE if this is not the case.";
+        return 1;
+    fi
+    echo -n "Starting NTP daemon... "
+
+    $NTPD $NTPD_OPTS
+
+    # You can't always rely on the ntpd exit code, see Bug #2420
+    # case "$?" in
+    #     0) echo "OK!"
+    #         return 0;;
+    #     *) echo "FAILED!"
+    #         return 1;;
+    # esac
+
+    sleep 1
+
+    if ps -Ao args|grep -q "^$NTPD $NTPD_OPTS"; then
+        echo "OK!"
+        return 0
+    else
+        echo "FAILED!"
+        [ -e $PIDFILE ] && rm $PIDFILE
+        return 1
+    fi
+}
+
+ntpd_stop() {
+    if [ ! -r $PIDFILE ]; then
+        echo "ntpd doesn't seem to be running, cannot read the pid file."
+        return 1;
+    fi
+    echo -n "Stopping NTP daemon...";
+    PID=`cat $PIDFILE`
+
+    if kill -TERM $PID 2> /dev/null;then
+        # Give ntp 15 seconds to exit
+        for i in `seq 1 15`; do
+            if [ -n "`ps -p $PID|grep -v PID`" ]; then
+                echo -n .
+                sleep 1
+            else
+                echo " OK!"
+                rm $PIDFILE
+                return 0
+            fi
+        done
+    fi
+
+    echo " FAILED! ntpd is still running";
+    return 1
+}
+
+ntpd_status() {
+    if [ -r $PIDFILE ]; then
+        echo "NTP daemon is running as `cat $PIDFILE`"
+    else
+        echo "NTP daemon is not running"
+    fi
+}
+
+case "$1" in
+    'start')
+        ntpd_start
+        ;;
+    'stop')
+        ntpd_stop
+        ;;
+    'restart')
+        ntpd_stop && ntpd_start
+        ;;
+    'status')
+        ntpd_status
+        ;;
+    *)
+        echo "Usage: $0 (start|stop|restart|status)"
+esac
diff --git a/scripts/rc/ntpwait b/scripts/rc/ntpwait
new file mode 100644 (file)
index 0000000..2542b2a
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+NTPWAIT=/usr/sbin/ntpwait
+
+ntpwait_start() {
+    $NTPWAIT -v
+}
+
+case "$1" in
+    'start')
+        ntpwait_start
+        ;;
+    *)
+        echo "Usage: $0 (start)"
+esac
diff --git a/scripts/rc/rc.d/TIMESYNC b/scripts/rc/rc.d/TIMESYNC
new file mode 100644 (file)
index 0000000..a8c074f
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# PROVIDE: TIMESYNC
+# REQUIRE: LOGIN ntpwait
+
+# This depedency ensures that all services which require stable system clock
+# are run after ntpd is synchronized. It's run as late as possible, if you need
+# stable clock before login use BEFORE: LOGIN
diff --git a/scripts/rc/rc.d/ntpd b/scripts/rc/rc.d/ntpd
new file mode 100644 (file)
index 0000000..ea33458
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# PROVIDE: ntpd
+# REQUIRE: syslogd cleanvar devfs
+# BEFORE: SERVERS
+
+. /etc/rc.subr
+
+name="ntpd"
+rcvar="ntpd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+start_precmd="ntpd_precmd"
+
+load_rc_config $name
+
+ntpd_precmd()
+{
+        rc_flags="-c ${ntpd_config} ${ntpd_flags}"
+
+        if checkyesno ntpd_sync_on_start; then
+                rc_flags="-g $rc_flags"
+        fi
+
+        if [ -z "$ntpd_chrootdir" ]; then
+                return 0;
+        fi
+
+        rc_flags="-u ntpd:ntpd -i ${ntpd_chrootdir} $rc_flags"
+}
+
+run_rc_command "$1"
diff --git a/scripts/rc/rc.d/ntpwait b/scripts/rc/rc.d/ntpwait
new file mode 100644 (file)
index 0000000..891d0db
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+# This script, when run, runs ntp-wait if ntpd is enabled.
+
+# PROVIDE: ntpwait
+
+. /etc/rc.subr
+
+name="ntpwait"
+rcvar="ntpwait_enable"
+start_cmd="ntpwait_start"
+ntp_wait="/usr/sbin/ntp-wait"
+
+load_rc_config "$name"
+
+ntpwait_start() {
+        if checkyesno ntpd_enable; then
+                $ntp_wait -v
+        fi
+}
+
+run_rc_command "$1"
index d84b8c517211934e626c26373e2142b6747fd1ff..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,2 +0,0 @@
-#!/bin/sh
-/etc/init.d/xntp start
index aa18639c2fe665803f912211199d26e9fd506992..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,6 +0,0 @@
-#!/bin/sh
-if [ -x /etc/init.d/xntp ]
-then
-       /etc/init.d/xntp stop
-fi
-exit 0
index b870151a27b4692304e8165bd3b73c26018e3355..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,4 +0,0 @@
-#!/bin/sh
-/etc/init.d/xntp stop
-
-exit 0
index 3de20b076b8c84a83c8eae1efb3fc1df9bb93ab4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,19 +0,0 @@
-!default 755 root bin
-i pkginfo
-i preinstall
-i postinstall
-i preremove
-f none /etc/init.d/xntp=xntp 0755 root other
-l none /etc/rc2.d/S79xntp=/etc/init.d/xntp
-l none /etc/rc1.d/K79xntp=/etc/init.d/xntp
-l none /etc/rc0.d/K79xntp=/etc/init.d/xntp
-f none /usr/sbin/xntpd=xntpd/xntpd 0555 root other
-f none /usr/sbin/xntpdc=xntpdc/xntpdc 0555 root other
-f none /usr/sbin/ntpq=ntpq/ntpq 0555 root other
-f none /usr/sbin/ntptrace=ntptrace/ntptrace 0555 root other
-f none /usr/sbin/ntpdate=ntpdate/ntpdate 0555 root other
-f none /usr/share/man/man1m/xntpd.1m=doc/xntpd.8 0444 root other
-f none /usr/share/man/man1m/xntpdc.1m=doc/xntpdc.8 0444 root other
-f none /usr/share/man/man1m/ntpdate.1m=doc/ntpdate.8 0444 root other
-f none /usr/share/man/man1m/ntpq.1m=doc/ntpq.8 0444 root other
-f none /usr/share/man/man1m/ntptrace.1m=doc/ntptrace.8 0444 root other
index 227b943aaa2ef6dfbe57c724ac48a7e4bb000b7e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-killproc() {   # kill named processes
-       pid=`/usr/bin/ps -e |
-               /usr/bin/grep $1 |
-               /usr/bin/sed -e 's/^  *//' -e 's/ .*//'`
-       [ "$pid" != "" ] && kill $pid
-}
-
-case "$1" in
-'start')
-       ps -e | grep xntpd > /dev/null 2>&1
-       if [ $? -eq 0 ]
-       then
-               echo "ntp daemon already running. ntp start aborted"
-               exit 0
-       fi
-       if [ -f /etc/inet/ntp.conf -a -x /usr/sbin/xntpd ]
-       then
-               /usr/sbin/xntpd -c /etc/inet/ntp.conf
-       fi
-       ;;
-'stop')
-       killproc xntpd
-       ;;
-*)
-       echo "Usage: /etc/init.d/xntp { start | stop }"
-       ;;
-esac
index ba53e05adbbb7547a95eab647c5c261350a4f602..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,64 +0,0 @@
-#! /usr/bin/perl -w
-# 980904 Harlan Stenn - created
-
-# vvv CHANGE THESE vvv
-
-$ps = "/bin/ps x |";
-
-$ntp_conf = "/etc/ntp.conf";
-$ntpd = "/usr/local/bin/xntpd";
-$ntpdate = "/usr/local/bin/ntpdate -b -s 10.0.0.1 10.0.0.2";
-
-# ^^^ CHANGE THESE ^^^
-
-{
-  if (0)
-    {
-    }
-  elsif ($ARGV[0] eq "start")
-    {
-      @pidlist = pidlist($ntpd);
-      if (defined(@pidlist))
-        {
-         warn "NTP is already running\n";
-        }
-      else
-        {
-         if ( -f $ntp_conf && -x $ntpd )
-           {
-              system ($ntpdate);
-              system ($ntpd." -c ".$ntp_conf);
-           }
-        }
-    }
-  elsif ($ARGV[0] eq "stop")
-    {
-      @pidlist = pidlist($ntpd);
-      kill 'TERM', @pidlist if (scalar(@pidlist) > 0);
-    }
-  else
-    {
-      die "Usage: $0 {start,stop}\n";
-    }
-}
-
-sub pidlist ($)
-  {
-    my ($target) = @_;
-    my ($qt) = quotemeta($target);
-    my @pids;
-
-    open(PS, $ps) || die "Can't run ps: $!\n";
-    while (<PS>)
-      {
-       chomp;
-       next unless (/$qt/);
-       print "Got <$_>\n";
-       if (/^\s*(\d+)\s+/)
-         {
-           push @pids, $1;
-         }
-      }
-    close(PS);
-    return @pids;
-  }
diff --git a/scripts/summary-opts b/scripts/summary-opts
new file mode 100644 (file)
index 0000000..2cbc5cc
--- /dev/null
@@ -0,0 +1,69 @@
+
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code  
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        'directory' => '/var/log/ntp',
+        'end-date' => '',
+        'output-directory' => '/tmp',
+        'peer-dist-limit' => '400',
+        'skip-time-steps' => '3600',
+        'start-date' => '19700101',
+        'help' => '', 'more-help' => ''
+    };
+    my $argument = '';
+    my $ret = GetOptionsFromArray($args, $opts, (
+        'directory=s', 'end-date=i', 'output-directory=s',
+        'peer-dist-limit=f', 'skip-time-steps=f', 'start-date=i',
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+summary - compute various stastics from NTP stat files
+USAGE: summary [ -<flag> [<val>] | --<name>[{=| }<val>] ]... 
+
+      , --directory=str          Directory containing stat files
+      , --end-date=num           End date
+      , --output-directory=str   Output directory
+      , --peer-dist-limit=float  Peer dist limit
+      , --skip-time-steps=float  Ignore time offsets larger that this
+      , --start-date=num         Start date
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+
diff --git a/scripts/summary-opts.def b/scripts/summary-opts.def
new file mode 100644 (file)
index 0000000..21bc922
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- Mode: Text -*- */
+autogen definitions perlopt;
+#include autogen-version.def
+prog-name      = 'summary';
+prog-title     = 'compute various stastics from NTP stat files';
+long-opts;
+gnu-usage;
+
+flag = {
+    name        = directory;
+    arg-type    = string;
+    arg-default = '/var/log/ntp';
+    descrip     = 'Directory containing stat files';
+    doc         = <<-  _EndOfDoc_
+        The directory where @code{ntpd} will search for .stat files generated
+        by @code{ntpd}.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = end-date;
+    arg-type    = number;
+    descrip     = 'End date';
+    doc         = <<-  _EndOfDoc_
+        Process all files with the date suffix less or equal to value of this
+        option.  Defaults to today minus one day (Use @code{date -u +%Y%m%d})
+        to get the timestamp.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = output-directory;
+    arg-type    = str;
+    arg-default = '/tmp';
+    descrip     = 'Output directory';
+    doc         = <<-  _EndOfDoc_
+        The output directory @code{summary} will write all output files to.
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = peer-dist-limit;
+    arg-type    = string;
+    arg-name    = float;
+    arg-default = 400;
+    descrip     = 'Peer dist limit';
+    doc     = <<-  _EndOfDoc_
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = skip-time-steps;
+    arg-type    = string;
+    arg-name    = float;
+    arg-default = 3600;
+    descrip     = 'Ignore time offsets larger that this';
+    doc     = <<-  _EndOfDoc_
+       _EndOfDoc_;
+};
+
+flag = {
+    name        = start-date;
+    arg-type    = num;
+    arg-default = 19700101;
+    descrip     = 'Start date';
+    doc     = <<-  _EndOfDoc_
+        Process all files with the date suffix more or equal to value of
+        this option.  Defaults to 197000101.
+       _EndOfDoc_;
+};
+
+doc-section = {
+    ds-type     = 'DESCRIPTION';
+    ds-format   = 'texi';
+    ds-text     = <<- _EndOfDoc
+       _EndOfDoc;
+};
index 515dfc6b56f7c4e75307869b0d59c547e4b9e13b..a99f8df0e73e637e2873cc8e7492d5df612674de 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-require 5.003; # "never tested with any other version of Perl"
+package summary;
+use 5.006_000;
 use strict;
 
-use Getopt::Long;
+my ($log_date_pattern, $statsdir, $outputdir, $skip_time_steps, $startdate,
+    $enddate, $peer_dist_limit);
 
-my $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d';
-my $statsdir = "/var/log/ntp";         # directory with input files
-my $outputdir = "/tmp";                        # directory for output files
-my $skip_time_steps = 3600.0;          # ignore time offsets larger that this
-my $startdate = "19700101";            # first data file to use (YYYYMMDD)
-my $enddate=`date -u +%Y%m%d`; chomp $enddate; --$enddate;
-my $peer_dist_limit = 400.0;
+exit run(@ARGV) unless caller;
 
-my %options = ("directory|input-directory=s" => \$statsdir,
-              "output-directory=s" => \$outputdir,
-              "skip-time-steps:f" => \$skip_time_steps,
-              "start-date=s" => \$startdate,
-              "end-date=s" => \$enddate,
-              "peer-dist-limit=f" => \$peer_dist_limit);
+sub run {
+    my $opts;
+    if (!processOptions(\@ARGV, $opts)) {
+        usage(1);
+    };
 
-if ( !GetOptions(%options) )
-{
-    print STDERR "valid options for $0 are:\n";
-    my $opt;
-    foreach $opt (sort(keys %options)) {
-       print STDERR "\t--$opt\t(default is ";
-       if ( ref($options{$opt}) eq "ARRAY" ) {
-           print STDERR join(", ",  map { "'$_'" } @{$options{$opt}});
-       } else {
-           print STDERR "'${$options{$opt}}'";
-       }
-       print STDERR ")\n";
+    $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d';
+    $statsdir         = $opts->{directory};
+    $outputdir        = $opts->{'output-directory'};
+    $skip_time_steps  = $opts->{'skip-time-steps'};
+    $startdate        = $opts->{'start-date'};
+    $enddate          = $opts->{'end-date'};
+    if (!$enddate){
+        $enddate = `date -u +%Y%m%d`; 
+        chomp $enddate;
+        --$enddate;
     }
-    print STDERR "\n";
-    die;
-}
+    $peer_dist_limit = $opts->{'peer-dist-limit'};
+
+    # check possibly current values of options
+    die "$statsdir: no such directory" unless (-d $statsdir);
+    die "$outputdir: no such directory" unless (-d $outputdir);
+    die "$skip_time_steps: skip-time-steps must be positive"
+        unless ($skip_time_steps >= 0.0);
+    die "$startdate: invalid start date|$`|$&|$'"
+        unless ($startdate =~ m/.*$log_date_pattern$/);
+    die "$enddate: invalid end date"
+        unless ($enddate =~ m/.*$log_date_pattern$/);
+
+    $skip_time_steps = 0.128 if ($skip_time_steps == 0);
+
+    my $loop_summary="$outputdir/loop_summary";
+    my $peer_summary="$outputdir/peer_summary";
+    my $clock_summary="$outputdir/clock_summary";
+    my (@loopfiles, @peerfiles, @clockfiles);
 
-# check possibly current values of options
-die "$statsdir: no such directory" unless (-d $statsdir);
-die "$outputdir: no such directory" unless (-d $outputdir);
-die "$skip_time_steps: skip-time-steps must be positive"
-    unless ($skip_time_steps >= 0.0);
-die "$startdate: invalid start date|$`|$&|$'"
-    unless ($startdate =~ m/.*$log_date_pattern$/);
-die "$enddate: invalid end date"
-    unless ($enddate =~ m/.*$log_date_pattern$/);
+    print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
 
-$skip_time_steps = 0.128 if ($skip_time_steps == 0);
+    opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
+    rewinddir SDIR;
+    @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
+    rewinddir SDIR;
+    @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
+    rewinddir SDIR;
+    @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
+    closedir SDIR;
+
+    # remove old summary files
+    for ($loop_summary, $peer_summary, $clock_summary) { unlink $_ if -f $_ };
+
+    my $date;
+    for (@loopfiles) {
+        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
+        if ($date ge $startdate && $date le $enddate) {
+            do_loop($statsdir, $_, $loop_summary);
+        }
+    }
+
+    for (@peerfiles) {
+        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
+        if ($date ge $startdate && $date le $enddate) {
+            do_peer($statsdir, $_, $peer_summary);
+        }
+    }
+
+    for (@clockfiles) {
+        $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
+        if ($date ge $startdate && $date le $enddate) {
+            do_clock($statsdir, $_, $clock_summary);
+        }
+    }
+
+    print STDERR "Creating peer summary with limit $peer_dist_limit\n";
+    peer_summary($peer_summary) if (-f $peer_summary);
+}
 
 sub min
 {
@@ -328,46 +363,7 @@ sub peer_summary
     print sort @lines;
 }
 
-my $loop_summary="$outputdir/loop_summary";
-my $peer_summary="$outputdir/peer_summary";
-my $clock_summary="$outputdir/clock_summary";
-my (@loopfiles, @peerfiles, @clockfiles);
-
-print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
-
-opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
-rewinddir SDIR;
-@loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR;
-rewinddir SDIR;
-@peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR;
-rewinddir SDIR;
-@clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR;
-closedir SDIR;
-
-# remove old summary files
-map { unlink $_ if -f $_ } ($loop_summary, $peer_summary, $clock_summary);
-
-my $date;
-map {
-    $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
-    if ($date ge $startdate && $date le $enddate) {
-       do_loop $statsdir, $_, $loop_summary;
-    }
-} @loopfiles;
-
-map {
-    $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
-    if ($date ge $startdate && $date le $enddate) {
-       do_peer $statsdir, $_, $peer_summary;
-    }
-} @peerfiles;
-
-map {
-    $date = $_; $date =~ s/.*($log_date_pattern)$/$1/;
-    if ($date ge $startdate && $date le $enddate) {
-       do_clock $statsdir, $_, $clock_summary;
-    }
-} @clockfiles;
+@summary_opts@
 
-print STDERR "Creating peer summary with limit $peer_dist_limit\n";
-peer_summary $peer_summary if (-f $peer_summary);
+1;
+__END__
diff --git a/scripts/t/1_common.t b/scripts/t/1_common.t
new file mode 100644 (file)
index 0000000..9cd665c
--- /dev/null
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+use Test::More tests => 30;
+use Test::Command;
+use List::MoreUtils qw(any);
+
+my @script_list = qw(
+    calc_tickadj/calc_tickadj ntp-wait/ntp-wait ntpsweep/ntpsweep
+    ntptrace/ntptrace summary plot_summary
+);
+
+for my $script (@script_list) {
+    fail("$script not a regular file") if (!-f $script);
+    pass("$script is a regular file");
+    fail("$script not an executabe") if (!-x $script);
+    pass("$script is an executable");
+    fail("$script is not readable") if (!-r $script);
+    pass("$script is readable");
+
+    TODO: {
+        #todo_skip "$script - Broken script/no perl", 2
+        #    if any { $script eq $_ } qw(freq_adj ntp-groper ntp-status plot_summary);
+
+        require_ok("./$script");
+        cmd_is("./$script --rubbish", '', qr/^Unknown option:/, 1, 
+            "$script reports unkown option");
+    }
+}
diff --git a/scripts/t/calc_tickadj.t b/scripts/t/calc_tickadj.t
new file mode 100644 (file)
index 0000000..066fd39
--- /dev/null
@@ -0,0 +1,90 @@
+use strict;
+use warnings;
+use Test::More tests => 21;
+use Test::Util qw(check_help override);
+use Test::Command;  
+use Test::Output;
+
+my $script_name = "calc_tickadj/calc_tickadj";
+
+my @options = (
+    [qw(drift-file=str d)],
+    [qw(tick=num t)]
+);
+
+check_help($script_name, @options);
+
+require_ok("./$script_name");
+
+BEGIN {
+    my ($tick, $drift, $drift_file, $open_fail);
+
+    override('calc_tickadj', open => sub (*;@) {
+            return if $open_fail;
+            is($_[1], $drift_file) if defined $drift_file;
+            CORE::open $_[0], '<', \$drift;
+        });
+
+    override('calc_tickadj', readpipe => sub {
+            return $tick;
+        });
+
+    sub run_calc_tickadj {
+        my %opts = @_;
+        $open_fail  = delete $opts{fail_open} || 0;
+        $drift      = delete $opts{drift} || '0.00';
+        $tick       = delete $opts{tick} || '';
+        $drift_file = delete $opts{drift_file};
+        calc_tickadj::run(@{ delete $opts{opts} || [] })
+    }
+}
+
+my $ret;
+
+# Test tick detection
+for (
+    'tick = 10000',
+    'PRESET tick = 10000',
+    'KERNEL tick = 10000',
+    '  tick  =  10000',
+) {
+    stdout_like( sub { $ret = run_calc_tickadj(tick => $_) }, qr/10000 usec; 10000000 nsec/ );
+    ok(!$ret);
+}
+
+# Test how the script copes if tickadj is not found/outputs invalid data
+for (undef, "Invalid stuff") {
+    eval {
+        run_calc_tickadj(tick => $_);
+    } or do {
+        chomp $@; 
+        is($@, 'Could not get tick value, try manually with -t/--tick') 
+    };
+
+    # Still invalid data but pass tick as argument
+    stdout_like( sub { $ret = run_calc_tickadj(tick => $_, opts => [qw(-t 9999)]) }, qr/9999 usec; 9999000 nsec/);
+    ok(!$ret);
+}
+
+# Test -d switch
+stdout_like(
+    sub { $ret = run_calc_tickadj(drift_file => 'foobar', drift => '14.00', tick => 'tick = 10000', opts => [qw(-d foobar)]) },
+    qr/14.000 \(drift\)/
+);
+ok(!$ret);
+
+# Open on drift file fails
+eval {
+    run_calc_tickadj(fail_open => 1, opts => [qw(-t 10000)]);
+} or do {
+    chomp $@; 
+    like($@, qr(Could not open))
+};
+
+# Open succeeds but drift file is invalid
+eval {
+    run_calc_tickadj(drift => 'NOTVALIDDRIFTVALUE', opts => [qw(-t 10000)])
+} or do {
+    chomp $@; 
+    like($@, qr(Invalid drift file value <NOTVALIDDRIFTVALUE>));
+};
diff --git a/scripts/t/lib/Test/Command.pm b/scripts/t/lib/Test/Command.pm
new file mode 100644 (file)
index 0000000..d216820
--- /dev/null
@@ -0,0 +1,261 @@
+package Test::Command;
+use strict;
+use warnings;
+use Test::Builder;
+use IPC::Open3;
+use IO::Handle;
+use Carp;
+use base qw(Exporter);
+our @EXPORT = qw(cmd_is start_command com_stdout_is com_stderr_is com_ret_is
+        com_slurp_stderr com_slurp_stdout wait_for_command);
+our $VERSION = 0.1;
+
+my $Test = Test::Builder->new();
+
+my ($pid, $writer_h, $stdout_h, $stderr_h);
+my $in_command = 0;
+
+$SIG{CHLD} = sub { wait_for_command() if $in_command; };
+
+sub start_command {
+    my (@cmd) = @_;
+
+    croak "Specify a command to start_command" unless @cmd;
+
+    ($pid, $writer_h, $stdout_h, $stderr_h) = 
+        (0, IO::Handle->new(), IO::Handle->new(), IO::Handle->new());
+
+    # Older IPC:Open3 just warns is there was an error
+    local $SIG{__WARN__} = sub { die @_ };
+
+    $in_command = 1;
+    $pid = open3($writer_h, $stdout_h, $stderr_h, @cmd); 
+}
+
+sub wait_for_command {
+    waitpid $pid, 0;
+    my $ret = $? >> 8;
+    if (com_slurp_stderr() =~ /^open3: (.+)$/) { 
+        croak $1 
+    }
+    return $ret;
+}
+
+sub com_ret_is {
+    my ($ex_ret, $descr) = @_;
+
+    local $SIG{CHLD} = 'IGNORE';
+    my $ret = wait_for_command;
+    $Test->ok(_test_ret($ret, $ex_ret), $descr);
+}
+
+sub com_stdout_is {
+    my ($ex_stdout, $descr) = @_;
+
+    my $stdout = com_slurp_stdout();
+    $Test->ok(_test_string($stdout, $ex_stdout, 'STDOUT'), $descr);
+}
+
+sub com_stderr_is {
+    my ($ex_stderr, $descr) = @_;
+
+    my $stderr = com_slurp_stderr();
+    $Test->ok(_test_string($stderr, $ex_stderr, 'STDERR'), $descr);
+}
+
+{
+    my $stderr;
+    sub com_slurp_stderr { 
+        $stderr or $stderr = _slurp_fh($stderr_h);
+    }
+}
+{
+    my $stdout;
+    sub com_slurp_stdout { 
+        $stdout or $stdout = _slurp_fh($stdout_h);
+    }
+}
+
+sub _test_one {
+    my ($s, $ex, $name) = @_;
+    my $diag = defined $ex && _test_string($s, $ex);
+    if ($diag) {
+        $Test->diag("$name doesn't match\n$diag\n");
+        return 0;
+    }
+    return 1;
+}
+
+sub cmd_is {
+    my ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr);
+
+    if (ref $_[0] eq 'HASH') {
+        my %args = %{ $_[0] };
+        ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr) = 
+            @args{qw(run stdout stderr ret descr)};
+    }
+    else {
+        ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr) = @_;
+    }
+
+
+    local $SIG{CHLD} = 'DEFAULT';
+    start_command($cmd);
+
+    my $stdout = com_slurp_stdout;
+    my $stderr = $stderr_h ? com_slurp_stderr : '';
+    my $ret = wait_for_command;
+    my $ok = 1;
+
+    _test_string($stdout, $ex_stdout, 'STDOUT') or $ok = 0;
+    _test_string($stderr, $ex_stderr, 'STDERR') or $ok = 0;
+    _test_ret($ret, $ex_ret) or $ok = 0;
+
+    $Test->ok($ok, $descr);
+}
+
+sub _slurp_fh { 
+    my $fh = shift;
+    do { local $/; <$fh> } 
+}
+
+sub _test_ret {
+    my ($got, $exp) = @_;
+
+    return 1 if not defined $exp;
+
+    if ($got != $exp) {
+        return $Test->diag(<<DIAG);
+Return value doesn't match
+Expected: $exp
+Got: $got
+DIAG
+    }
+    return 1;
+}
+
+sub _test_string {
+    my ($got, $exp, $name) = @_;
+
+    return 1 if not defined $exp;
+
+    if (ref $exp eq 'Regexp') {
+        if ($got !~ $exp) {
+            return $Test->diag(<<DIAG);
+$name doesn't match.
+Expected: '$exp'
+Got: '$got'
+DIAG
+        }
+    }
+    elsif (ref $exp eq 'CODE') {
+        if (!(my $ret = $exp->($got))) {
+            return $Test->diag(<<DIAG);
+$name doesn't match.
+Expected CODE to return true value
+Got: '$ret'
+DIAG
+        }
+    }
+    else {
+        if ($got ne $exp) {
+            return $Test->diag(<<DIAG);
+$name doesn't match.
+Expected: '$exp'
+Got: '$got'
+DIAG
+        }
+    }
+    return 1;
+}
+
+1;
+__END__
+=head1 NAME
+
+Test::Command - Test output and ret value of an external command.
+
+=head1 VERSION
+
+Version 0.1
+
+=head1 SYNOPSIS
+
+    use Test::More tests => 1;
+    use Test::Command;
+
+    cmd_is('ls', '.\n..', '', 0, "ls on empty directory");
+    cmd_is({run => 'ls', stdout => '.\n..', ret => 0});
+
+    start_command('ls');
+    ...
+    wait_for_command;
+    com_stdout_is('.\n..');
+
+=head1 DESCRIPTION
+
+Test::Command provides a simple interface for testing stdout, stderr and return
+value of an external command.
+
+=cut
+
+=head1 TESTS
+
+=over 4 
+
+=item B<is_cmd( cmd, exp_stdout, exp_stderr, exp_ret, diag )>
+
+    Runs command and tests for expected results. Pass undef in the parameter you
+    don't want to test for.
+
+=over 4 
+
+=item B<cmd> - string of command tu run
+
+=item B<exp_stdout> - expected stdout
+
+=item B<exp_stderr> - expected stderr
+
+=item B<exp_ret> - expected return value of a program
+
+=item B<diag> - diagnostic message printed if test failed
+
+=back
+
+=item B<start_command( cmd )>
+
+Start a command. Returns immediately and doesn't wait for command to finish.
+
+=item B<wait_for_command>
+
+Wait for command started with C<start_command> to finish. Returns exit status
+(return value of waipid) of the command.
+
+=item B<com_slurp_stdout>
+
+=item B<com_slurp_stderr>
+
+Slurp stdout/stderr of a process started with start_command and return it.
+
+=item B<com_stdout_is( ex_stdout, diag )>
+
+=item B<com_stderr_is( ex_stdout, diag )>
+
+Test stdout/stderr of the command started with start_command. This will block
+until the process has closed its stdout or stderr
+
+=item B<com_ret_is( ex_ret, diag )>
+
+Test for expected return value of a command started with start_command.
+
+=back
+
+All parameters that represent expected value for stdout or stderr can be a
+string, a compiled regexp or a code reference, which is passed the contents as
+a first parameter and is expected to return true or false value.
+
+=head1 AUTHOR
+
+Oliver Kindernay <oliver.kindernay@gmail.com>
+
+=cut
diff --git a/scripts/t/lib/Test/Mode6/Server.pm b/scripts/t/lib/Test/Mode6/Server.pm
new file mode 100644 (file)
index 0000000..93c4c38
--- /dev/null
@@ -0,0 +1,66 @@
+package Test::Mode6::Server;
+use strict;
+use warnings;
+use IO::Socket::INET;
+use Test::Builder;
+use NTP::Mode6::Packet;
+use Carp;
+use Exporter qw(import);
+use Errno qw(EINTR);
+use Devel::Hexdump qw(xd);
+our @EXPORT = qw(start_mode6_server expect_mode6_command stop_mode6_server);
+
+my $socket;
+my $Test = Test::Builder->new();
+
+sub start_mode6_server {
+    $socket = IO::Socket::INET->new(
+        Proto     => 'udp',
+        LocalPort => 123,
+    ) or croak "Could not create UDP socket on port 123: $!";
+    $Test->diag("NTP control server started");
+}
+
+sub _diag_and_fail {
+    $Test->diag(shift);
+    $Test->ok(0, shift);
+    0;
+}
+
+sub expect_mode6_command {
+    my ($ex_packet, $respond, $descr) = @_;
+
+    return _diag_and_fail('Not a CODE reference', $descr) 
+        if ref $respond ne 'CODE';
+
+    my $remote_addr = $socket->recv(my $in_msg = '', 4096, 0);
+    if (!$remote_addr) {
+        _diag_and_fail("Error in recv: $!", $descr) if $! != EINTR;
+        return;
+    }
+
+    my $recv_packet = NTP::Mode6::Packet->new();
+    $recv_packet->decode($in_msg);
+    my $diag = $recv_packet->eq($ex_packet);
+    if ($diag) {
+        _diag_and_fail("Packets don't match\n$diag", $descr);
+        return;
+    }
+
+    my $resp_packet = $respond->($recv_packet);
+    return _diag_and_fail('reponse sub didn\'t return NTP::Mode6::Packet object', $descr)
+        if ref $resp_packet ne 'NTP::Mode6::Packet';
+
+    my $out_msg = $resp_packet->encode;
+    $socket->send($out_msg, 0, $remote_addr) == length $out_msg
+        or return _diag_and_fail("Error sending response packet: $!", $descr);
+
+    $Test->ok(1, $descr);
+}
+
+sub stop_mode6_server {
+    close $socket;
+    $Test->diag("NTP control server stopped");
+}
+
+1;
diff --git a/scripts/t/lib/Test/Util.pm b/scripts/t/lib/Test/Util.pm
new file mode 100644 (file)
index 0000000..ea10a11
--- /dev/null
@@ -0,0 +1,167 @@
+package Test::Util;
+use strict;
+use warnings;
+use Test::Command;
+use Test::More;
+use File::Basename qw(basename);
+use Exporter       qw(import);
+use Carp           qw(croak);
+use Scalar::Util   qw(set_prototype);
+use Sub::Override;
+
+our @EXPORT_OK = qw(check_help override call_orig mock_ntp_util run_mocked);
+
+our $DIAG = 0;
+sub cdiag { diag @_ if $DIAG }
+
+sub check_help {
+    my ($script_path, @options) = @_;
+    my $script_name = basename($script_path);
+
+    cmd_is("$script_path -?", '', 
+        sub {
+            my $help = shift;
+            my $i;
+
+            cdiag "Got help '$help'";
+
+            $help =~ /^$script_name\ [^\n]+\nUSAGE:\ $script_name\ 
+                      \[\ -<flag>\ \[<val>\]\ \|\ --<name>\[{=\|\ }<val>\]\ \]
+                    /x or return 0;
+
+            cdiag "Usage line OK";
+
+            for (@options, [qw(help ?)], [qw(more-help)]) {
+                my ($long, $short) = @$_;
+                my $o = $short ? "-$short, --$long" : "  , --$long";
+
+                cdiag "Testing for $o\n";
+
+                return 0 if $help !~ /$o/;
+            }
+
+            cdiag "Options OK";
+
+            $help =~ <<'END' or return 0;
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.$
+END
+            cdiag "Bottom line OK";
+
+            return 1;
+        }, 0, "help's ok");
+}
+
+my $override = Sub::Override->new;
+
+{
+    my (@vars, @peers, @hosts, @offset_stratums);
+
+    sub mock_ntp_util {
+        my ($package, %mock) = @_;
+
+        $override->replace($package.'::ntp_read_vars' => sub { 
+                return undef if !@vars;
+                croak 'vars elements should be HASH refs' 
+                    if ref $vars[0] ne 'HASH';
+                return +{ %{ shift @vars } };
+            }) if exists $mock{read_vars};
+
+        $override->replace($package.'::ntp_peers'     => sub { 
+                return () if !@peers;
+                croak 'peer elements should be ARRAY refs'
+                    if ref $peers[0] ne 'ARRAY';
+                return @{ shift @peers }
+            }) if exists $mock{peers};
+
+        $override->replace($package.'::do_dns'        => sub { 
+                return shift @hosts 
+            }) if exists $mock{dns};
+
+        $override->replace($package.'::ntp_sntp_line' => sub { 
+                return () if !@offset_stratums;
+                croak 'offset_stratums elements should be ARRAY refs' 
+                    if ref $offset_stratums[0] ne 'ARRAY';
+                return @{ shift @offset_stratums };
+            }) if exists $mock{sntp_line};
+    }
+
+    sub run_mocked {
+        my %opts = @_;
+
+        for (qw(vars peers hosts offset_stratums)) {
+            croak "$_ is not an ARRAY reference" if exists $opts{$_} && ref $opts{$_} ne 'ARRAY';
+        }
+        croak "run is not a CODE reference" if ref $opts{run} ne 'CODE';
+
+        @vars  = @{ $opts{vars} } if defined $opts{vars};
+        @peers = @{ $opts{peers}} if defined $opts{peers};
+        @hosts = @{ $opts{hosts}} if defined $opts{hosts};
+        @offset_stratums = @{ $opts{offset_stratums} } if defined $opts{offset_stratums};
+        $opts{run}->();
+    }
+}
+
+sub override {
+    my ($package, $subname, $code) = @_;
+
+    croak 'not a CODE reference' if ref $code ne 'CODE';
+
+    my $proto = prototype $code;
+    my $override = set_prototype(sub {
+            my $caller = caller;
+            if ($caller eq $package) {
+                return $code->(@_);
+            }
+            else {
+                call_orig($package, $subname);
+            }
+        }, $proto);
+
+    {
+        no strict 'refs';
+        *{"CORE::GLOBAL::".${subname}} = $override;
+    }
+}
+
+sub call_orig {
+    my ($pkg, $name) = (shift, shift);
+    # TODO: cache this?
+    _make_helper($pkg, $name)->(@_);
+}
+
+
+# Doing this right would require some hairy code and a lot more work (just look
+# at Fatal.pm) Since we just want to override open sleep and few other builtins
+# this is sufficient.
+my %orig_helpers = (
+    sleep => 'return CORE::sleep $_[0]',
+    open  => '
+        no strict q(refs);
+        if (@_ == 1) {
+            return CORE::open($_[0]);
+        }
+        elsif (@_ == 2) {
+            return CORE::open($_[0], $_[1]);
+        }
+        elsif (@_ >= 3) {
+            return CORE::open($_[0], $_[1], @_[2 .. $#_]);
+        }
+    ',
+);
+
+# This a helper for the override sub, it generates sub that provides the
+# original package context
+sub _make_helper {
+    my ($pkg, $name) = @_;
+    my $sub = eval "
+        sub {
+            package $pkg;
+            $orig_helpers{$name}
+        }
+    ";
+    die "Eval failed for ${pkg}: $@" unless $sub;
+    return $sub;
+}
+
+1;
diff --git a/scripts/t/ntp-wait.t b/scripts/t/ntp-wait.t
new file mode 100644 (file)
index 0000000..7a01b12
--- /dev/null
@@ -0,0 +1,142 @@
+use strict;
+use warnings;
+use Test::More tests => 22;
+use Test::Command;
+use Test::Output;
+use Test::Util qw(check_help mock_ntp_util run_mocked override);
+
+my $script_name = 'ntp-wait/ntp-wait';
+
+require_ok($script_name);
+
+my @options = (
+    [qw(tries=num n)], 
+    [qw(sleep=num s)],
+    [qw(verbose v)],
+);
+check_help("./$script_name", @options);
+
+mock_ntp_util('ntp_wait', read_vars => 1);
+
+{
+    my $sleep_for;
+
+    BEGIN {
+        override('ntp_wait', sleep => sub {
+                is(shift, $sleep_for, 'sleep with the right value');
+            });
+    }
+
+    sub run_ntp_wait {
+        my (%opts) = @_;
+        $sleep_for = delete $opts{sleep_for} || 0;
+        run_mocked(run => sub { ntp_wait::run(@{ delete $opts{opts} || [] }) }, %opts);
+    }
+}
+
+my $ret;
+
+my %vars = (
+    status_line => {
+        leap => 'leap_alarm',
+    }
+);
+
+stdout_like(
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) },
+    qr/ntpd did not synchronize/,
+    'failed to synchronize in time with leap_alarm'
+);
+is($ret, 1, 'fails when failed to synchronize with leap_alarm');
+
+%vars = (
+    status_line => {
+        leap => 'sync_alarm',
+    }
+);
+
+stdout_like(
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) },
+    qr/ntpd did not synchronize/,
+    'failed to synchronize in time with sync_alarm'
+);
+is($ret, 1, 'fails when failed to synchronize with sync_alarm');
+
+stdout_like(
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) },
+    qr/ntpd did not synchronize/,
+    'failed to synchronize in time with -v'
+);
+is($ret, 1, 'fails when failed to synchronize with -v');
+
+
+%vars = (
+    status_line => {
+        leap => 'leap_bogus',
+    }
+);
+
+stdout_like( 
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 2 -s 0 -v)]) },
+    qr/Unexpected 'leap' status <leap_bogus>/,
+    'prints debug information on invalid leap status'
+);
+is($ret, 1, 'fails on invalid leap status');
+
+
+stdout_like(
+    sub { $ret = run_ntp_wait(vars => [{}], opts => [qw(-n 2 -s 0 -v)]) },
+    qr/Leap status not avalaible/,
+    'print debug information when no leap returned'
+);
+is($ret, 1, 'fails if not leap returned');
+
+%vars = (
+    status_line => {
+        leap => 'leap_none',
+    }
+);
+
+stdout_is( 
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0)]) },
+    '',
+    'prints nothing if OK'
+);
+is($ret, 0, 'succeeds if OK');
+
+stdout_like( 
+    sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) },
+    qr/OK/,
+    'prints OK if OK and -v'
+);
+is($ret, 0, 'succeeds if OK');
+
+#Test the -n switch 
+
+my @vars = (
+    { 
+        status_line => {
+            leap => 'leap_alarm',
+        }
+    },
+    { 
+        status_line => {
+            leap => 'leap_none',
+        }
+    },
+);
+stdout_like( 
+    sub { $ret = run_ntp_wait(vars => \@vars, opts => [qw(-n 2 -s 0 -v)]) },
+    qr/OK/,
+    '-n switch works'
+);
+is($ret, 0, 'succeeds if OK the second time');
+
+# Test the -s switch
+
+stdout_like( 
+    sub { $ret = run_ntp_wait(vars => \@vars, opts => [qw(-n 2 -s 10 -v)], sleep_for => 10) },
+    qr/OK/,
+    '-s switch works'
+);
+is($ret, 0, 'suceeds if OK and -s');
diff --git a/scripts/t/ntpsweep.t b/scripts/t/ntpsweep.t
new file mode 100644 (file)
index 0000000..be97482
--- /dev/null
@@ -0,0 +1,117 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Util qw(check_help override mock_ntp_util run_mocked);
+use Test::Output;
+use Sub::Override;
+
+my $script_name = "ntpsweep/ntpsweep";
+
+my @options = (
+    [qw(host-list=str l)],
+    [qw(peers p)],
+    [qw(maxlevel=num m)],
+    [qw(strip=str s)],
+);
+
+require "./$script_name";
+
+check_help("./$script_name", @options);
+
+mock_ntp_util('ntpsweep', read_vars => 1, peers => 1, sntp_line => 1, dns => 1);
+
+my $header = <<'HEADER';
+Host                             st offset(s) version     system       processor
+--------------------------------+--+---------+-----------+------------+---------
+HEADER
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }) },
+    $header."localhost                         ?\n",
+    'no information about host'
+);
+
+# Test deprecated -h switch
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-h localhost)) }) },
+    $header."localhost                         ?\n",
+    '-h switch works'
+);
+
+my @vars = ({
+    processor      => 'i686',
+    system         => 'Linux/2.6.37.6-smp',
+    daemon_version => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+});
+
+my @offset_stratum = (['0.00', 3]);
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }, offset_stratums => \@offset_stratum ) },
+    $header.'localhost                         3     0.000'.(' 'x26)."\n",
+    'one host ok but no variables'
+);
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }, offset_stratums => \@offset_stratum, vars => \@vars ) },
+    $header."localhost                         3     0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n",
+    'one host with ok variables'
+);
+
+## Add one peer
+my @peers = ([{ remote => 'pieskovisko.sk' }]);
+push @offset_stratum, ['0.01', 2];
+push @vars, {
+    processor      => 'i686',
+    system         => 'Bungalanga OS',
+    daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+};
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) },
+    $header."localhost (1)                     3     0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n"
+           ." pieskovisko.sk (0)               2     0.010 4.1.5p3\@1.2 Bungalanga O i686\n",
+    'one peer'
+);
+
+# Add another peer
+push @peers, [{ remote => 'nic.nz' }];
+push @offset_stratum, ['0.04', 1];
+push @vars, {
+    processor      => 'i686',
+    system         => 'NIC OS',
+    daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+};
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p -m 1)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) },
+    $header."localhost (1)                     3     0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n"
+           ." pieskovisko.sk (1)               2     0.010 4.1.5p3\@1.2 Bungalanga O i686\n",
+    'one peer twice but -m 1'
+);
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) },
+    $header."localhost (1)                     3     0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n"
+           ." pieskovisko.sk (1)               2     0.010 4.1.5p3\@1.2 Bungalanga O i686\n"
+           ."  nic.nz (0)                      1     0.040 4.1.5p3\@1.2 NIC OS       i686\n",
+    'one peer twice'
+);
+
+push @peers, [{ remote => 'localhost' }];
+push @offset_stratum, ['0.04', 1];
+push @vars, {
+    processor      => 'i686',
+    system         => 'NIC OS',
+    daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+};
+
+stdout_is(
+    sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) },
+    $header."localhost (1)                     3     0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n"
+           ." pieskovisko.sk (1)               2     0.010 4.1.5p3\@1.2 Bungalanga O i686\n"
+           ."  nic.nz (1)                      1     0.040 4.1.5p3\@1.2 NIC OS       i686\n"
+           ."   = localhost".(' 'x18)."\n",
+    'loop detection'
+);
+done_testing;
diff --git a/scripts/t/ntptrace.t b/scripts/t/ntptrace.t
new file mode 100644 (file)
index 0000000..f471beb
--- /dev/null
@@ -0,0 +1,154 @@
+use strict;
+use warnings;
+use Test::More tests => 6;
+use Test::Command;
+use Test::Output;
+use Test::Util qw(check_help mock_ntp_util run_mocked);
+use Sub::Override;
+
+my $script_name = 'ntptrace/ntptrace';
+
+my @options = (
+    [qw(numeric n)], 
+    [qw(max-hosts=num m)],
+    [qw(host=str r)],
+);
+check_help($script_name, @options);
+
+require "./$script_name";
+
+mock_ntp_util('ntptrace', read_vars => 1, dns => 1);
+
+my @vars = ({
+    version    => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+    processor  => 'i686',
+    system     => 'Linux/2.6.37.6-smp',
+    leap       => '00',
+    stratum    => '3',
+    precision  => '-21',
+    rootdelay  => '38.998',
+    rootdisp   => '94.413',
+    refid      => '92.240.244.202',
+    reftime    => 'd5842335.00c8980f  Sun Jul  7 2013 19:26:13.003',
+    clock      => 'd5842501.ee752d06  Sun Jul  7 2013 19:33:53.931',
+    peer       => '848',
+    tc         => '9',
+    mintc      => '3',
+    offset     => '-23.168',
+    frequency  => '-10.213',
+    sys_jitter => '14.724',
+    clk_jitter => '14.754',
+    clk_wander => '3.847',
+});
+
+stdout_is(
+    sub { run_mocked(run => sub { ntptrace::run() }, vars => [@vars,{}], hosts => [qw(localhost)]) },
+    "localhost: stratum 3, offset -0.023168, synch distance 0.113912\n",
+    'traces single host'
+);
+
+stdout_is(
+    sub { run_mocked(run => sub { ntptrace::run(qw(-n)) }, vars => [@vars,{}]) },
+    "127.0.0.1: stratum 3, offset -0.023168, synch distance 0.113912\n",
+    '-n switch works, localhost is default'
+);
+
+#Add response to a rv peer
+push @vars, {
+    srcadr     => '1.2.3.4',
+    srcport    => '123',
+    dstadr     => '192.168.1.11',
+    dstport    => '123',
+    leap       => '00',
+    stratum    => '2',
+    precision  => '-22',
+    rootdelay  => '5.142',
+    rootdisp   => '30.884',
+    refid      => '195.146.149.222',
+    reftime    => 'd584299f.8904b565  Sun Jul  7 2013 19:53:35.535',
+    rec        => 'd5842d6b.02efea46  Sun Jul  7 2013 20:09:47.011',
+    reach      => '377',
+    unreach    => '0',
+    hmode      => '3',
+    pmode      => '4',
+    hpoll      => '9',
+    ppoll      => '9',
+    headway    => '0',
+    flash      => '00 ok',
+    keyid      => '0',
+    offset     => '-24.318',
+    delay      => '33.499',
+    dispersion => '20.588',
+    jitter     => '39.916',
+    xleave     => '0.031',
+    filtdelay  => '   42.35   93.96  235.47   33.50   34.31   33.90   37.25   34.97',
+    filtoffset => '  -21.46    4.17   77.27  -24.32  -23.48  -23.17  -21.27  -25.42',
+    filtdisp   => '    0.00    8.01   15.78   23.54   31.25   39.21   47.24   55.23',
+};
+
+# Add response from the peer
+push @vars, {
+    version        => 'ntpd 4.2.4p8@1.1612-o Tue Apr 19 07:08:29 UTC 2011 (1)',
+    processor      => "i686",
+    system         => "Linux/2.6.38-10-generic-pae",
+    leap           => '00',
+    stratum        => '1',
+    precision      => '-20',
+    rootdelay      => '0.000',
+    rootdisp       => '0.348',
+    peer           => '41683',
+    refid          => 'CDMA',
+    reftime        => 'd58443c5.cc475b06  Sun Jul  7 2013 19:45:09.797',
+    poll           => '5',
+    clock          => 'd58443cd.079e0276  Sun Jul  7 2013 19:45:17.029',
+    state          => '4',
+    offset         => '0.000',
+    frequency      => '-21.795',
+    jitter         => '0.001',
+    noise          => '0.005',
+    stability      => '0.000',
+    tai            => '0',
+};
+
+stdout_is(
+    sub { run_mocked(run => sub { ntptrace::run() }, vars => \@vars, hosts => [qw(localhost kenny.oneemedia.com)]) },
+    <<'END',
+localhost: stratum 3, offset -0.023168, synch distance 0.113912
+kenny.oneemedia.com: stratum 1, offset 0.000000, synch distance 0.000348, refid 'CDMA'
+END
+    'traced a peer up to stratum 1'
+);
+
+stdout_is(
+    sub { run_mocked(run => sub { ntptrace::run(qw(-m 1)) }, vars => \@vars, hosts => [qw(localhost)]) },
+    "localhost: stratum 3, offset -0.023168, synch distance 0.113912\n",
+    '-m switch works'
+);
+
+splice @vars, 0, 1, {
+    version    => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)',
+    processor  => "i686",
+    system     => "Linux/2.6.37.6-smp",
+    leap       => '00',
+    stratum    => '3',
+    precision  => '-21',
+    rootdelay  => '38.998',
+    rootdisp   => '94.413',
+    refid      => '127.127.0.1',
+    reftime    => 'd5842335.00c8980f  Sun Jul  7 2013 19:26:13.003',
+    clock      => 'd5842501.ee752d06  Sun Jul  7 2013 19:33:53.931',
+    peer       => '848',
+    tc         => '9',
+    mintc      => '3',
+    offset     => '-23.168',
+    frequency  => '-10.213',
+    sys_jitter => '14.724',
+    clk_jitter => '14.754',
+    clk_wander => '3.847',
+};
+
+stdout_is(
+    sub { run_mocked(run => sub { ntptrace::run() }, vars => \@vars) },
+    "127.0.0.1: stratum 3, offset -0.023168, synch distance 0.113912\n",
+    'stopped on 127.127 refid'
+);
diff --git a/scripts/t/plot_summary.t b/scripts/t/plot_summary.t
new file mode 100644 (file)
index 0000000..7941a16
--- /dev/null
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+use Test::More tests => 1;
+use Test::Util qw(check_help);
+
+my $script_name = "plot_summary";
+
+my @options = (
+    [qw(directory=str)],
+    [qw(identifier=str)],
+    [qw(output-file=str)],
+    [qw(plot-term=str)],
+    [qw(dont-wait)],
+    [qw(offset-limit=float)],
+);
+
+check_help("./$script_name", @options);
diff --git a/scripts/t/summary.t b/scripts/t/summary.t
new file mode 100644 (file)
index 0000000..516dc87
--- /dev/null
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+use Test::More tests => 1;
+use Test::Util qw(check_help);
+
+my $script_name = "summary";
+
+my @options = (
+    [qw(directory=str)],
+    [qw(end-date=num)],
+    [qw(output-directory=str)],
+    [qw(peer-dist-limit=float)],
+    [qw(skip-time-steps=float)],
+    [qw(start-date=num)],
+);
+
+check_help("./$script_name", @options);
diff --git a/scripts/t/util.t b/scripts/t/util.t
new file mode 100644 (file)
index 0000000..1154086
--- /dev/null
@@ -0,0 +1,179 @@
+use strict;
+use warnings;
+use Test::More tests => 12;
+use Test::Util qw(override);
+
+BEGIN {
+    my %test;
+
+    override('NTP::Util', open => sub { 
+            is($_[1], $test{open_str}, 'open with right string')
+                if defined $test{open_str};
+            return if $test{fail_open};
+            open $_[0], '<', \$test{output};
+        });
+
+    sub test_read_vars {
+        my (%opts) = @_;
+        _parse_opts(%opts);
+        ntp_read_vars(@{ delete $opts{opts} || []});
+    }
+
+    sub test_peers {
+        my (%opts) = @_;
+        _parse_opts(%opts);
+        ntp_peers(@{ delete $opts{opts} || []});
+    }
+
+    sub test_sntp_line {
+        my (%opts) = @_;
+        _parse_opts(%opts);
+        ntp_sntp_line(@{ delete $opts{opts} || []});
+    }
+
+    sub _parse_opts {
+        my (%opts) = @_;
+        $test{fail_open} = delete $opts{fail_open} || 0;
+        $test{output}    = delete $opts{output} || '' ;
+        $test{open_str}  = delete $opts{open_str};
+    }
+}
+
+require_ok 'NTP::Util';
+NTP::Util->import(qw(ntp_read_vars ntp_peers ntp_sntp_line));
+
+eval {
+    test_read_vars(opts => [0, []], fail_open => 1,
+        open_str => "ntpq -n -c 'rv 0 ' |");
+} or do {
+    like($@, qr/Could not start ntpq: /, 'ntp_read_vars dies on failed execution');
+};
+
+is(
+    test_read_vars(opts => [0, []], output => 'Connection refused'),
+    undef,
+    'returns undef when connection refused'
+);
+
+is_deeply(
+    test_read_vars(opts => [0, [qw(var1 var2 var_3 rootdisp offset)]], 
+        output => "var1=foo, var_3=\"bar\"\nphase=1.00, rootdispersion=4.00\n"),
+        {   
+            var1     => 'foo',
+            var_3    => 'bar',
+            var2     => undef,
+            offset   => '1.00',
+            rootdisp => '4.00',
+        },
+    'variables correctly parsed'
+);
+
+is_deeply(
+    test_read_vars(opts => [0, []], 
+        output => <<'VAR_END'),
+associd=0 status=c012 leap_alarm, sync_unspec, 1 event, freq_set,
+var1=foo, var_3="bar"
+phase=1.00, rootdispersion=4.00
+VAR_END
+        {   
+            var1     => 'foo',
+            var_3    => 'bar',
+            offset   => '1.00',
+            rootdisp => '4.00',
+
+            status_line => { 
+                leap   => 'leap_alarm',
+                sync   => 'sync_unspec',
+                status => 'c012'
+            } 
+        },
+    'variables correctly parsed'
+);
+
+eval {
+    test_peers(opts => ['localhost'], fail_open => 1,
+        open_str => "ntpq -np localhost |");
+} or do {
+    like($@, qr/Could not start ntpq: /, 'ntp_peers dies on failed execution');
+};
+
+is_deeply(
+    test_peers(opts => ['localhost'], 
+        output => <<'PEER_END'),
+             remote           refid      st t when poll reach   delay   offset  jitter
+==============================================================================
++194.160.23.2    .GPS.            1 u  188 1024  377    0.290    0.051   0.058
+-217.31.205.226  195.113.144.201  2 u  227 1024  377    6.092   -0.372   0.064
+ 147.231.19.43   .INIT.          16 u    - 1024    0    0.000    0.000 4000.00
+*195.113.144.201 .GPS.            1 u  211 1024  377    5.817    0.023   0.054
+PEER_END
+    [
+          {
+            'when' => '188',
+            'reach' => '377',
+            'delay' => '0.290',
+            'st' => '1',
+            'remote' => '+194.160.23.2',
+            'poll' => '1024',
+            'jitter' => '0.058',
+            'refid' => '.GPS.',
+            't' => 'u',
+            'offset' => '0.051'
+          },
+          {
+            'when' => '227',
+            'reach' => '377',
+            'delay' => '6.092',
+            'st' => '2',
+            'remote' => '-217.31.205.226',
+            'poll' => '1024',
+            'jitter' => '0.064',
+            'refid' => '195.113.144.201',
+            't' => 'u',
+            'offset' => '-0.372'
+          },
+          {
+            'when' => '-',
+            'reach' => '0',
+            'delay' => '0.000',
+            'st' => '16',
+            'remote' => ' 147.231.19.43',
+            'poll' => '1024',
+            'jitter' => '4000.00',
+            'refid' => '.INIT.',
+            't' => 'u',
+            'offset' => '0.000'
+          },
+          {
+            'when' => '211',
+            'reach' => '377',
+            'delay' => '5.817',
+            'st' => '1',
+            'remote' => '*195.113.144.201',
+            'poll' => '1024',
+            'jitter' => '0.054',
+            'refid' => '.GPS.',
+            't' => 'u',
+            'offset' => '0.023'
+          }
+        ],
+    'peers correctly parsed'
+);
+
+eval {
+    test_sntp_line(opts => ['localhost'], fail_open => 1,
+        open_str => "sntp localhost |");
+} or do {
+    like($@, qr/Could not start sntp: /, 'ntp_sntp_line dies on failed execution');
+};
+
+is_deeply(
+    [test_sntp_line(opts => ['localhost'], 
+        output => <<'SNTP_END')],
+sntp 4.2.7p379@1.2946-o Tue Aug 27 18:55:18 UTC 2013 (4)
+Can't open KOD db file /var/db/ntp-kod for writing: Permission denied
+2013-09-19 22:49:29.381062 (-0100) +0.000007 +/- 0.077138 localhost 127.0.0.1 s4
+SNTP_END
+    ['+0.000007', '4'],
+    'stratum and offset parsed'
+);
index dcc78c07703ac1f99ad9961c16dfcbf400f1251e..bc0dcd411acf5d4fd3df3a79b800a5aea8da53f8 100644 (file)
@@ -176,7 +176,7 @@ $(PROGRAMS): version.o
 ## non-file "-levent_core".
 
 version.c: $(sntp_OBJECTS) ../libntp/libntp.a Makefile $(srcdir)/scm-rev
-       env CSET=`cat $(srcdir)/scm-rev` $(top_builddir)/../scripts/mkver sntp
+       env CSET=`cat $(srcdir)/scm-rev` $(top_builddir)/../scripts/build/mkver sntp
 
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
@@ -212,15 +212,15 @@ $(srcdir)/scm-rev:
 
 $(srcdir)/m4/version.m4: $(srcdir)/../packageinfo.sh
        TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \
-       ../scripts/genver m4/version.m4
+       ../scripts/build/genver m4/version.m4
 
 $(srcdir)/include/version.def: $(srcdir)/../packageinfo.sh
        TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \
-       ../scripts/genver include/version.def
+       ../scripts/build/genver include/version.def
 
 $(srcdir)/include/version.texi: $(srcdir)/../packageinfo.sh
        TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \
-       ../scripts/genver include/version.texi
+       ../scripts/build/genver include/version.texi
 
 $(srcdir)/../COPYRIGHT:
        cd .. && $(MAKE) $(AM_MAKEFLAGS) COPYRIGHT-please
@@ -276,7 +276,7 @@ $(srcdir)/invoke-sntp.menu: $(srcdir)/invoke-sntp.texi
 
 $(srcdir)/invoke-sntp.texi: $(srcdir)/sntp-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section sntp-opts.def
-       $(top_srcdir)/../scripts/check--help $@
+       $(top_srcdir)/../scripts/build/check--help $@
 
 $(srcdir)/sntp.html: $(srcdir)/invoke-sntp.menu $(srcdir)/invoke-sntp.texi $(srcdir)/sntp.texi $(srcdir)/include/version.texi
        cd $(srcdir) && ( makeinfo --force --html --no-split -o sntp.html sntp.texi || true )
diff --git a/sntp/ag-tpl/Mdoc.pm b/sntp/ag-tpl/Mdoc.pm
new file mode 100644 (file)
index 0000000..08db2fc
--- /dev/null
@@ -0,0 +1,515 @@
+=head1 NAME
+
+Mdoc - perl module to parse Mdoc macros
+
+=head1 SYNOPSIS
+
+    use Mdoc qw(ns pp soff son stoggle mapwords);
+
+See mdoc2man and mdoc2texi for code examples.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item def_macro( NAME, CODE, [ raw => 1, greedy => 1, concat_until => '.Xx' ] )
+
+Define new macro. The CODE reference will be called by call_macro(). You can
+have two distinct definitions for and inline macro and for a standalone macro
+(i. e. 'Pa' and '.Pa').
+
+The CODE reference is passed a list of arguments and is expected to return list
+of strings and control characters (see C<CONSTANTS>).
+
+By default the surrouding "" from arguments to macros are removed, use C<raw>
+to disable this.
+
+Normaly CODE reference is passed all arguments up to next nested macro. Set
+C<greedy> to to pass everything up to the end of the line.
+
+If the concat_until is present, the line is concated until the .Xx macro is
+found. For example the following macro definition
+
+    def_macro('.Oo', gen_encloser(qw([ ]), concat_until => '.Oc' }
+    def_macro('.Cm', sub { mapwords {'($_)'} @_ } }
+
+and the following input
+
+    .Oo
+    .Cm foo |
+    .Cm bar |
+    .Oc
+
+results in [(foo) | (bar)]
+
+=item get_macro( NAME )
+
+Returns a hash reference like:
+
+    { run => CODE, raw => [1|0], greedy => [1|0] }
+
+Where C<CODE> is the CODE reference used to define macro called C<NAME>
+
+=item parse_line( INPUT, OUTPUT_CODE, PREPROCESS_CODE )
+
+Parse a line from the C<INPUT> filehandle. If a macro was detected it returns a
+list (MACRO_NAME, @MACRO_ARGS), otherwise it calls the C<OUTPUT_CODE>, giving
+caller a chance to modify line before printing it. If C<PREPROCESS_CODE> is
+defined it calls it prior to passing argument to a macro, giving caller a
+chance to alter them.  if EOF was reached undef is returned.
+
+=item call_macro( MACRO, ARGS, ... )
+
+Call macro C<MACRO> with C<ARGS>. The CODE reference for macro C<MACRO> is
+called and for all the nested macros. Every called macro returns a list which
+is appended to return value and returned when all nested macros are processed.
+Use to_string() to produce a printable string from the list.
+
+=item to_string ( LIST )
+
+Processes C<LIST> returned from call_macro() and returns formatted string.
+
+=item mapwords BLOCK ARRAY
+
+This is like perl's map only it calls BLOCK only on elements which are not
+punctuation or control characters.
+
+=item space ( ['on'|'off] )
+
+Turn spacing on or off. If called without argument it returns the current state.
+
+=item gen_encloser ( START, END )
+
+Helper function for generating macros that enclose their arguments.
+    gen_encloser(qw({ }));
+returns
+    sub { '{', ns, @_, ns, pp('}')}
+
+=item set_Bl_callback( CODE , DEFS )
+
+This module implements the Bl/El macros for you. Using set_Bl_callback you can
+provide a macro definition that should be executed on a .Bl call.
+
+=item set_El_callback( CODE , DEFS )
+
+This module implements the Bl/El macros for you. Using set_El_callback you can
+provide a macro definition that should be executed on a .El call.
+
+=item set_Re_callback( CODE )
+
+The C<CODE> is called after a Rs/Re block is done. With a hash reference as a
+parameter, describing the reference.
+
+=back 
+
+=head1 CONSTANTS
+
+=over 4
+
+=item ns
+
+Indicate 'no space' between to members of the list.
+
+=item pp ( STRING )
+
+The string is 'punctuation point'. It means that every punctuation
+preceeding that element is put behind it. 
+
+=item soff
+
+Turn spacing off.
+
+=item son
+
+Turn spacing on.
+
+=item stoggle
+
+Toogle spacing.
+
+=item hs
+
+Print space no matter spacing mode.
+
+=back
+
+=head1 TODO
+
+* The concat_until only works with standalone macros. This means that
+    .Po blah Pc
+will hang until .Pc in encountered.
+
+* Provide default macros for Bd/Ed
+
+* The reference implementation is uncomplete
+
+=cut
+
+package Mdoc;
+use strict;
+use warnings;
+use List::Util qw(reduce);
+use Text::ParseWords qw(quotewords);
+use Carp;
+use Exporter qw(import);
+our @EXPORT_OK = qw(ns pp soff son stoggle hs mapwords gen_encloser nl);
+
+use constant {
+    ns      => ['nospace'],
+    soff    => ['spaceoff'],
+    son     => ['spaceon'],
+    stoggle => ['spacetoggle'],
+    hs      => ['hardspace'],
+};
+
+sub pp { 
+    my $c = shift;
+    return ['pp', $c ];
+}
+sub gen_encloser {
+    my ($o, $c) = @_;
+    return sub { ($o, ns, @_, ns, pp($c)) };
+}
+
+sub mapwords(&@) {
+    my ($f, @l) = @_;
+    my @res;
+    for my $el (@l) {
+        local $_ = $el;
+        push @res, $el =~ /^(?:[,\.\{\}\(\):;\[\]\|])$/ || ref $el eq 'ARRAY' ? 
+                    $el : $f->();
+    }
+    return @res;
+}
+
+my %macros;
+
+###############################################################################
+
+# Default macro definitions start
+
+###############################################################################
+
+def_macro('Xo',  sub { @_ }, concat_until => '.Xc');
+
+def_macro('.Ns', sub {ns, @_});
+def_macro('Ns',  sub {ns, @_});
+
+{
+    my %reference;
+    def_macro('.Rs', sub { () } );
+    def_macro('.%A', sub {
+        if ($reference{authors}) {
+            $reference{authors} .= " and @_"
+        }
+        else {
+            $reference{authors} = "@_";
+        }
+        return ();
+    });
+    def_macro('.%T', sub { $reference{title} = "@_"; () } );
+    def_macro('.%O', sub { $reference{optional} = "@_"; () } );
+
+    sub set_Re_callback {
+        my ($sub) = @_;
+        croak 'Not a CODE reference' if not ref $sub eq 'CODE';
+        def_macro('.Re', sub { 
+            my @ret = $sub->(\%reference);
+            %reference = (); @ret
+        });
+        return;
+    }
+}
+
+def_macro('.Bl', sub { die '.Bl - no list callback set' });
+def_macro('.It', sub { die '.It called outside of list context' });
+def_macro('.El', sub { die '.El requires .Bl first' });
+
+
+{ 
+    my $elcb = sub { () };
+
+    sub set_El_callback {
+        my ($sub) = @_;
+        croak 'Not a CODE reference' if ref $sub ne 'CODE';
+        $elcb = $sub;
+        return;
+    }
+
+    sub set_Bl_callback {
+        my ($blcb, %defs) = @_;
+        croak 'Not a CODE reference' if ref $blcb ne 'CODE';
+        def_macro('.Bl', sub { 
+
+            my $orig_it   = get_macro('.It');
+            my $orig_el   = get_macro('.El');
+            my $orig_bl   = get_macro('.Bl');
+            my $orig_elcb = $elcb;
+
+            # Restore previous .It and .El on each .El
+            def_macro('.El', sub {
+                    def_macro('.El', delete $orig_el->{run}, %$orig_el);
+                    def_macro('.It', delete $orig_it->{run}, %$orig_it);
+                    def_macro('.Bl', delete $orig_bl->{run}, %$orig_bl);
+                    my @ret = $elcb->(@_);
+                    $elcb = $orig_elcb;
+                    @ret
+                });
+            $blcb->(@_) 
+        }, %defs);
+        return;
+    }
+}
+
+def_macro('.Sm', sub { 
+    my ($arg) = @_;
+    if (defined $arg) {
+        space($arg);
+    } else {
+        space() eq 'off' ? 
+            space('on') : 
+            space('off'); 
+    }
+    () 
+} );
+def_macro('Sm', do { my $off; sub { 
+    my ($arg) = @_;
+    if (defined $arg && $arg =~ /^(on|off)$/) {
+        shift;
+        if    ($arg eq 'off') { soff, @_; }
+        elsif ($arg eq 'on')  { son, @_; }
+    }
+    else {
+        stoggle, @_;
+    }
+}} );
+
+###############################################################################
+
+# Default macro definitions end
+
+###############################################################################
+
+sub def_macro {
+    croak 'Odd number of elements for hash argument <'.(scalar @_).'>' if @_%2;
+    my ($macro, $sub, %def) = @_;
+    croak 'Not a CODE reference' if ref $sub ne 'CODE';
+
+    $macros{ $macro } = { 
+        run          => $sub,
+        greedy       => delete $def{greedy} || 0,
+        raw          => delete $def{raw}    || 0,
+        concat_until => delete $def{concat_until},
+    };
+    if ($macros{ $macro }{concat_until}) {
+        $macros{ $macros{ $macro }{concat_until} } = { run => sub { @_ } };
+        $macros{ $macro }{greedy}                  = 1;
+    }
+    return;
+}
+
+sub get_macro {
+    my ($macro) = @_;
+    croak "Macro <$macro> not defined" if not exists $macros{ $macro };
+    +{ %{ $macros{ $macro } } }
+}
+
+#TODO: document this
+sub parse_opts {
+    my %args;
+    my $last;
+    for (@_) {
+        if ($_ =~ /^\\?-/) {
+            s/^\\?-//;
+            $args{$_} = 1;
+            $last = _unquote($_);
+        }
+        else {
+            $args{$last} = _unquote($_) if $last;
+            undef $last;
+        }
+    }
+    return %args;
+}
+
+sub _is_control {
+    my ($el, $expected) = @_;
+    if (defined $expected) {
+        ref $el eq 'ARRAY' and $el->[0] eq $expected;
+    }
+    else {
+        ref $el eq 'ARRAY';
+    }
+}
+
+{
+    my $sep = ' ';
+
+    sub to_string {
+        if (@_ > 0) { 
+            # Handle punctunation
+            my ($in_brace, @punct) = '';
+            my @new = map {
+                if (/^([\[\(])$/) {
+                    ($in_brace = $1) =~ tr/([/)]/;
+                    $_, ns
+                }
+                elsif (/^([\)\]])$/ && $in_brace eq $1) {
+                    $in_brace = '';
+                    ns, $_
+                }
+                elsif ($_ =~ /^[,\.;:\?\!\)\]]$/) {
+                    push @punct, ns, $_;
+                    ();
+                }
+                elsif (_is_control($_, 'pp')) {
+                    $_->[1]
+                }
+                elsif (_is_control($_)) {
+                    $_
+                }
+                else {
+                    splice (@punct), $_;
+                }
+            } @_;
+            push @new, @punct;
+
+            # Produce string out of an array dealing with the special control characters
+            # space('off') must but one character delayed
+            my ($no_space, $space_off) = 1;
+            my $res = '';
+            while (defined(my $el = shift @new)) {
+                if    (_is_control($el, 'hardspace'))   { $no_space = 1; $res .= ' ' }
+                elsif (_is_control($el, 'nospace'))     { $no_space = 1;             }
+                elsif (_is_control($el, 'spaceoff'))    { $space_off = 1;            }
+                elsif (_is_control($el, 'spaceon'))     { space('on');               }
+                elsif (_is_control($el, 'spacetoggle')) { space() eq 'on' ? 
+                                                            $space_off = 1 : 
+                                                            space('on')              }
+                else {
+                    if ($no_space) {
+                        $no_space = 0;
+                        $res .= "$el"
+                    }
+                    else {
+                        $res .= "$sep$el"
+                    }
+
+                    if ($space_off)    { space('off'); $space_off = 0; }
+                }
+            }
+            $res
+        }
+        else { 
+            '';
+        }
+    }
+
+    sub space {
+        my ($arg) = @_;
+        if (defined $arg && $arg =~ /^(on|off)$/) {
+            $sep = ' ' if $arg eq 'on';
+            $sep = ''  if $arg eq 'off';
+            return;
+        }
+        else {
+            return $sep eq '' ? 'off' : 'on';
+        }
+    }
+}
+
+sub _unquote {
+    my @args = @_;
+    $_ =~ s/^"([^"]+)"$/$1/g for @args;
+    wantarray ? @args : $args[0];
+}
+
+sub call_macro {
+    my ($macro, @args) = @_;
+    my @ret; 
+
+    my @newargs;
+    my $i = 0;
+
+    @args = _unquote(@args) if (!$macros{ $macro }{raw});
+
+    # Call any callable macros in the argument list
+    for (@args) {
+        if ($_ =~ /^[A-Z][a-z]+$/ && exists $macros{ $_ }) {
+            push @ret, call_macro($_, @args[$i+1 .. $#args]);
+            last;
+        } else {
+            if ($macros{ $macro }{greedy}) {
+                push @ret, $_;
+            }
+            else {
+                push @newargs, $_;
+            }
+        }
+        $i++;
+    }
+
+    if ($macros{ $macro }{concat_until}) {
+        my ($n_macro, @n_args) = ('');
+        while (1) {
+            die "EOF was reached and no $macros{ $macro }{concat_until} found" 
+                if not defined $n_macro;
+            ($n_macro, @n_args) = parse_line(undef, sub { push @ret, shift });
+            if ($n_macro eq $macros{ $macro }{concat_until}) {
+                push @ret, call_macro($n_macro, @n_args);
+                last;
+            }
+            else {
+                $n_macro =~ s/^\.//;
+                push @ret, call_macro($n_macro, @n_args) if exists $macros{ $n_macro };
+            }
+        }
+    }
+
+    if ($macros{ $macro }{greedy}) {
+        #print "MACROG $macro (", (join ', ', @ret), ")\n";
+        return $macros{ $macro }{run}->(@ret);
+    }
+    else {
+        #print "MACRO $macro (", (join ', ', @newargs), ")".(join ', ', @ret)."\n";
+        return $macros{ $macro }{run}->(@newargs), @ret;
+    }
+}
+
+{
+    my ($in_fh, $out_sub, $preprocess_sub);
+    sub parse_line {
+        $in_fh          = $_[0] if defined $_[0] || !defined $in_fh;
+        $out_sub        = $_[1] if defined $_[1] || !defined $out_sub;
+        $preprocess_sub = $_[2] if defined $_[2] || !defined $preprocess_sub;
+
+        croak 'out_sub not a CODE reference' 
+            if not ref $out_sub eq 'CODE';
+        croak 'preprocess_sub not a CODE reference' 
+            if defined $preprocess_sub && not ref $preprocess_sub eq 'CODE';
+
+        while (my $line = <$in_fh>) {
+            chomp $line;
+            if ($line =~ /^\.[A-z][a-z0-9]+/ || $line =~ /^\.%[A-Z]/ || 
+                $line =~ /^\.\\"/) 
+            {
+                $line =~ s/ +/ /g;
+                my ($macro, @args) = quotewords(' ', 1, $line);
+                @args = grep { defined $_ } @args;
+                $preprocess_sub->(@args) if defined $preprocess_sub;
+                if ($macro && exists $macros{ $macro }) {
+                    return ($macro, @args);
+                } else {
+                    $out_sub->($line);
+                }
+            }
+            else {
+                $out_sub->($line);
+            }
+        }
+        return;
+    }
+}
+
+1;
+__END__
diff --git a/sntp/ag-tpl/agman-cmd.tpl b/sntp/ag-tpl/agman-cmd.tpl
new file mode 100644 (file)
index 0000000..6d937e4
--- /dev/null
@@ -0,0 +1,58 @@
+[+: -*- Mode: nroff -*-
+
+  AutoGen5 template man
+
+## agman-cmd.tpl -- Template for command line man pages
+##
+##  This file is part of AutoOpts, a companion to AutoGen.
+##  AutoOpts is free software.
+##  Copyright (C) 1992-2013 Bruce Korb - all rights reserved
+##
+##  AutoOpts is available under any one of two licenses.  The license
+##  in use must be one of these two and the choice is under the control
+##  of the user of the license.
+##
+##   The GNU Lesser General Public License, version 3 or later
+##      See the files "COPYING.lgplv3" and "COPYING.gplv3"
+##
+##   The Modified Berkeley Software Distribution License
+##      See the file "COPYING.mbsd"
+##
+##  These files have the following sha256 sums:
+##
+##  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
+##  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
+##  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
+
+# Produce a man page for section 1, 5 or 8 commands.
+# Which is selected via:  -DMAN_SECTION=n
+# passed to the autogen invocation.  "n" may have a suffix, if desired.
+#
+:+][+:
+
+(define head-line (lambda()
+        (sprintf ".TH %s %s \"%s\" \"%s\" \"%s\"\n.\\\"\n"
+                (get "prog-name") man-sect
+        (shell "date '+%d %b %Y'") package-text section-name) ))
+
+(define man-page #t)
+(out-push-new)                              :+][+:
+
+INCLUDE "mdoc-synopsis.tlib"                :+][+:
+INCLUDE "cmd-doc.tlib"                      :+][+:
+
+INVOKE build-doc                            :+][+:
+
+  (shell (string-append
+    "fn='" (find-file "mdoc2man") "'\n"
+    "test -f ${fn} || die mdoc2man not found from $PWD\n"
+    "${fn} <<\\_EndOfMdoc_ || die ${fn} failed in $PWD\n"
+    (out-pop #t)
+    "\n_EndOfMdoc_" ))
+
+:+][+:
+
+(out-move (string-append (get "prog-name") "."
+          man-sect))      :+][+:
+
+agman-cmd.tpl ends here   :+]
diff --git a/sntp/ag-tpl/agmdoc-cmd.tpl b/sntp/ag-tpl/agmdoc-cmd.tpl
new file mode 100644 (file)
index 0000000..bb605fb
--- /dev/null
@@ -0,0 +1,46 @@
+[+: -*- Mode: nroff -*-
+
+  AutoGen5 template mdoc
+
+## agman-cmd.tpl -- Template for command line mdoc pages
+##
+##  This file is part of AutoOpts, a companion to AutoGen.
+##  AutoOpts is free software.
+##  AutoOpts is Copyright (C) 1992-2013 by Bruce Korb - all rights reserved
+##
+##  AutoOpts is available under any one of two licenses.  The license
+##  in use must be one of these two and the choice is under the control
+##  of the user of the license.
+##
+##   The GNU Lesser General Public License, version 3 or later
+##      See the files "COPYING.lgplv3" and "COPYING.gplv3"
+##
+##   The Modified Berkeley Software Distribution License
+##      See the file "COPYING.mbsd"
+##
+##  These files have the following sha256 sums:
+##
+##  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
+##  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
+##  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
+
+# Produce a man page for section 1, 5 or 8 commands.
+# Which is selected via:  -DMAN_SECTION=n
+# passed to the autogen invocation.  "n" may have a suffix, if desired.
+#
+:+][+:
+
+(define head-line (lambda() (string-append
+   ".Dd "   (shell "date '+%B %e %Y' | sed 's/  */ /g'")
+   "\n.Dt " UP-PROG-NAME " " man-sect " " section-name
+   "\n.Os " (shell "uname -sr") "\n") ))
+
+(define man-page #f)                        :+][+:
+
+INCLUDE "mdoc-synopsis.tlib"                :+][+:
+INCLUDE "cmd-doc.tlib"                      :+][+:
+INVOKE build-doc                            :+][+:
+
+(out-move (string-append
+          (get "prog-name") "." man-sect))  :+][+:
+agmdoc-cmd.tpl ends here  :+]
diff --git a/sntp/ag-tpl/cmd-doc.tlib b/sntp/ag-tpl/cmd-doc.tlib
new file mode 100644 (file)
index 0000000..0f4c50c
--- /dev/null
@@ -0,0 +1,1079 @@
+[+: -*- Mode: nroff -*-
+
+  AutoGen5 template man
+
+# cmd-doc.tlib -- Template for command line man/mdoc pages
+#
+#  This file is part of AutoOpts, a companion to AutoGen.
+#  AutoOpts is free software.
+#  Copyright (C) 1992-2013 Bruce Korb - all rights reserved
+#
+#  AutoOpts is available under any one of two licenses.  The license
+#  in use must be one of these two and the choice is under the control
+#  of the user of the license.
+#
+#   The GNU Lesser General Public License, version 3 or later
+#      See the files "COPYING.lgplv3" and "COPYING.gplv3"
+#
+#   The Modified Berkeley Software Distribution License
+#      See the file "COPYING.mbsd"
+#
+#  These files have the following sha256 sums:
+#
+#  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
+#  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
+#  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
+
+# Produce a man page for section 1, 5, 6 or 8 commands.  Which is
+# selected via: -DMAN_SECTION=n.  "n" may have a suffix, if desired.
+# These sections have default section names that may be overridden
+# with -DSECTIN_NAME=XX, also passed to the autogen invocation.
+#
+:+][+:
+
+   ;;# START-BUILDTREE-ISMS
+   ;;
+   (shell "CLexe=${AGexe%/agen5/*}/columns/columns
+  test -x \"${CLexe}\" || {
+    CLexe=${AGexe%/autogen}/columns
+    test -x \"${CLexe}\" || die 'columns program is not findable'
+  }")
+
+:+][+: # END-BUILDTREE-ISMS
+
+(shell "CLexe=`echo ${AGexe} | sed 's@/autogen@/columns@'`
+       test -x \"${CLexe}\" || CLexe=`which columns`")
+
+# END-INSTALL-ONLY-CODE :+][+:
+
+(define down-prog-name (string-downcase! (get "prog-name")))
+(define UP-PROG-NAME   (get-up-name "prog-name"))
+
+(define command-doc    #t)
+(define tmp-val        (getenv "MAN_SECTION"))
+(define man-sect       (if (exist? "cmd-section") (get "cmd-section") "1"))
+(define file-name      "")
+(define sect-name      "")
+(define macro-name     "")
+(define tmp-str        "")
+(define fname-line     "")
+(define use-flags      (exist? "flag.value"))
+(define named-mode     (not (or use-flags (exist? "long-opts") )))
+
+(if (defined? 'tmp-val)
+    (if (string? tmp-val)
+        (set! man-sect tmp-val)))
+
+(define section-name
+   (if (=* man-sect "1") "User Commands"
+   (if (=* man-sect "5") "File Formats"
+   (if (=* man-sect "6") "Games"
+   (if (=* man-sect "8") "System Management"
+   (error
+    "the agman-cmd template only produces section 1, 5, 6 and 8 man pages")
+)))))
+(set! tmp-val (getenv "SECTION_NAME"))
+(if (defined? 'tmp-val) (if (string? tmp-val)
+    (set! section-name tmp-val) ))
+
+(define package-text "")
+(define package+version (and (exist? "package") (exist? "version")))
+
+(if (or (exist? "package") (exist? "version")) (begin
+  (set! package-text (string-append
+        (get "package")
+        (if package+version " (" "")
+        (get "version")
+        (if package+version ")" "") ))
+) )
+
+(define name-to-fname (lambda (nm)
+    (string-tr (string-downcase nm) " " "-") ))
+
+(define sect-line-fname (lambda () (begin
+    (out-push-new file-name)
+    (emit (string-append ".Sh \"" sect-name "\"\n"))
+    (string-append "mk-" macro-name) )))
+
+(make-tmp-dir)
+
+(define home-rc-files (exist? "homerc"))
+(define home-rc-text
+  "\nSee \\fBOPTION PRESETS\\fP for configuration files.")
+
+(define environ-init  (exist? "environrc"))
+(define environ-text
+  "\nSee \\fBOPTION PRESETS\\fP for configuration environment variables.")
+
+(emit (head-line))
+(dne ".\\\"  ")                       :+]
+.Sh NAME
+.Nm [+: prog-name                     :+]
+.Nd [+: prog-title                    :+]
+[+: INCLUDE "tpl-config.tlib"         :+][+:#
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  B U I L D   D O C
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE build-doc                      :+][+:
+
+(if (not command-doc) (begin
+    (set! home-rc-files #f)
+    (set! home-rc-text "")
+)   )                                 :+][+:
+
+INVOKE doc-sections                   :+][+:
+INVOKE ao-sections                    :+][+:
+INVOKE assemble-sections              :+][+:
+
+ENDDEF build-doc
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  A S S E M B L E   S E C T I O N S
+.\"
+.\" Emit the files for each section that was provided, and do conversions
+.\"
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE assemble-sections    :+][+:
+
+(out-push-new)
+
+:+][+:
+#.\" Insert the sections in the prescribed order
+#.\" Ensure a newline between them all.  We strip blank lines,
+#.\" so extra blank lines get removed.
+#:+]
+cvt_prog='[+:
+
+    (define target-form   (if man-page "man" "mdoc"))
+    (define source-form   (get "option-format" "texi"))
+    (define converter     (string-append source-form "2" target-form ))
+    (set! tmp-str (find-file converter))
+
+    (if (not (defined? 'tmp-str))
+        (error (string-append "cannot locate " converter)))
+    tmp-str
+:+]'
+cvt_prog=`cd \`dirname "$cvt_prog"\` >/dev/null && pwd
+         `/`basename "$cvt_prog"`
+cd $tmp_dir
+test -x "$cvt_prog" || die "'$cvt_prog' is not executable"
+{
+    list='synopsis description options option-presets'
+    for f in $list ; do cat $f ; echo ; done
+    rm -f $list name
+    list='implementation-notes environment files examples exit-status errors
+        compatibility see-also conforming-to history authors copyright bugs
+        notes'
+    for f in $list ; do cat $f ; echo ; done > .end-doc
+    rm -f $list
+    list=`ls -1 *`' .end-doc'
+    for f in $list ; do cat $f ; echo ; done
+    rm -f $list
+} 1>.doc 2>/dev/null
+[+:
+IF (exist? "doc-sub")       :+][+:
+  (out-push-new (string-append tmp-dir "/.cmds")) :+][+:
+  FOR doc-sub               :+][+:
+
+    IF (define field-name (get "sub-type" target-form))
+       (~~ target-form field-name)  :+][+:
+
+       (set! field-name (get "sub-name"))
+       (define rep-string (string-append "<<" field-name ">>"))
+       (emit (string-substitute (get "sub-text") rep-string (get field-name)))
+       "\n"
+
+    :+][+:  ENDIF           :+][+:
+
+  ENDFOR doc-sub            :+][+:
+
+  (out-pop)
+  (define post-proc-cmd (string-append
+          (get "doc-sub-cmd" "sed -f %s %s") " | "
+          egrep-prog " -v '^[  ]*$' | $cvt_prog"))
+  (sprintf post-proc-cmd ".cmds" ".doc")
+  :+][+:
+ELSE                       \:+]
+[+:(. egrep-prog):+] -v '^[    ]*$' .doc | $cvt_prog[+:
+ENDIF doc-sub exists        :+][+:
+
+(shell (out-pop #t))        :+][+:
+
+ENDDEF assemble-sections
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  D O C   S E C T I O N S
+.\"
+.\" Emit the files for each section that was provided.
+.\" If multiple sections exist, they get glued together with ".Pp"
+.\" between them.
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE doc-sections                   :+][+:
+
+FOR doc-section                       :+][+:
+  IF
+    (define sec-type (string-upcase (get "ds-type")))
+    (define sec-name (name-to-fname sec-type))
+    (define cvt-fn (find-file (string-append
+         (get "ds-format" "man") "2mdoc")))
+    (if (not (defined? 'cvt-fn))
+        (error (sprintf "Cannot locate converter for %s"
+               (get "ds-format" "man"))))
+
+    (define sec-file (string-append tmp-dir "/" sec-name))
+    (access? sec-file R_OK)           :+][+:
+        (out-push-add sec-file)
+        (emit ".Pp\n")                :+][+:
+
+  ELSE :+][+: CASE
+    (out-push-new sec-file)
+    sec-type :+][+:
+
+    == ""    :+][+: (error "unnamed doc-section") :+][+:
+    *==* " " :+].Sh "[+: (. sec-type)             :+]"[+:
+    *        :+].Sh [+: (. sec-type)              :+][+:
+    ESAC     :+][+:
+  ENDIF :+]
+[+:
+    (shell (string-append
+      "fn='" cvt-fn "'\n"
+      "test -f ${fn} || die ${fn} not found from $PWD\n"
+      "${fn} <<\\_EndOfDocSection_ || die ${fn} failed in $PWD\n"
+      (get "ds-text")
+      "\n_EndOfDocSection_"
+    ))                                :+][+:
+
+    CASE (emit "\n") sec-type         :+][+:
+    == FILES                          :+][+:
+        (if home-rc-files (emit home-rc-text))
+        (set! home-rc-files #f)       :+][+:
+
+    == ENVIRONMENT                    :+][+:
+        (if environ-init (emit environ-text))
+        (set! environ-init #f)        :+][+:
+    ESAC                              :+][+:
+
+    (out-pop)
+    :+][+:
+
+ENDFOR doc-section                    :+][+:
+
+ENDDEF doc-sections
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  A O   S E C T I O N S
+.\"
+.\" Emit the files for the sections that these templates augment,
+.\" replace or conditionally replace
+.\"
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE ao-sections              :+][+:
+  IF (. command-doc)            :+][+:
+    INVOKE cond-section  sec = "OPTIONS"       mode = "replace"       :+][+:
+    INVOKE cond-section  sec = "EXIT STATUS"   mode = "insert"        :+][+:
+
+    IF (or home-rc-files environ-init)                                :+][+:
+      INVOKE cond-section   sec = "OPTION PRESETS" mode = "replace"   :+][+:
+
+      IF (. home-rc-files)                                            :+][+:
+        INVOKE cond-section sec = "FILES"          mode = "append"    :+][+:
+      ENDIF                                                           :+][+:
+
+      IF (. environ-init)                                             :+][+:
+        INVOKE cond-section sec = "ENVIRONMENT"    mode = "append"    :+][+:
+      ENDIF                                                           :+][+:
+    ENDIF                                                             :+][+:
+
+  ELSE  section 5, not command  :+][+:
+    INVOKE cond-section sec = "FILES"          mode = "append"        :+][+:
+  ENDIF  section 5/not          :+][+:
+
+  INVOKE cond-section  sec = "SYNOPSIS"        mode = "alt"           :+][+:
+  INVOKE cond-section  sec = "DESCRIPTION"     mode = "append"        :+][+:
+  INVOKE cond-section  sec = "AUTHORS"         mode = "alt"           :+][+:
+  INVOKE cond-section  sec = "BUGS"            mode = "append"        :+][+:
+  INVOKE cond-section  sec = "NOTES"           mode = "append"        :+][+:
+
+IF (exist? "copyright")                                               :+][+:
+  INVOKE cond-section   sec = "COPYRIGHT"      mode = "alt"           :+][+:
+ENDIF                                                                 :+][+:
+
+ENDDEF ao-sections
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  C O N D I T I O N A L   S E C T I O N
+.\"
+.\" Figure out what to do for AutoOpts required sections, depending on "mode"
+.\" In all cases, if the file does not exist, invoke the "mk" macro to create
+.\" a new file.  If it does exist, then:
+.\"
+.\" alt       Alternate -- emit no text
+.\" replace   throw away any pre-existing file.
+.\" append    invoke the "append" macro to emit additional text
+.\" insert    save the current contents, replacing the .Sh line with .Pp.
+.\"           invoke the "mk" macro then emit the saved text
+.\"
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE cond-section                     :+][+:
+
+  IF
+     (set! sect-name   (string-upcase! (string-substitute
+                       (get "sec") "-" " " )))
+     (set! macro-name  (string-downcase! (string-substitute
+                       sect-name " " "-" )))
+     (set! file-name   (string-append tmp-dir "/" macro-name))
+
+     (not (access? file-name R_OK))     :+][+:
+
+     INVOKE (sect-line-fname)           :+][+:
+
+  ELSE file exists                      :+][+:
+
+     CASE (get "mode")                  :+][+:
+
+     ==  replace                        :+][+:
+         INVOKE (sect-line-fname)       :+][+:
+
+     ==  append                         :+][+:
+         (out-push-add file-name)       :+][+:
+         INVOKE (string-append "append-" macro-name) :+][+:
+
+     ==  insert                         :+][+:
+         (set! fname-line (shellf
+           "sed '1s/.Sh .*/.Pp/' %1$s ; rm -f %1$s" file-name)) :+][+:
+         INVOKE (sect-line-fname)       :+][+:
+
+     ==  alt                            :+][+:
+         (out-push-new)                 :+][+:
+
+     *                                  :+][+:
+         (error (sprintf "invalid section type: %s" (get "mode")))
+
+     :+][+:
+     ESAC                               :+][+:
+
+  ENDIF file existence/non-existence    :+][+:
+  (out-pop) :+][+: # All paths open out :+][+:
+ENDDEF cond-section
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - D E S C R I P T I O N
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-description                   :+][+:
+
+   (out-push-new)
+   (emit
+     (if (exist? "prog-man-descrip")
+         (stack-join "\n.Pp\n" "prog-man-descrip")
+         (if (exist? "detail")
+             (stack-join "\n.Pp\n" "detail")
+             "There is no description for this command."
+   ) )   )
+   (shell "sed 's/^$/.sp/' <<\\_EODesc_\n" (out-pop #t) "\n_EODesc_")
+
+   :+][+:
+   INVOKE append-description            :+][+:
+
+ENDDEF mk-description
+
+.\" = = = = = = = = = = = = = = = = = =
+.\"  A P P E N D - D E S C R I P T I O N
+.\" = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE append-description               :+][+:
+
+IF (= (get "main.main-type") "for-each"):+][+:
+
+  CASE main.handler-type                :+][+:
+  ~* ^(name|file)|.*text               \:+]
+.Pp
+This program will perform its function for every file named on the command
+line or every file named in a list read from stdin.  The arguments or input
+names must be pre\-existing files.  The input list may contain comments,
+which[+:
+
+  !E                                   \:+]
+.Pp
+This program will perform its function for every command line argument
+or every non\-comment line in a list read from stdin.
+The input list comments[+:
+
+  *                                     :+][+:
+  (error "the 'for-each' main has in invalid handler-type.") :+][+:
+  ESAC                                 \:+]
+ are blank lines or lines beginning with a '[+:
+  ?% comment-char "%s" "#" :+]' character.
+[+:
+
+ENDIF - "main" is of "for-each" type    :+][+:
+
+ENDDEF append-description
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - O P T I O N S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-options
+
+:+][+:
+
+(define opt-arg       "")
+(define dis-name      "")
+(define opt-name      "")
+(define optname-from  "A-Z_^")
+(define optname-to    "a-z--")
+(define cvt-cmd       "")
+(define formatted-doc (exist? "option-format"))
+
+(if formatted-doc (begin
+    (out-push-new)
+    (set! cvt-cmd (string-append (get "option-format") "2mdoc"))
+)   )
+
+(if (exist? "preserve-case")
+   (begin
+      (set! optname-from "_^")
+      (set! optname-to   "--")
+)  )
+
+(define fix-optname (lambda (o_nm) (begin
+   (set! o_nm (string-tr o_nm optname-from optname-to))
+   (set! o_nm (string-substitute o_nm "-" "\\-" ))
+   o_nm )))
+
+(if (exist? "option-info")
+    (string-append ".Pp\n" (get "option-info") "\n") )
+\:+]
+.Bl -tag[+:
+
+FOR flag                              :+][+:
+  IF (not (exist? "documentation"))   :+][+:
+    IF (exist? "aliases")             :+][+:
+      INVOKE emit-alias-opt           :+][+:
+    ELSE                              :+][+:
+      INVOKE emit-flag-text           :+][+:
+    ENDIF                             :+][+:
+
+  ELSE                                :+]
+.Ss "[+: (get "descrip" "") :+]"[+:
+(set! tmp-str (get "documentation" ""))
+(if (> (string-length tmp-str) 3) (string-append
+    "\n" tmp-str "\n" ))              :+][+:
+
+  ENDIF                               :+][+:
+ENDFOR flag
+
+.\" = = = = = = = = = = = = = = = = =
+.\"  help option
+.\" = = = = = = = = = = = = = = = = =
+
+:+]
+.It [+:
+  (define tmp-val (get "help-value" "\\&?"))
+  (if (and use-flags (> (string-length tmp-val) 0))
+      (string-append "Fl " tmp-val
+            (if (exist? "long-opts") " , Fl -help" "") )
+      (string-append (if (exist? "long-opts") "Fl -" "") "help" )
+  )  \:+]
+
+Display usage information and exit.[+:#
+
+.\" = = = = = = = = = = = = = = = = =
+.\"  more-help option
+.\" = = = = = = = = = = = = = = = = = :+][+:
+
+  IF (not (exist? "no-libopts")) :+]
+.It [+:
+  (define tmp-val (get "more-help-value" "\\&!"))
+  (if (and use-flags (> (string-length tmp-val) 0))
+      (string-append "Fl " tmp-val
+            (if (exist? "long-opts") " , Fl -more-help" "") )
+      (string-append (if (exist? "long-opts") "Fl -" "") "more-help" )
+  )       \:+]
+
+Pass the extended usage information through a pager.[+:
+
+ENDIF no no-libopts
+
+.\" = = = = = = = = = = = = = = = = =
+.\"  save and load configuration
+.\" = = = = = = = = = = = = = = = = = :+][+:
+
+IF (exist? "homerc") :+]
+.It [+:
+
+  IF (not (exist? "disable-save"))    :+][+:
+
+  (define tmp-val (get "save-opts-value" ">"))
+  (if (and use-flags (> (string-length tmp-val) 0))
+      (string-append "Fl " tmp-val " Oo Ar cfgfile Oc"
+            (if (exist? "long-opts")
+                " , Fl -save-opts Oo Ns = Ns Ar cfgfile Oc" ) "")
+      (string-append (if (exist? "long-opts") "Fl -" "")
+            "save-opts Oo Ns = Ns Ar cfgfile Oc" )
+  )       \:+]
+
+Save the option state to \fIcfgfile\fP.  The default is the \fIlast\fP
+configuration file listed in the \fBOPTION PRESETS\fP section, below.
+The command will exit after updating the config file.
+.It [+:
+  ENDIF  saving not disabled          :+][+:
+
+  (define tmp-val (get "load-opts-value" "<"))
+  (define tmp-str (if (exist? "long-opts") "Fl -" ""))
+
+  (if (and use-flags (> (string-length tmp-val) 0))
+      (string-append "Fl " tmp-val " Ar cfgfile"
+         (if (exist? "long-opts")
+             (string-append " , " tmp-str "load-opts Ns = Ns Ar cfgfile"
+                            " , " tmp-str "no-load-opts" )
+             "") )
+      (string-append tmp-str "load-opts Ns = Ns Ar cfgfile , "
+                     tmp-str "no-load-opts" )
+  )       \:+]
+
+Load options from \fIcfgfile\fP.
+The \fIno-load-opts\fP form will disable the loading
+of earlier config/rc/ini files.  \fI\-\-no-load-opts\fP is handled early,
+out of order.[+:
+
+ENDIF (exist? "homerc")
+
+.\" = = = = = = = = = = = = = = = = =
+.\"  version
+.\" = = = = = = = = = = = = = = = = = :+][+:
+
+IF (exist? "version") :+]
+.It [+:
+
+  (define tmp-val (get "version-value" "v"))
+  (if (and use-flags (> (string-length tmp-val) 0))
+      (string-append "Fl " tmp-val " Op Brq Ar v|c|n"
+            (if (exist? "long-opts")
+                "  Fl -version Op Brq Ar v|c|n" ) "")
+      (string-append (if (exist? "long-opts") "Fl -" "")
+            "version Op Brq Ar v|c|n" )
+  )       \:+]
+
+Output version of program and exit.  The default mode is `v', a simple
+version.  The `c' mode will print copyright information and `n' will
+print the full copyright notice.[+:
+ENDIF                                 :+]
+.El
+[+:
+
+(if formatted-doc
+    (shell (string-append
+    "fn='" (find-file cvt-cmd)
+    "'\ntest -f ${fn} || die '" cvt-cmd " not found'\n"
+    "${fn} <<\\_EndOfMdoc_ || die ${fn} failed in $PWD\n"
+    (out-pop #t)
+    "\n_EndOfMdoc_" )) )           :+][+:
+
+ENDDEF mk-options
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - O P T I O N - P R E S E T S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-option-presets              \:+]
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from [+:
+  IF (. home-rc-files)
+    :+]configuration ("RC" or ".INI") file(s)[+:
+    IF (. environ-init) :+] and values from
+[+:
+    ENDIF                             :+][+:
+  ENDIF                               :+][+:
+  IF (. environ-init) :+]environment variables named:
+.nf
+  \fB[+:(. UP-PROG-NAME):+]_<option-name>\fP or \fB[+:(. UP-PROG-NAME):+]\fP
+.fi
+.ad[+:
+    IF (. home-rc-files)              :+]
+The environmental presets take precedence (are processed later than)
+the configuration files.[+:
+    ENDIF                             :+][+:
+  ELSE                                :+].[+:
+  ENDIF                               :+][+:
+
+  CASE
+    (define rc-file
+       (get "rcfile" (string-append "." (get "prog-name") "rc")) )
+    (count "homerc")                  :+][+:
+
+  == "0"                              :+][+:
+  == "1"                              :+][+:
+
+    CASE homerc                       :+][+:
+    ~~ '\.|\$HOME'                    :+]
+The file "\fI[+: (string-append (get "homerc") "/" rc-file)
+:+]\fP" will be used, if present.[+:
+
+    == ""                             :+][+:
+
+    *                                 :+]
+The \fIhomerc\fP file is "\fI[+:homerc:+]\fP", unless that is a directory.
+In that case, the file "\fI[+: (. rc-file) :+]\fP"
+is searched for within that directory.[+:
+    ESAC                              :+][+:
+
+  *                                   :+]
+The \fIhomerc\fP files are [+:
+    FOR homerc ", "                   :+][+:
+      IF (last-for?)                  :+]and [+:
+      ENDIF :+]"\fI[+: homerc :+]\fP"[+: ENDFOR :+].
+If any of these are directories, then the file \fI[+: (. rc-file) :+]\fP
+is searched for within those directories.[+:
+  ESAC                                :+][+:
+
+ENDDEF mk-option-presets
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - E X I T - S T A T U S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-exit-status                \:+]
+One of the following exit values will be returned:
+.Bl -tag
+[+:
+(ag-fprintf 0 ".It 0 \" (EXIT_%s)\"\n%s\n"
+    (string->c-name! (string-upcase (get "exit-name[0]" "SUCCESS")))
+    (get "exit-desc[0]" "Successful program execution.") )
+
+(define need-ex-noinput  (exist? "homerc"))
+(define need-ex-software #t)
+
+(ag-fprintf 0 ".It 1 \" (EXIT_%s)\"\n%s\n"
+    (string->c-name! (string-upcase (get "exit-name[1]" "FAILURE")))
+    (get "exit-desc[1]"
+    "The operation failed or the command syntax was not valid.")) :+][+:
+
+FOR exit-desc (for-from 2)            :+][+:
+  (if (= (for-index) 66)
+      (set! need-ex-noinput  #f)
+      (if (= (for-index) 70)
+          (set! need-ex-software #f) ))
+
+  (set! tmp-str (get (sprintf "exit-name[%d]" (for-index)) "* unnamed *"))
+  (sprintf ".It %d \" (EXIT_%s)\"\n%s\n"
+    (for-index)
+    (string-upcase (string->c-name! tmp-str))
+    (get "exit-desc" ""))             :+][+:
+ENDFOR exit-desc                      :+][+:
+(if need-ex-noinput
+    (emit ".It 66 \" (EX_NOINPUT)\"
+A specified configuration file could not be loaded.\n"))
+
+(if need-ex-software
+    (emit ".It 70 \" (EX_SOFTWARE)\"
+libopts had an internal operational error.  Please report
+it to autogen-users@lists.sourceforge.net.  Thank you.\n"))
+
+(if (> (string-length fname-line) 1)
+    (emit fname-line))                :+]
+.El
+[+:
+
+ENDDEF mk-exit-status
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - A U T H O R S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-authors                     :+][+:
+
+  (define remove-authors #t)
+
+  (set! tmp-val
+      (if (exist? "copyright.author")
+          (stack-join ",\n" "copyright.author")
+          (stack-join ",\n" "copyright.owner") ))
+
+  (if (> (string-length tmp-val) 1)
+      (string-append tmp-val "\n")
+      (delete-file file-name))
+
+  :+][+:
+
+ENDDEF mk-authors
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - B U G S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-bugs                        :+][+:
+
+    (set! tmp-val (get "copyright.eaddr" (get "eaddr")))
+    (if (> (string-length tmp-val) 1)
+        (string-append "Please send bug reports to: " tmp-val "\n")
+        (delete-file file-name) )
+    :+][+:
+
+ENDDEF mk-bugs                        :+][+:
+
+DEFINE append-bugs                    :+][+:
+
+    (set! tmp-val (get "copyright.eaddr" (get "eaddr")))
+    (if (> (string-length tmp-val) 1)
+        (string-append "\n.Pp\nPlease send bug reports to: " tmp-val "\n") )
+    :+][+:
+
+ENDDEF append-bugs
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - C O P Y R I G H T  (+ licensing)
+.\"
+.\"  This section is guaranteed to be the last section in the man page
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-copyright                    \:+]
+Copyright (C) [+: copyright.date        :+] [+:
+  (get "copyright.owner" (get "copyright.author" (get "copyright.eaddr")))
+  :+] all rights reserved.
+[+: CASE (get "copyright.type")         :+][+:
+    = note  :+][+: (get "copyright.text") :+][+:
+    == ''   :+]This program has an unspecified license.[+:
+
+    *       :+][+:
+    (string-append "This program is released under the terms of "
+            (license-name (get "copyright.type")) ".")    :+][+:
+
+  ESAC      :+]
+[+:
+ENDDEF mk-copyright
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - N O T E S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-notes                       \:+]
+This manual page was \fIAutoGen\fP-erated from the \fB[+: prog-name :+]\fP
+option definitions.
+[+:
+
+ENDDEF mk-notes
+
+.\" = = = = = APPEND TO IT:           :+][+:
+
+DEFINE append-notes                   :+]
+.Pp
+This manual page was \fIAutoGen\fP-erated from the \fB[+: prog-name :+]\fP
+option definitions.[+:
+
+ENDDEF append-notes
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - E N V I R O N M E N T
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-environment                 :+][+:
+  INVOKE  append-environment          :+][+:
+ENDDEF mk-environment
+
+.\" = = = = = APPEND TO IT:           :+][+:
+
+DEFINE append-environment             :+]
+[+:(. environ-text)                   :+][+:
+ENDDEF append-environment
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  M K - F I L E S
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE mk-files                       :+][+:
+  INVOKE  append-files                :+][+:
+ENDDEF mk-files
+
+.\" = = = = = APPEND TO IT:           :+][+:
+
+DEFINE append-files                   :+]
+[+:(. home-rc-text)                   :+][+:
+ENDDEF append-files
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  E M I T   A L I A S   O P T
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE emit-alias-opt                   :+]
+.It [+:
+  IF (exist? "value")                   :+][+:
+    IF (exist? "long-opts")            \:+]
+ Fl [+:value:+] , Fl \-[+: name         :+][+:
+    ELSE                               \:+]
+ Fl [+:value:+][+:
+    ENDIF  (exist? "long-opts")         :+][+:
+
+  ELSE  value does not exist -- named option only  :+][+:
+
+    IF (not (exist? "long-opts"))      \:+]
+ [+: name :+][+:
+    ELSE                               \:+]
+ Fl \-[+: (get "name")                   :+][+:
+    ENDIF                               :+][+:
+  ENDIF                                 :+]
+This is an alias for the \fI--[+: aliases :+]\fR option.[+:
+  IF (exist? "deprecated")            :+]
+.sp
+.B
+NOTE: THIS OPTION IS DEPRECATED
+[+:
+  ENDIF                               :+][+:
+ENDDEF emit-alias-opt
+
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+.\"  E M I T   F L A G   T E X T
+.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+:
+
+DEFINE emit-flag-text                 :+][+:
+
+  (if (exist? "enable")
+      (set! opt-name (string-append (get "enable") "-" (get "name")))
+      (set! opt-name (get "name")) )
+  (if (exist? "disable")
+      (set! dis-name (string-append (get "disable") "-" (get "name")))
+      (set! dis-name "") )
+
+  (set! opt-name (fix-optname opt-name))
+  (if (> (string-length dis-name) 0)
+      (set! dis-name (fix-optname dis-name)) )
+
+  (if (not (exist? "arg-type"))
+      (set! opt-arg "")
+      (set! opt-arg (string-append "Ar "
+            (fix-optname (if (exist? "arg-name")
+                (get "arg-name")
+                (string-downcase! (get "arg-type"))  ))
+            ))
+  )
+
+:+]
+.It [+:
+  IF (exist? "value")                   :+][+:
+    IF (exist? "long-opts")             :+][+:
+
+          # * * * * * * * * * * * * * * * * * * * *
+          *
+          *  The option has a flag value (character) AND
+          *  the program uses long options
+          *
+          \:+]
+ Fl [+:value:+][+:
+      IF (not (exist? "arg-type"))      :+] , Fl -[+:
+      ELSE  :+] [+:(. opt-arg):+] , Fl -[+:
+      ENDIF :+][+: (. opt-name)         :+] [+:
+      IF (exist? "arg-type")            :+][+:
+              ? arg-optional Oo Ns = Ns
+              :+] [+:  (. opt-arg)      :+] [+:
+              arg-optional Oc           :+][+:
+      ENDIF                             :+][+:
+      IF (exist? "disable") :+] , Fl -[+:(. dis-name):+][+:
+      ENDIF                             :+][+:
+
+    ELSE                                :+][+:
+
+          # * * * * * * * * * * * * * * * * * * * *
+          *
+          *  The option has a flag value (character) BUT
+          *  the program does _NOT_ use long options
+          *
+          \:+]
+ Fl [+:value:+] [+:
+      IF (exist? "arg-type")            :+][+:
+            arg-optional Oo     :+] [+:(. opt-arg):+] [+:
+            arg-optional Oc     :+] [+:
+      ENDIF "                           :+][+:
+    ENDIF  (exist? "long-opts")         :+][+:
+
+
+  ELSE  value does not exist -- named option only  :+][+:
+
+    IF (not (exist? "long-opts"))       :+][+:
+
+          # * * * * * * * * * * * * * * * * * * * *
+          *
+          *  The option does not have a flag value (character).
+          *  The program does _NOT_ use long options either.
+          *  Special magic:  All arguments are named options.
+          *
+          \:+]
+ [+: (. opt-name) :+] [+:
+      IF (exist? "arg-type")            :+] [+:
+         ? arg-optional ' Oo = Ns' ' Ns = Ns '
+         :+] [+:(. opt-arg)              :+] [+:
+         arg-optional Oc                :+] [+:
+      ENDIF:+][+:
+      IF (exist? "disable") :+] , Fl -[+:(. dis-name):+][+:
+      ENDIF                             :+][+:
+
+
+    ELSE                                :+][+:
+          # * * * * * * * * * * * * * * * * * * * *
+          *
+          *  The option does not have a flag value (character).
+          *  The program, instead, only accepts long options.
+          *
+          \:+]
+ Fl -[+: (. opt-name) :+] [+:
+
+      IF (exist? "arg-type") :+][+:
+            arg-optional Oo :+] Ns = Ns [+:(. opt-arg):+] [+:
+            arg-optional Oc :+][+:
+      ENDIF                             :+][+:
+
+      IF (exist? "disable")
+        :+], " Fl \-[+:(. dis-name):+]"[+:
+      ENDIF                             :+][+:
+    ENDIF                               :+][+:
+  ENDIF                                 :+]
+[+: (get "descrip" "") :+].[+:
+
+  IF (exist? "min")                     :+]
+This option is required to appear.[+:
+  ENDIF                                 :+][+:
+
+  IF (exist? "max") :+]
+This option may appear [+:
+    IF % max (= "%s" "NOLIMIT")
+      :+]an unlimited number of times[+:ELSE
+      :+]up to [+: max :+] times[+:
+    ENDIF:+].[+:
+  ENDIF:+][+:
+
+  IF (exist? "disable")               :+]
+The \fI[+:(. dis-name):+]\fP form will [+:
+    IF (exist? "stack-arg")
+          :+]clear the list of option arguments[+:
+    ELSE  :+]disable the option[+:
+    ENDIF :+].[+:
+  ENDIF:+][+:
+
+  IF (exist? "enabled")               :+]
+This option is enabled by default.[+:
+  ENDIF                               :+][+:
+
+  IF (exist? "no-preset")             :+]
+This option may not be preset with environment variables
+or in initialization (rc) files.[+:
+  ENDIF                               :+][+:
+
+  IF (and (exist? "default") named-mode) :+]
+This option is the default option.[+:
+  ENDIF                               :+][+:
+
+  IF (exist? "equivalence")           :+]
+This option is a member of the [+:equivalence:+] class of options.[+:
+  ENDIF                               :+][+:
+
+  IF (exist? "flags-must")            :+]
+This option must appear in combination with the following options:
+[+: FOR flags-must ", " :+][+:flags-must:+][+:ENDFOR:+].[+:
+  ENDIF                               :+][+:
+
+  IF (exist? "flags-cant")            :+]
+This option must not appear in combination with any of the following options:
+[+: FOR flags-cant ", " :+][+:flags-cant:+][+:ENDFOR:+].[+:
+  ENDIF                               :+][+:
+
+
+  IF (~* (get "arg-type") "key|set") :+]
+This option takes a keyword as its argument[+:
+
+    IF (=* (get "arg-type") "set")
+
+:+] list.  Each entry turns on or off
+membership bits.  The bits are set by name or numeric value and cleared
+by preceding the name or number with an exclamation character ('!').
+They can all be cleared with the magic name \fInone\fR and they can all be set
+with
+.IR all .
+A single option will process a list of these values.[+:
+
+    ELSE
+
+:+].  The argument sets an enumeration value that can
+be tested by comparing them against the option value macro.[+:
+
+    ENDIF
+
+:+]
+The available keywords are:
+.in +4
+.nf
+.na
+[+: (shellf "${CLexe} --indent='' --spread=1 -W50 <<_EOF_\n%s\n_EOF_"
+            (join "\n" (stack "keyword"))  )   :+]
+.fi
+or their numeric equivalent.
+.in -4[+: (if (exist? "arg-default") "\n.sp" ) :+][+:
+
+  ELIF (=* (get "arg-type") "num")    :+]
+This option takes an integer number as its argument.[+:
+
+    IF  (exist? "arg-range")          :+]
+The value of
+.[+:(. opt-arg):+]
+is constrained to being:
+.in +4
+.nf
+.na[+:FOR arg_range ", or"            :+]
+[+: (shellf "
+range='%s'
+
+case \"X${range}\" in
+X'->'?*  )
+  echo \"less than or equal to\" `
+    echo $range | sed 's/->//' ` ;;
+
+X?*'->'  )
+  echo \"greater than or equal to\" `
+    echo $range | sed 's/->.*//' ` ;;
+
+X?*'->'?* )
+  echo \"in the range \" `
+    echo $range | sed 's/->/ through /' ` ;;
+
+X?* )
+  echo exactly $range ;;
+
+X* ) echo $range is indeterminate
+esac"
+
+(get "arg-range") )
+:+][+:
+      ENDFOR arg-range :+]
+.fi
+.in -4[+:
+
+    ENDIF  arg-range exists           :+][+:
+
+  ENDIF  arg-type key/set/num         :+][+:
+
+  IF (exist? "arg-default")           :+]
+The default
+.[+: (. opt-arg) :+]
+for this option is:
+.ti +4
+ [+: (join " + " (stack "arg-default" )) :+][+:
+  ENDIF                               :+]
+.sp
+[+:
+ (if (exist? "doc") (string-substitute (get "doc" "") "\n\n" "\n.sp\n")
+     "This option has not been fully documented." ) :+][+:
+  IF (exist? "deprecated")            :+]
+.sp
+.B
+NOTE: THIS OPTION IS DEPRECATED
+[+:
+  ENDIF                               :+][+:
+
+ENDDEF emit-flag-text
+
+.\" cmd-doc.tlib ends here \:+]
diff --git a/sntp/ag-tpl/mdoc-synopsis.tlib b/sntp/ag-tpl/mdoc-synopsis.tlib
new file mode 100644 (file)
index 0000000..35fe05b
--- /dev/null
@@ -0,0 +1,92 @@
+[+: autogen5 template man :+][+: 
+DEFINE mk-synopsis                          :+][+:
+  (out-push-new file-name)                 \:+]
+.Sh SYNOPSIS
+.Nm[+:
+
+  IF (. use-flags)                          :+][+:
+    IF (exist? "long-opts")                 :+]
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+[+: ELSE no long options:                   :+]
+.Op Fl flags
+.Op Fl flag Op Ar value
+[+: ENDIF
+                                            :+][+:
+  ELIF (exist? "long-opts")
+                                            :+]
+.Op Fl \-option-name
+.Op Fl \-option-name Ar value
+[+:
+
+  ELIF  (not (exist? "argument"))           :+]
+.Op Ar option\-name Ar value
+.Pp
+All arguments are named options.
+[+:
+  ENDIF                                     :+][+:
+
+  IF (exist? "argument")                    :+][+:
+    argument                                :+][+:
+
+    IF (exist? "reorder-args")              :+]
+.Pp
+Operands and options may be intermixed.  They will be reordered.
+[+: ENDIF                                   :+][+:
+
+  ELIF (or (exist? "long-opts") use-flags)
+
+:+]
+.Pp
+All arguments must be options.
+[+:
+
+  ENDIF                                     :+][+:
+
+  IF (exist? "main")                        :+][+:
+  CASE main.main-type                       :+][+:
+  == shell-process                          :+]
+.Pp
+This program will emit text that is expected to be evaluated by
+a Bourne-compatible shell, thus digesting the options for the script.[+:
+
+  == shell-parser                           :+]
+.Pp
+This program is designed to produce output suitable for inclusion
+into a shell script that will parse the options described.[+:
+
+  == for-each                               :+]
+.Pp
+The operands that this program operates on may be specified either
+on the command line or read from standard input, one per line.
+In that input, leading and trailing white space is stripped,
+blank lines are ignored[+:
+
+    IF (define comment-char (get "comment-char" "#"))
+       (> (string-length comment-char) 1)  \:+]
+ and lines beginning with the character
+.I [+: (substring comment-char 1 0):+]
+are treated as comments[+:
+    ENDIF :+].[+:
+
+    IF (exist? "interleaved")               :+]
+Options may be interleaved with operands both on the command
+line and when operands are read from standard input.[+:
+    ENDIF  interleaved
+
+:+]
+Standard input may not be a terminal.[+:
+
+  ESAC  main-type                           :+][+:
+  ENDIF  main exists                        :+]
+.Pp
+[+:
+
+FOR explain   "\n.Pp\n"                     :+][+:
+  (get "explain" "")                        :+][+:
+ENDFOR                                      :+][+:
+
+(out-pop)                                   :+][+:
+ENDDEF mk-synopsis                          :+]
diff --git a/sntp/ag-tpl/mdoc2man b/sntp/ag-tpl/mdoc2man
new file mode 100755 (executable)
index 0000000..a3041ee
--- /dev/null
@@ -0,0 +1,181 @@
+#!/usr/bin/perl
+package mdoc2man;
+use strict;
+use warnings;
+use File::Basename;
+use lib dirname(__FILE__);
+use Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser);
+
+########
+## Basic
+########
+
+Mdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1);
+Mdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1);
+Mdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } );
+Mdoc::def_macro( '.Nd', sub { "\\- @_" } );
+
+# Macros that enclose things
+Mdoc::def_macro( '.Brq', gen_encloser(qw({ }))          , greedy => 1 );
+Mdoc::def_macro( '.Op' , gen_encloser(qw([ ]))          , greedy => 1 );
+Mdoc::def_macro( '.Qq' , gen_encloser(qw(" "))          , greedy => 1 );
+Mdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 );
+Mdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
+Mdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
+Mdoc::def_macro( '.Pq' , gen_encloser(qw/( )/)          , greedy => 1 );
+Mdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1);
+
+Mdoc::def_macro( 'Oo',  sub { '[', @_ } );
+Mdoc::def_macro( 'Oc',  sub { ']', @_ } );
+
+Mdoc::def_macro( 'Po',  sub { '(', @_} );
+Mdoc::def_macro( 'Pc',  sub { ')', @_ } );
+
+Mdoc::def_macro( 'Bro', sub { '{', ns, @_ } );
+Mdoc::def_macro( 'Brc', sub { '}', @_ } );
+
+Mdoc::def_macro( '.Oo',  gen_encloser(qw([ ])), concat_until => '.Oc' );
+Mdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' );
+Mdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc' );
+
+Mdoc::def_macro( '.Ev', sub { @_ } );
+Mdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 );
+Mdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } );
+Mdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\fR(".(shift).")\\f[]", @_ } );
+Mdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\fR()\\f[]" } );
+Mdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\fR()\\f[]" } );
+Mdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } );
+Mdoc::def_macro( '.Ux', sub { "UNIX", @_ } );
+
+Mdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } );
+Mdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } );
+{
+    my $name;
+    Mdoc::def_macro('.Nm', sub {
+        $name = shift if (!$name);
+        "\\f\\*[B-Font]$name\\fP", @_
+    } );
+}
+
+########
+## lists
+########
+
+my %lists = (
+    bullet => sub {
+        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
+    },
+
+    tag    => sub {
+        my (%opts) = @_;
+
+        my $width = '';
+
+        if (exists $opts{width}) {
+            $width = ' '.((length $opts{width})+1);
+        }
+
+        if (exists $opts{compact}) {
+            my $dobrns = 0;
+            Mdoc::def_macro('.It', sub {
+                    my @ret = (".TP$width\n.NOP", hs);
+                    if ($dobrns) {
+                        ".br\n.ns\n", ns, @ret, @_;
+                    }
+                    else {
+                        $dobrns = 1;
+                        @ret, @_;
+                    }
+                }, raw => 1);
+        }
+        else {
+            Mdoc::def_macro('.It', sub {
+                    ".TP$width\n.NOP", hs, @_
+                }, raw => 1);
+        }
+    },
+);
+
+Mdoc::set_Bl_callback(do { my $nested = 0; sub {
+    my $type = shift;
+    my %opts = Mdoc::parse_opts(@_);
+    if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) {
+
+        # Wrap nested lists with .RS and .RE
+        Mdoc::set_El_callback(sub { 
+                return '.RE' if $nested-- > 1;
+                return '.PP';
+            });
+
+        $lists{$1}->(%opts);
+
+        if ($nested++) {
+            return ".RS";
+        }
+        else {
+            return ();
+        }
+    }
+    else {
+        die "Invalid list type <$type>";
+    }
+}}, raw => 1);
+
+# don't bother with arguments for now and do what mdoc2man'.sh' did
+
+Mdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } );
+Mdoc::def_macro('.Ed', sub { ".in -4\n.fi" } );
+
+Mdoc::set_Re_callback(sub { 
+        my ($reference) = @_;
+        <<"REF";
+$reference->{authors},
+\\fI$reference->{title}\\fR,
+$reference->{optional}\n.PP
+REF
+});
+
+# Define all macros which have the same sub for inline and standalone macro
+for (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) {
+    my $m = Mdoc::get_macro(".$_");
+    Mdoc::def_macro($_, delete $m->{run}, %$m);
+}
+
+sub print_line {
+    print shift."\n";
+}
+
+sub run {
+    print <<'DEFS';
+.de1 NOP
+.  it 1 an-trap
+.  if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+DEFS
+
+    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) {
+        my @ret = Mdoc::call_macro($macro, @args);
+        print_line(Mdoc::to_string(@ret)) if @ret;
+    }
+    return 0;
+}
+
+exit run(@ARGV) unless caller;
+
+1;
+__END__
index d6aacf0e99997ac548e0015ff4a4d6172e0c95a5..b5e96f5a1145e99fbb17a84cf2d62ce4cde9079d 100755 (executable)
 #! /usr/bin/perl
-
+package mdoc2texi;
 use strict;
-
-my ($line);
-my ($bdEd);
-my ($blCf,$blEl,@blEl,$blIt,@blIt);
-my ($progName);
-my (@words,$wc);
-my ($extArg,$ns,$sm,$wantspace);
-my ($noNl);
-my ($ref,$refCount);
-my (%anchor, $aCount);
-
-$aCount = 0;
-$noNl = 0;
-$ns = 0;               # .Ns (no spaces from here to first parameter)
-$sm = 1;               # Spacing mode
-$extArg = 0;           # Extended Arguments - disable NLs.
-$ref = 0;
-$refCount = 0;
-
-###
-#
-# We don't know what order we'll see .Sx
-#
-# Whenever we find one, we look it up.
-# If it doesn't exist we assign it an anchor name.
-# Regardless, we "return" the anchor name, as we're either going
-# to define the anchor point using '@anchor{anchor-1}' or reference
-# the anchor using something like '@xref{anchor-1,,whatever}'
-#
-###
-
-while ($line = <STDIN>)
-{
-    chomp $line;
-
-    $wc = 0;
-
-    if ($line =~ /^\./)
-    {
-       $line =~ s/^\.//;
-       @words = split(/\s+/, $line);
-       parseMacro();
-    }
-    else
-    {
-       print $line;
-    }
-
-    if ($noNl)
-    {
-       $noNl = 0;
-    }
-    elsif (!$extArg)
-    {
-       print "\n";
-    }
-}
-
-sub Anchor ($)
-{
-    my $string = shift;
-
-    # Look up the provided string.
-    # If it's not there, bump $aCount and "add" the anchor.
-    # Return the anchor.
-
-    if (!exists $anchor{$string})
-    {
-        ++$aCount;
-        $anchor{$string} = "anchor-$aCount";
-    }
-
-    return $anchor{$string};
-}
-
-sub Handle_An
-{
-    # We should eventually support -nosplit and -split.
-    # Usage: .An <author name> ...
-    #   .An "Joe Author"                Joe Author
-    #   .An "Joe Author" ,              Joe Author,
-    #   .An "Joe Author" Aq user@site   Joe Author <user@site>
-    #   .An "Joe Author" ) ) ,          Joe Author)),
-
-    do {
-       parseQuote(\@words) if ($words[0] =~ /^"/);
-       print shift @words;             # XXX: Spaces?
-    } while scalar(@words);
-
-    # Anything else should be punctuation.
-    while ($_ = shift @words)
-    {
-        print;
-    }
-
-    print "\@*";
-}
-
-sub Handle_Bd
-{
-    # Must end with a .Ed.
-    # Bd {-literal | -filled | -unfilled | -ragged | -centered} 
-    #  [-offset <string>] [-file <file name>] [-compact]
-
-    my ($bd);
-
-    if ($words[0] eq '-literal')       # Literal font display.
-    {
-       $bd = "\@verbatim";
-       $bdEd = "\@end verbatim";
-    }
-    elsif ($words[0] eq '-filled')     # Filled display (R/L justified)
-    {
-       die "Handle_Bd: -filled not supported.\n";
-       $bd = "\@table \@asis";
-       $bdEd = "\@end table";
-    }
-    elsif ($words[0] eq '-unfilled')   # Display as typed.
-    {
-       die "Handle_Bd: -unfilled not supported.\n";
-       $bd = "\@table \@asis";
-       $bdEd = "\@end table";
-    }
-    elsif ($words[0] eq '-ragged')     # Left-justified only.
-    {
-       die "Handle_Bd: -ragged not supported.\n";
-       $bd = "\@table \@asis";
-       $bdEd = "\@end table";
-    }
-    elsif ($words[0] eq '-centered')   # Center each line.
-    {
-       die "Handle_Bd: -centered not supported.\n";
-       $bd = "\@table \@asis";
-       $bdEd = "\@end table";
-    }
-    else
-    {
-       die "Handle_Bd: Unknown list type <$words[0]>\n";
-    }
-
-    shift @words;
-
-    while ($_ = shift @words)
-    {
-       if (/^-file$/)
-       {
-           die "Handle_Bd: -file not supported.\n";
-       }
-       elsif (/^-offset$/)
-       {
-           die "Handle_Bd: -offset not supported.\n";
-           $_ = shift @words;
-           if (/^left$/)
-           {
-           }
-           elsif (/^center$/)
-           {
-           }
-           elsif (/^indent$/)
-           {
-           }
-           elsif (/^indent-two$/)
-           {
-           }
-           elsif (/^right$/)
-           {
-           }
-           else
-           {
-               die "Handle_Bd: Unexpected value for -offset: <$_>\n";
-           }
-       }
-       else
-       {
-           die "Handle_Bd: unexpected argument: <$_>\n";
-       }
-    }
-    print $bd;
-}
-
-sub Handle_Bl
-{
-    # Must end with a .El.  May be nested, including inside displays.
-    #
-    # .Bl {-hang | -ohang | -tag | -diag | -inset} [-width <string>] \
-    #    [-offset <string>]
-    # .Bl -column [-offset <string>] <string1> <string2> ...
-    # .Bl {-item | -enum [-nested] | -bullet | -hyphen | -dash} \
-    #    [-offset <string>] [-compact]
-    # "-offset indent" uses a standard indent.
-    # -compact suppresses insertion of vertical space before the list and
-    # between the list items.
-    my ($inMulti);
-
-    # For nesting, save needed context:
-    # $blEl
-    # $blIt
-    unshift @blEl, $blEl;
-    unshift @blIt, $blIt;
-
-    $blEl = "blEl - XXX!";
-    $blIt = "";                                # Would undef be easier?
-
-    $inMulti = 0;
-    if ($words[0] eq '-hang')          # hanging tags
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-ohang')      # tag on its own line, no indent
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-tag')                # like hang.
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-diag')       # man (4) diagnostic list - inset
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-inset')      # inset list - See the mdoc page.
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-column')     # Multiple columns
-    {
-       print "\@multitable\n";         # XXX Set $wc to 0?
-       $blEl = "\@end multitable";
-       $blCf = "\@columnfractions";
-       $inMulti = 1;
-    }
-    elsif ($words[0] eq '-item')       # Item with no list markers
-    {
-       print "\@table \@asis";
-       $blEl = "\@end table";
-    }
-    elsif ($words[0] eq '-enum')       # Enumerated (numbered) list
-    {
-       print "\@itemize \@enumerate";
-       $blEl = "\@end enumerate";
-    }
-    elsif ($words[0] eq '-bullet')     # Bullet list
-    {
-       print "\@itemize \@bullet";
-       $blEl = "\@end itemize";
-    }
-    elsif ($words[0] eq '-hyphen')     # dash/hyphen list
-    {
-       # What would be better?  Maybe a 2 column table...
-       print "\@itemize \@bullet";
-       $blEl = "\@end itemize";
-       $blIt = "-";                    # @minus ?
-    }
-    elsif ($words[0] eq '-dash')       # dash/hyphen list
-    {
-       # What would be better?  Maybe a 2 column table...
-       print "\@itemize \@bullet";
-       $blEl = "\@end itemize";
-       $blIt = "-";                    # @minus ?
-    }
-    else
-    {
-       die "Handle_Bl: Unknown list type <$words[0]>\n";
-    }
-
-    shift @words;
-
-    while ($_ = shift @words)
-    {
-       if (/^-width$/)
-       {
-           # Maybe some day we will do something with the value...
-           parseQuote(\@words) if ($words[0] =~ /^"/);
-           shift @words;
-       }
-       elsif (/^-offset$/)
-       {
-           # Maybe some day we will do something with the value...
-           shift @words;
-       }
-       elsif (/^-compact$/)
-       {
-           # No argument expected
-       }
-       elsif ($inMulti)                        # -column width
-       {
-           # Maybe some day we will do something with the value...
-           $blCf .= " .2";
-       }
-       else
-       {
-           die "Handle_Bl: unexpected argument: <$_>\n";
-       }
-    }
-
-    if ($blCf ne "")
-    {
-       print $blCf;            # The \n below used to be here...
-       $blCf = "";
-    }
-
-    print "\n";
-    $wc = 0;
-}
-
-sub Handle_Comment
-{
-    # print STDERR "In Handle_Comment\n";
-    while ($_ = shift @words)
-    {
-    }
-
-    $noNl = 1;                                 # No newline needed.
-}
-
-sub Handle_It
-{
-    # .It Li "sntp ntpserver.somewhere"
-
-    # print STDERR "Handle_It: looking at <", join(" ",@words), ">\n";     # XXX
-    # die "Handle_It: \$wc was $wc, not 0.\n" if ($wc);
-
-    print "\@item";
-    if ($blIt ne "")
-    {
-       print " $blIt";
-       # Assert @words is empty?
-    }
-    else
-    {
-       do
-       {
-           # print STDERR "Handle_It: looking at <", join(" ",@words), ">\n";     # XXX
-           parseMacro();
-       } while scalar(@words);
-    }
-}
-
-sub Handle_D
-{
-    # .D1 Fl abcdefg   <tt>-abcdefg<>          # 1 line of indented text
-    # .Dl % ls \-l /etc        <tt>% ls -l /etc<tt>    # 1 indented literal text line
-
-    if (/^D1$/)
-    {
-       print "\@example\n";
-       $wc = 0;
-       parseMacro();
-       print "\n\@end example";
-    }
-    elsif (/^Dl$/)
-    {
-       print "\@example\n";
-       while ($_ = shift @words)
-       {
-           s/\\//;
-           print "$_ ";
-       }
-       print "\n\@end example";
-    }
-    else
-    {
-       die "Handle_D(): Unexpected mode: <$_>\n";
-    }
-}
-
-sub Handle_Ed
-{
-    print $bdEd;
-}
-
-sub Handle_El
-{
-    print $blEl;
-
-    $blIt = shift @blIt;
-    $blEl = shift @blEl;
-}
-
-sub Handle_Em
-{
-    # Usage: .Em stuff
-    #   .Em word                <italic>word</italic>
-    #   .Em or Ap ing           <italic>or</italic>'ing
-    #
-
-    print '@emph{';
-    parseMacro();                      # XXX: Might we get a leading space?
-    print "}";
-
-    # On the assumption that the rest is punctuation...
-    while ($_ = shift @words)
-    {
-        print;
-    }
-}
-
-sub Handle_ArCmFlIcLi
-{
-    # .Ar wants an italic code font, texi uses @kbd for that.
-    # .Cm is .Fl but no '-'.
-    # Usage: .Fl <argument> ...
-    #
-    #   .Fl          -
-    #   .Fl cfv      -cfv
-    #   .Fl cfv .    -cfv.
-    #   .Cm cfv .    cfv.
-    #   .Fl s v t    -s -v -t
-    #   .Fl - ,      --,
-    #   .Fl xyz ) ,  -xyz),
-    #   .Fl |        - |
-    #   .Ic "do while {...}"    do while {...}
-    #  .Li M1 M2 ;  <tt>M1 M2<tt<tt>>;
-    #
-    my ($dash, $didOne, $font, $fontE);
-
-    $dash = (/^Fl$/) ? "-" : "";
-    $font = (/^Ar$/) ? "\@kbd{" : "\@code{";           # }
-    $fontE = '}';
-    $didOne = 0;
-
-    do {
-        if ($words[0] eq '|')
-        {
-            print " " if $didOne && $sm && !$ns;
-            print $font, $dash, $fontE, ' ' if ($dash ne "");
-            print "$words[0]";
-           $ns = 0;
-        }
-        elsif ($words[0] eq '-')
-        {
-            print " " if $didOne && $sm && !$ns;
-            print $font, $dash, $words[0], $fontE;
-           $ns = 0;
-        }
-        elsif ($words[0] =~ /^"/)
-        {
-            print " " if $didOne && $sm && !$ns;
-            print $font;
-            print $dash if ($dash ne "");       # Do we need this?
-            parseQuote(\@words);
-            print $words[0];
-            print $fontE;
-           $ns = 0;
-        }
-        elsif ($words[0] eq 'Ar')               # Argument
-        {
-            $font = '@kbd{';                    # } slanted tty
-        }
-        elsif ($words[0] eq 'Ic')               # Interactive/internal command
-        {
-            $font = '@code{';                  # }
-        }
-        elsif ($words[0] eq 'Xc')
-        {
-            $sm = 1;
-        }
-        elsif ($words[0] eq 'Xo')
-        {
-            $sm = 0;
-        }
-        elsif ($words[0] =~ /^[[:punct:]]$/)
-        {
-            print $words[0];
-        }
-        else            # Should be empty or a word
-        {
-            print " " if $didOne && $sm && !$ns;
-            print $font;
-            print $dash if ($dash ne "");       # Do we need this?
-           $words[0] =~ s/\\&//;
-            print $words[0];
-            print $fontE;
-           $ns = 0;
-        }
-        shift @words;
-        $didOne = 1;
-    } while (scalar(@words) && $words[0] ne "Op");
-}
-
-sub Handle_Fn
-{
-    # Usage: .Fn <function> [<parameter>] ...
-    #   .Fn getchar             <code>getchar</code>()
-    #   .Fn strlen ) ,          <code>strlen</code>()),
-    #   .Fn align "char *pt" ,  <code>align</code(<slant>char *pt<slant>),
-    #
-    my ($didArg, $isOpen);
-
-    print '@code{', $words[0], "}(";
-    $isOpen = 1;
-    shift;
-
-    $didArg = 0;
-    while ($_ = shift @words)
-    {
-        if ($words[0] =~ /^"/) {
-            # assert $isOpen == 1
-            if ($didArg)
-           {
-                print '@code{,}', (($sm) ? ' ' : '');  # Ignore $ns here
-           }
-            parseQuote(\@words);
-            print '@emph{', $words[0], "}";
-            $didArg = 1;
-           $ns = 0;
-        } else {
-            print ")" if ($isOpen);
-            $isOpen = 0;
-            print $words[0];
-        }
-    }
-}
-
-sub Handle_Nm
-{
-    # Usage: .Nm [<argument>] ...
-    #
-    #   .Nm groff_mdoc  groff_mdoc
-    #   .Nm \-mdoc      -mdoc
-    #   .Nm foo ) ) ,   foo)),
-    #   .Nm :           groff_mdoc:
-    #
-    if (!defined $progName)
-    {
-        if (defined $ENV{AG_DEF_PROG_NAME})
-        {
-            $progName = $ENV{AG_DEF_PROG_NAME};
-        }
-        else
-        {
-            $progName = "XXX Program Name";
-        }
-    }
-
-    if ($words[0] =~ /^[\\\w]/)
-    {
-        $progName = shift @words;
-    }
-    print '@code{', $progName, '}';
-
-    # Anything after this should be punctuation
-
-    while ($_ = shift @words)
-    {
-        print;
-    }
-}
-
-sub Handle_Ns
-{
-    # Usage: .Pf ...
-    #   .Pa ntpkey_cert_ Ns Ar hostname
-    #
-    # Suppress whitespace between "here" and the first parameter
-
-    $wc = 0;           # This might be ok...
-}
-
-sub Handle_Op
-{
-    # Usage: .Op [<option>] ...
-    #   .Op                                     []
-    #   .Op Fl k                                [-k]
-    #   .Op Fl k ) .                            [-k]).
-    #   .Op Fl c Ar objfil Op Ar corfil ,       [ -c objfil [corfil]],
-    #   .Op word1 word2                         [word1 word2]
-    #
-    # If we decide to support Oo and Oc this almost becomes recursive,
-    # but we can handle that with separate Handle_Oo and Handle_Oc
-    # routines.
-
-    my ($op);
-
-    print '[';
-    $op = 1;
-    do {
-       # print STDERR "Handle_Op: looking at <$words[0]>\n";
-        if ($op && $words[0] =~ /^(Ar|Cm|Fl|Ic)$/)
-        {
-                $_ = shift @words;
-                Handle_ArCmFlIcLi();
-        }
-        elsif ($words[0] =~ /^[[:punct:]]$/)
-        {
-                print ']' if ($op);
-                $op = 0;
-                print shift @words;
-        }
-        else
-        {
-                print shift @words;
-        }
-    } while (@words > 0);
-    print ']' if ($op);
-}
-
-sub Handle_Pa
-{
-    # Usage: .Pa [<pathname>] ...
-    #   .Pa                     ~
-    #   .Pa /usr/share          /usr/share
-    #   .Pa /tmp/fooXXXXX ) .   /tmp/fooXXXXX).
-    #
-    my ($pa_path);
-    if (@words == 0)
-    {
-        $pa_path = "~";
-    }
-    else
-    {
-        $pa_path = shift @words;
-    }
-
-    print '@file{',"$pa_path","}";
-}
-
-sub Handle_Pf
-{
-    # Usage: .Pf ...
-    #   .Pf ( Fa name2         (<slant>name2
-    #
-    # Suppress whitespace between the first and 2nd argument.
-
-    die "Handle_Pf: not done yet\n";
-}
-
-sub Handle_Q
-{
-    # Usage: .Ql ...
-    #   .Aq ...                 Angle bracket: <...>
-    #   .Bq ...                 bracket: [...]
-    #   .Brq ...                braces: {...}
-    #   .Dq ...                 double quote: <lq><lq>...<rq><rq>
-    #   .Eq XX YY ...           Enclose String: XX...YY
-    #   .Pq XX ...              parenthesis: (...)
-    #   .Ql ...                 Quoted literal: <lq>...<rq> or <tt>...<tt>
-    #   .Qq ...                 Straight 2ble quote: "..."
-    #   .Sq ...                 Single quote: <lq>...<rq>
-    #
-
-    my ($lq, $rq);
-    $wc = 0;
-
-    # print STDERR "Handle_Q: <", join(' ', @words), ">\n";     # XXX
-
-    if    (/^Aq$/)      { $lq = "<"; $rq = ">"; }
-    elsif (/^Bq$/)      { $lq = "["; $rq = "]"; }
-    elsif (/^Brq$/)     { $lq = "{"; $rq = "}"; }
-    elsif (/^Dq$/)      { $lq = '@quotedblleft{}'; $rq = '@quotedblright{}'; }
-    elsif (/^Eq$/)      { $lq = shift @words; $rq = shift @words; }
-    elsif (/^Pq$/)      { $lq = "("; $rq = ")"; }
-    elsif (/^Ql$/)      { $lq = '@quoteleft{}'; $rq = '@quoteright{}'; }
-    elsif (/^Qq$/)      { $lq = '"'; $rq = '"'; }
-    elsif (/^Sq$/)      { $lq = '@quoteleft{}'; $rq = '@quoteright{}'; }
-
-    print "$lq";
-
-    do {
-       parseMacro();
-    } while (@words > 0 && $words[0] !~ /^[[:punct:]]$/);
-
-    print "$rq";
-    # The assumption is the rest are punctuation.
-    while ($_ = shift @words)
-    {
-        print;
-    }
-}
-
-sub Handle_Ref
-{
-    # Usage:
-    #  .Rs     Starts a reference.  No arguments.  Collects info.
-    #          Causes a line break in the SEE ALSO section.  Yeah.
-    #  .Re     Ends a reference.  No arguments.  Emits collected data:
-    #  .%A     Reference author name; one name per invocation.
-    #  .%B     Book title.
-    #  .%C     City/Place (not implemented yet).
-    #  .%D     Date.
-    #  .%I     Issuer/publisher name.
-    #  .%J     Journal name.
-    #  .%N     Issue number.
-    #  .%O     Optional information.
-    #  .%P     Page Number.
-    #  .%Q     Corporate or foreign author.
-    #  .%R     Report name.
-    #  .%T     Title of article. Italic.
-    #  .%U     Optional hypertext reference.
-    #  .%V     Volume
-    #
-    # Collecting during Rs and emitting during Re would make it easy
-    # to be pretty about multiple authors, journals, etc.
-    # 
-    # Remember to:
-    #  $noNl = 1;                              # No newline needed.
-    # where appropriate.
-
-    if (/^Rs$/)
-    {
-       die "Cannot nest .Rs directives.\n" if ($ref);
-       ++$ref;
-
-       # Assert no args?
-       # Initialize.
-       # Assert $refCount is 0?
-       $refCount = 0;
-
-       print "\@*\n";
-        $extArg = 1;                   # HMS: give it a try...
-       $wc = 0;
-    }
-    elsif (/^Re$/)
-    {
-       --$ref;
-       die ".Re seen without a .Rs directive.\n" if ($ref);
-
-       # Assert no args?
-
-       print ".";
-
-       # Cleanup.
-        $extArg = 0;                   # HMS: give it a try...
-       # Initialize.
-       $refCount = 0;
-    }
-    elsif (/^%A$/)
-    {
-       print ", " if ($refCount++);
-       parseMacro();
-    }
-    elsif (/^%O$/)
-    {
-       print ", " if ($refCount++);
-       parseMacro();
-    }
-    elsif (/^%T$/)
-    {
-       print ", " if ($refCount++);
-
-       # Use @emph{} for italics.
-       $wc = 0;
-       Handle_Em();
-    }
-    else
-    {
-       die "Handle_Ref: Unknown/unimplemented command in .Rs/.Rs block <$_>\n";
-    }
-}
-
-sub Handle_Sec
-{
-    # Usage: .Sh
-    # Usage: .Ss
-    #   .Sh word(s)
-    #
-    # Might be a quoted string.
-    #
-    # Drops an anchor.
-    my ($a, $sh);
-
-    $sh =~ /Sh/;
-
-    parseQuote(\@words) if ($words[0] =~ /^"/);
-
-    while ($_ = shift @words)
-    {
-       $a .= " " if ($a ne "");
-       $a .= $_;
-    }
-
-    print '@node ', "$a\n";
-    print '@', ($sh ? "sub" : ""), "section $a\n";
-    print "@anchor{$a}\n";
-    $wc = 0;
-}
-
-sub Handle_Sm
-{
-    # Usage: Sm [ off | on ]
-
-    if (scalar(@words))
-    {
-       if ($words[0] eq 'off')
-       {
-               $sm = 0;
-       }
-       elsif ($words[0] eq 'on')
-       {
-               $sm = 1;
-       }
-       else
-       {
-               die "Handle_Sm: Unexpected argument to Sm: <$words[0]>\n";
-       }
-       shift @words;
-    }
-    else
-    {
-       $sm = !$sm;
-    }
-}
-
-sub Handle_Sx
-{
-    # Usage: .Sx <section reference> ...
-    #   .Sh word(s)
-    #
-    # Might be a quoted string.
-    #
-    # References an anchor
-
-    my ($a);
-
-    parseQuote(\@words) if ($words[0] =~ /^"/);
-
-    while ($_ = shift @words)
-    {
-       $a .= " " if ($a ne "");
-       $a .= $_;
-       last if ($words[0] =~ /^[[:punct:]]$/);
-    }
-
-    print '@ref{',"$a","}";
-}
-
-sub Handle_Ta
-{
-    # Usage: .Ta
-    #   .Ta
-    #
-    # multitable column separator
-
-    print '@tab';
-}
-
-sub Handle_Ux
-{
-    # Usage: .Ux ...
-    #   .Ux                     UNIX
-    #   .Ux FOO                 FOO
-    #
-    my ($ux_name);
-    if (@words == 0)
-    {
-        $ux_name = "UNIX";
-    }
-    else
-    {
-        $ux_name = shift @words;
-    }
-
-    print '@sc{',"$ux_name","}";
-    while ($_ = shift @words)
-    {
-        print;
-    }
-}
-
-sub Handle_Xr
-{
-    # Usage: .Xr <man page name> [<section>] ...
-    #   .Xr mdoc        mdoc
-    #   .Xr mdoc ,      mdoc,
-    #   .Xr mdoc 7      mdoc(7)
-    #   .Xr xinit 1x ;  xinit(1x);
-    #
-    # Emitting things like @uref{/man.cgi/1/ls,,ls} would be OK,
-    # but we'd have to allow for changing /man.cgi/ (at least).
-    # I'm OK with:
-    #   @code{mdoc}
-    #   @code{mdoc},
-    #   @code{mdoc(7)}
-    #   @code{xinit(1x)};
-    #
-    my ($xr_cmd, $xr_sec, $xr_punc);
-    if (@words == 1)
-    {
-        $xr_cmd = $words[0];
-    }
-    elsif (@words == 2)
-    {
-        $xr_cmd = shift @words;
-        if ($words[0] =~ /[[:punct:]]/)
-        {
-            $xr_punc = shift @words;
-        }
-        else
-        {
-            $xr_sec = shift @words;
-        }
-    }
-    elsif (@words == 3)
-    {
-        $xr_cmd = shift @words;
-        $xr_sec = shift @words;
-        $xr_punc = shift @words;
-    }
-    else
-    {
-    }
-
-    # HMS: do we really want 'defined' in the following tests?
-    print '@code{',"$xr_cmd"    if (defined $xr_cmd);
-    print "($xr_sec)"           if (defined $xr_sec);
-    print "}"                   if (defined $xr_cmd);
-    print "$xr_punc"            if (defined $xr_punc);
-}
-
-sub parseQuote # ref to array of words
-{
-    my ($waref) = @_;   # word array reference
-    my ($string);
-
-    # print STDERR "parseQuote(): <$_", join(' ',@words), ">\n";
-    # Passing in "foo" will lose...
-
-    $string = shift @{$waref};
-
-    until ($string =~ /\"$/) {
-        $string .= " ".shift @{$waref};
-    }
-
-    $string =~ s/^\"(.*)\"$/$1/;
-
-    unshift @{$waref}, $string;
-}
-
-sub pSp
-{
-    print ' ' if $wantspace;
-}
-
-sub isPunct ($)
-{
-    my $string = shift;
-    my $rc;
-
-    $rc = ($string =~/^(\\&)?[[:punct:]]+$/) ? 1 : 0;
-    # print STDERR "isPunct($string): $rc\n";
-    return $rc;
-}
-
-sub parseMacro
-{
-    # print STDERR '@words = ', scalar(@words), ': ', join(' ', @words), "\n";
-
-    while ($_ = shift @words)
-    {
-       s/^\\&//;
-        $wantspace = (($wc++ && !isPunct($_) && $sm && !$ns) ? 1 : 0);
-
-        if    (/^\\"/)                 { Handle_Comment(); }
-        elsif (/^"/)                   { parseQuote(\@words); }
-        elsif (/^An$/)                 { pSp(); Handle_An(); }
-        elsif (/^Aq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Ar$/)                 { pSp(); Handle_ArCmFlIcLi(); }
-        elsif (/^Bd$/)                 { Handle_Bd(); }
-        elsif (/^Bl$/)                 { Handle_Bl(); }
-        elsif (/^Bq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Brq$/)                        { pSp(); Handle_Q(); }
-        elsif (/^Cm$/)                 { pSp(); Handle_ArCmFlIcLi(); }
-        elsif (/^D1$/)                 { Handle_D(); }
-        elsif (/^Dl$/)                 { Handle_D(); }
-        elsif (/^Dq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Ed$/)                 { Handle_Ed(); }
-        elsif (/^El$/)                 { Handle_El(); }
-        elsif (/^Em$/)                 { pSp(); Handle_Em(); }
-        elsif (/^Eq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Fl$/)                 { pSp(); Handle_ArCmFlIcLi(); }
-        elsif (/^Fn$/)                 { pSp(); Handle_Fn(); }
-        elsif (/^Ic$/)                 { pSp(); Handle_ArCmFlIcLi(); }
-        elsif (/^It$/)                 { Handle_It(); }
-        elsif (/^Li$/)                 { pSp(); Handle_ArCmFlIcLi(); }
-        elsif (/^Nm$/)                 { pSp(); Handle_Nm(); }
-        elsif (/^Ns$/)                 { Handle_Ns(); }
-        elsif (/^Op$/)                 { pSp(); Handle_Op(); }
-        elsif (/^Pa$/)                 { pSp(); Handle_Pa(); }
-        elsif (/^Pf$/)                 { Handle_Pf(); }
-        elsif (/^Pp$/)                 { ; }   # @* ?
-        elsif (/^Pq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Ql$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Qq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Re$/)                 { Handle_Ref(); }       # EOReference
-        elsif (/^Rs$/)                 { Handle_Ref(); }       # BOReference
-        elsif (/^%A$/)                 { Handle_Ref(); }
-        elsif (/^%B$/)                 { Handle_Ref(); }
-        elsif (/^%C$/)                 { Handle_Ref(); }
-        elsif (/^%D$/)                 { Handle_Ref(); }
-        elsif (/^%I$/)                 { Handle_Ref(); }
-        elsif (/^%J$/)                 { Handle_Ref(); }
-        elsif (/^%N$/)                 { Handle_Ref(); }
-        elsif (/^%O$/)                 { Handle_Ref(); }
-        elsif (/^%P$/)                 { Handle_Ref(); }
-        elsif (/^%Q$/)                 { Handle_Ref(); }
-        elsif (/^%R$/)                 { Handle_Ref(); }
-        elsif (/^%T$/)                 { Handle_Ref(); }
-        elsif (/^%U$/)                 { Handle_Ref(); }
-        elsif (/^%V$/)                 { Handle_Ref(); }
-        elsif (/^Sh$/)                 { Handle_Sec(); }       # Sec Header
-        elsif (/^Sm$/)                 { Handle_Sm(); }
-        elsif (/^Sq$/)                 { pSp(); Handle_Q(); }
-        elsif (/^Ss$/)                 { Handle_Sec(); }       # Sub Section
-        elsif (/^Sx$/)                 { pSp(); Handle_Sx(); } # Section xref
-        elsif (/^Ta$/)                 { pSp(); Handle_Ta(); } # pSP()?
-        elsif (/^Ux$/)                 { pSp(); Handle_Ux(); }
-        elsif (/^Xc$/)                 { $extArg = 0; }
-        elsif (/^Xo$/)                 { $extArg = 1; }
-        elsif (/^Xr$/)                 { pSp(); Handle_Xr(); }
-        else                            { pSp(); print; $ns = 0; }
-    }
-    $wc = 0;
-
-}
+use warnings;
+use File::Basename qw(dirname);
+use lib dirname(__FILE__);
+use Mdoc qw(ns pp hs mapwords gen_encloser nl);
+
+# Ignore commments
+Mdoc::def_macro( '.\"',  sub { () } );
+
+# Enclosers
+Mdoc::def_macro( '.An',  sub { @_, ns, '@*' } );
+Mdoc::def_macro( '.Aq',  gen_encloser(qw(< >)),   greedy => 1);
+Mdoc::def_macro( '.Bq',  gen_encloser(qw([ ])),   greedy => 1);
+Mdoc::def_macro( '.Brq', gen_encloser(qw(@{ @})), greedy => 1);
+Mdoc::def_macro( '.Pq',  gen_encloser(qw/( )/),   greedy => 1);
+Mdoc::def_macro( '.Qq',  gen_encloser(qw(" ")),   greedy => 1);
+Mdoc::def_macro( '.Op',  gen_encloser(qw(@code{[ ]})), greedy => 1);
+Mdoc::def_macro( '.Ql',  gen_encloser(qw(@quoteleft{} @quoteright{})),
+    greedy => 1);
+Mdoc::def_macro( '.Sq',  gen_encloser(qw(@quoteleft{} @quoteright{})),
+    greedy => 1);
+Mdoc::def_macro( '.Dq',  gen_encloser(qw(@quotedblleft{} @quotedblright{})), 
+    greedy => 1);
+Mdoc::def_macro( '.Eq', sub { 
+        my ($o, $c) = (shift, pop); 
+        gen_encloser($o, $c)->(@_) 
+},  greedy => 1);
+Mdoc::def_macro( '.D1', sub { "\@example\n", ns, @_, ns, "\n\@end example" },
+    greedy => 1);
+Mdoc::def_macro( '.Dl', sub { "\@example\n", ns, @_, ns, "\n\@end example" },
+    greedy => 1);
+
+Mdoc::def_macro( '.Oo',  gen_encloser(qw(@code{[ ]})), concat_until => '.Oc');
+Mdoc::def_macro( 'Oo',   sub { '@code{[', ns, @_ } );
+Mdoc::def_macro( 'Oc',   sub { @_, ns, pp(']}') } );
+
+Mdoc::def_macro( '.Bro', gen_encloser(qw(@code{@{ @}})), concat_until => '.Brc');
+Mdoc::def_macro( 'Bro',  sub { '@code{@{', ns, @_ } );
+Mdoc::def_macro( 'Brc',  sub { @_, ns, pp('@}}') } );
+
+Mdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc');
+Mdoc::def_macro( 'Po',   sub { '(', @_     } );
+Mdoc::def_macro( 'Pc',   sub { @_, ')' } );
+
+Mdoc::def_macro( '.Ar', sub { mapwords {"\@kbd{$_}"} @_ } );
+Mdoc::def_macro( '.Fl', sub { mapwords {"\@code{-$_}"} @_ } );
+Mdoc::def_macro( '.Cm', sub { mapwords {"\@code{-$_}"} @_ } );
+Mdoc::def_macro( '.Ic', sub { mapwords {"\@code{$_}"} @_ } );
+Mdoc::def_macro( '.Cm', sub { mapwords {"\@code{$_}"} @_ } );
+Mdoc::def_macro( '.Li', sub { mapwords {"\@code{$_}"} @_ } );
+Mdoc::def_macro( '.Va', sub { mapwords {"\@code{$_}"} @_ } );
+Mdoc::def_macro( '.Em', sub { mapwords {"\@emph{$_}"} @_ } );
+Mdoc::def_macro( '.Fn', sub { '@code{'.(shift).'()}' } );
+Mdoc::def_macro( '.Ss', sub { "\@subsubsection", hs, @_ });
+Mdoc::def_macro( '.Sh', sub { 
+        my $name = "@_"; 
+        "\@node", hs, "$name\n", ns, "\@subsection", hs, $name
+    });
+Mdoc::def_macro( '.Ss', sub { "\@subsubsection", hs, @_ });
+Mdoc::def_macro( '.Xr', sub { '@code{'.(shift).'('.(shift).')}', @_ } );
+Mdoc::def_macro( '.Sx', gen_encloser(qw(@ref{ })) );
+Mdoc::def_macro( '.Ux', sub { '@sc{unix}', @_ } );
+Mdoc::def_macro( '.Fx', sub { '@sc{freebsd}', @_ } );
+{
+    my $name;
+    Mdoc::def_macro('.Nm', sub {
+        $name = shift || $ENV{AG_DEF_PROG_NAME} || 'XXX' if (!$name);
+        "\@code{$name}"
+    } );
+}
+Mdoc::def_macro( '.Pa', sub { mapwords {"\@file{$_}"} @_ } );
+Mdoc::def_macro( '.Pp', sub { '' } );
+
+# Setup references
+
+Mdoc::def_macro( '.Rs', sub { "\@*\n", @_ } );
+Mdoc::set_Re_callback(sub {
+        my ($reference) = @_;
+        "@*\n", ns, $reference->{authors}, ',', "\@emph{$reference->{title}}",
+        ',', $reference->{optional}
+    });
+
+# Set up Bd/Ed
+
+my %displays = (
+    literal => [ '@verbatim', '@end verbatim' ],
+);
+
+Mdoc::def_macro( '.Bd', sub {
+        (my $type = shift) =~ s/^-//;
+        die "Not supported display type <$type>" 
+            if not exists $displays{ $type };
+
+        my $orig_ed = Mdoc::get_macro('.Ed');
+        Mdoc::def_macro('.Ed', sub {
+                Mdoc::def_macro('.Ed', delete $orig_ed->{run}, %$orig_ed);
+                $displays{ $type }[1];
+            });
+        $displays{ $type }[0]
+    });
+Mdoc::def_macro('.Ed', sub { die '.Ed used but .Bd was not seen' });
+
+# Set up Bl/El
+
+my %lists = (
+    bullet => [ '@itemize @bullet', '@end itemize' ],
+    tag    => [ '@table @asis', '@end table' ],
+);
+
+Mdoc::set_Bl_callback(sub {
+        my $type = shift;
+        die "Specify a list type"             if not defined $type;
+        $type =~ s/^-//;
+        die "Not supported list type <$type>" if not exists $lists{ $type };
+        Mdoc::set_El_callback(sub { $lists{ $type }[1] });
+        $lists{ $type }[0]
+    });
+Mdoc::def_macro('.It', sub { '@item', hs, @_ });
+
+for (qw(Aq Bq Brq Pq Qq Ql Sq Dq Eq Ar Fl Ic Pa Op Cm Li Fx Ux Va)) {
+    my $m = Mdoc::get_macro(".$_");
+    Mdoc::def_macro($_, delete $m->{run}, %$m);
+}
+
+sub print_line {
+    my $s = shift;
+    $s =~ s/\\&//g;
+    print "$s\n";
+}
+
+sub preprocess_args {
+    $_ =~ s/([{}])/\@$1/g for @_;
+}
+
+sub run {
+    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line, 
+            \&preprocess_args)
+    ) {
+        my @ret = Mdoc::call_macro($macro, @args);
+        if (@ret) {
+            my $s = Mdoc::to_string(@ret);
+            print_line($s);
+        }
+    }
+    return 0;
+}
+
+exit run(@ARGV) unless caller;
diff --git a/sntp/ag-tpl/perlopt.tpl b/sntp/ag-tpl/perlopt.tpl
new file mode 100644 (file)
index 0000000..476e4c3
--- /dev/null
@@ -0,0 +1,176 @@
+[= AutoGen5 template foo=(base-name) =]
+[= 
+  ;;(use-modules (ice-9 regex))
+  ;;(define script-name (regexp-substitute/global #f "(-opts)" (base-name) 'pre ""))
+  ;;(shellf "cat %s" script-name) 
+=]
+# DO NOT EDIT THE FOLLOWING
+#
+# It's auto generated option handling code [=
+(if (not (and (exist? "prog-name") (exist? "prog-title")))
+    (error "prog-name and prog-title are required"))
+(define prog-name (get "prog-name"))
+(if (> (string-length prog-name) 16)
+    (error (sprintf "prog-name limited to 16 characters:  %s"
+           prog-name)) ) 
+(if (not (exist? "long-opts"))
+    (error "long-opts is required"))
+
+;; perl list containing string to initialize the option hash
+(define perl_opts "")
+;; perl list containing option definitions for Getopt::Long
+(define perl_defs "       ")
+;; usage string
+(define perl_usage "") 
+
+(define optname-from "A-Z_^")
+(define optname-to   "a-z--")
+(define counter 0)
+
+(define q (lambda (s) (string-append "'" s "'")))
+(define qp (lambda (s) (string-append "q{" s "}")))
+
+=] [= FOR flag =][=
+
+(define optarg "")      ;; the option argument for Getopt::Long
+(define opttarget "''") ;; the value of a hash key that represents option
+(define optargname "")
+(define optisarray #f)
+(define optname (string-tr! (get "name") optname-from optname-to))
+
+=][=
+;; since autoopts doesn't support float we take the combination arg-name =
+;; float and arg-type = string as float
+=][=
+IF arg-type =][= 
+    CASE arg-type =][= 
+    =* num =][= (set! optarg "=i") =][= 
+    =* str =][= 
+        (if (and (exist? "arg-name") (== (get "arg-name") "float"))
+            (set! optarg "=f")
+            (set! optarg "=s") 
+        )
+    =][= 
+    * =][= 
+        (error (string-append "unknown arg type '" 
+        (get "arg-type") "' for " (get "name"))) =][= 
+        ESAC arg-type =][= 
+ENDIF =][=
+
+(if (exist? "stack-arg") 
+    ;; set optarget to array reference if can take more than one value
+    (if (and (exist? "max") (== (get "max") "NOLIMIT"))
+        (begin 
+          (set! opttarget (string-append 
+            "[" 
+            (if (exist? "arg-default") (q (get "arg-default")) "")
+            "]"
+            )
+          )
+          (set! optisarray #t)
+        )
+        (error "If stack-arg then max has to be NOLIMIT")
+    )
+    ;; just scalar otherwise
+    (if (exist? "arg-default") (set! opttarget (q (get "arg-default"))))
+)
+
+(set! perl_opts (string-append 
+                  perl_opts "'" (get "name") "' => " opttarget ",\n        "))
+
+(define def_add (string-append "'" optname (if (exist? "value") 
+                  (string-append "|" (get "value")) "") optarg "',"))
+
+(define add_len (+ (string-length def_add) counter))
+(if (> add_len 80) 
+    (begin 
+      (set! perl_defs (string-append perl_defs "\n        " def_add))
+      (set! counter 8)
+    )
+    (begin 
+      (set! perl_defs (string-append perl_defs " " def_add))
+      (set! counter (+ counter add_len))
+    )
+)
+
+(if (exist? "arg-type") 
+    (if (and (exist? "arg-name") (== (get "arg-name") "float"))
+        (set! optargname "=float")
+        (set! optargname (string-append "=" (substring (get "arg-type") 0 3)))
+    )
+    (set! optargname "  ")
+)
+
+(if (not (exist? "deprecated"))
+(set! perl_usage (string-append perl_usage 
+                    (sprintf "\n    %-28s %s" 
+                             (string-append (if (exist? "value") (string-append "-" (get "value")) "  ") ", --" (get "name") optargname)
+                             (get "descrip")))))
+(if optisarray 
+  (set! perl_usage (string-append perl_usage 
+        "\n                                   - may appear multiple times"))
+)
+
+=][= ENDFOR =]
+
+use Getopt::Long qw(GetOptionsFromArray);
+Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
+
+my $usage;
+
+sub usage {
+    my ($ret) = @_;
+    print STDERR $usage;
+    exit $ret;
+}
+
+sub paged_usage {
+    my ($ret) = @_;
+    my $pager = $ENV{PAGER} || '(less || more)';
+
+    open STDOUT, "| $pager" or die "Can't fork a pager: $!";
+    print $usage;
+
+    exit $ret;
+}
+
+sub processOptions {
+    my $args = shift;
+
+    my $opts = {
+        [= (emit perl_opts) =]'help' => '', 'more-help' => ''
+    };
+    my $argument = '[= argument =]';
+    my $ret = GetOptionsFromArray($args, $opts, (
+[= (emit perl_defs) =]
+        'help|?', 'more-help'));
+
+    $usage = <<'USAGE';
+[= prog-name =] - [= prog-title =]
+USAGE: [= prog-name =] [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [= argument =]
+[= (emit perl_usage ) =]
+    -?, --help                   Display usage information and exit
+      , --more-help              Pass the extended usage information through a pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+USAGE
+
+    usage(0)       if $opts->{'help'};
+    paged_usage(0) if $opts->{'more-help'};[= 
+    IF (exist? "argument") =]
+
+    if ($argument && $argument =~ /^[^\[]/ && !@$args) {
+        print STDERR "Not enough arguments supplied (See --help/-?)\n";
+        exit 1;
+    }[= 
+       ENDIF 
+    =]
+    $_[0] = $opts;
+    return $ret;
+}
+
+END { close STDOUT };
+[= 
+;;(shellf "mv %s.new %s" (base-name) script-name ) 
+=]
index 1fd0c1e2c4e2c37508a834674f5adc684600a2b0..3b48c8b2d488565296bd0f49eadf43417bcc86e1 100644 (file)
@@ -1,26 +1,35 @@
 dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
 dnl
-dnl This macro defines (with AC_DEFINE) VARNAME to the expansion of the DIR
-dnl variable, taking care of fixing up ${prefix} and such.
+dnl This macro sets VARNAME to the expansion of the DIR variable,
+dnl taking care of fixing up ${prefix} and such.
 dnl
-dnl Note that the 3 argument form is only supported with autoconf 2.13 and
-dnl later (i.e. only where AC_DEFINE supports 3 arguments).
+dnl VARNAME is then offered as both an output variable and a C
+dnl preprocessor symbol.
 dnl
-dnl Examples:
+dnl Example:
 dnl
-dnl    AC_DEFINE_DIR(DATADIR, datadir)
-dnl    AC_DEFINE_DIR(PROG_PATH, bindir, [Location of installed binaries])
+dnl    AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.])
 dnl
-dnl @version $Id: acinclude.m4,v 1.3 2000/08/04 03:26:22 stenn Exp $
-dnl @author Alexandre Oliva <oliva@lsd.ic.unicamp.br>
+dnl @category Misc
+dnl @author Stepan Kasal <kasal@ucw.cz>
+dnl @author Andreas Schwab <schwab@suse.de>
+dnl @author Guido U. Draheim <guidod@gmx.de>
+dnl @author Alexandre Oliva
+dnl @version 2006-10-13
+dnl @license AllPermissive
 
 AC_DEFUN([AC_DEFINE_DIR], [
-       ac_expanded=`(
-           test "x$prefix" = xNONE && prefix="$ac_default_prefix"
-           test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
-           eval echo \""[$]$2"\"
-       )`
-       ifelse([$3], [],
-         AC_DEFINE_UNQUOTED([$1], ["$ac_expanded"]),
-         AC_DEFINE_UNQUOTED([$1], ["$ac_expanded"], [$3]))
+  prefix_NONE=
+  exec_prefix_NONE=
+  test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
+  test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
+dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn
+dnl refers to ${prefix}.  Thus we have to use `eval' twice.
+  eval ac_define_dir="\"[$]$2\""
+  eval ac_define_dir="\"$ac_define_dir\""
+  AC_SUBST($1, "$ac_define_dir")
+  AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3])
+  test "$prefix_NONE" && prefix=NONE
+  test "$exec_prefix_NONE" && exec_prefix=NONE
 ])
+
index 4843ba41accd2865c1626f374068a97d5a927fad..f9942907a5bc581224647817ea05514835944d1a 100644 (file)
@@ -105,7 +105,7 @@ $(srcdir)/invoke-ntp-keygen.menu: $(srcdir)/invoke-ntp-keygen.texi
 
 $(srcdir)/invoke-ntp-keygen.texi: $(srcdir)/ntp-keygen-opts.def $(std_def_list)
        $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntp-keygen-opts.def
-       $(top_srcdir)/scripts/check--help $@
+       $(top_srcdir)/scripts/build/check--help $@
 
 $(srcdir)/ntp-keygen.html: $(srcdir)/ntp-keygen.texi $(top_srcdir)/sntp/include/version.texi
        cd $(srcdir) && ( makeinfo --force --html --no-split -o ntp-keygen.html ntp-keygen.texi || true )
@@ -123,7 +123,7 @@ $(top_srcdir)/sntp/scm-rev:
        cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
 
 version.c: $(ntp_keygen_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev
-       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntp-keygen
+       env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntp-keygen
 
 version.o: version.c
        env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o