[SCM] The rsync repository. - branch master updated
Rsync CVS commit messages
rsync-cvs at lists.samba.org
Tue Jun 9 04:07:22 UTC 2020
The branch, master has been updated
via 53fae556 Change man page src format from yodl to markdown.
from bd66a92e Tweak the new heading
https://git.samba.org/?p=rsync.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 53fae556521d035544ed8f0e968451ce6c60e664
Author: Wayne Davison <wayne at opencoder.net>
Date: Mon Jun 8 19:21:42 2020 -0700
Change man page src format from yodl to markdown.
This removes the yodl dependency, which is sometimes hard to track down.
Instead, this uses a python3 script that leverages the cmarkgfm library
to turn the source file into html. Then, the script parses the html in
order to turn the tag stream into a nroff stream using a simple state
machine. While it's doing that it also implements one added format rule
that turns an ordinal list that starts at 0 into a description list
(since markdown doesn't have an easy description list idiom).
-----------------------------------------------------------------------
Summary of changes:
.github/workflows/ccpp.yml | 2 +-
.gitignore | 5 +-
Makefile.in | 34 +-
configure.ac | 12 -
maybe-make-man | 37 +
md2man | 314 ++++
packaging/nightly-rsync | 20 +-
packaging/release-rsync | 17 +-
rsync-ssl.1.md | 90 +
rsync-ssl.yo | 99 --
rsync.1.md | 3905 ++++++++++++++++++++++++++++++++++++++++++++
rsync.yo | 3722 -----------------------------------------
rsyncd.conf.5.md | 1126 +++++++++++++
rsyncd.conf.yo | 1063 ------------
tweak_manpage | 44 -
15 files changed, 5500 insertions(+), 4990 deletions(-)
create mode 100755 maybe-make-man
create mode 100755 md2man
create mode 100644 rsync-ssl.1.md
delete mode 100644 rsync-ssl.yo
create mode 100644 rsync.1.md
delete mode 100644 rsync.yo
create mode 100644 rsyncd.conf.5.md
delete mode 100644 rsyncd.conf.yo
delete mode 100755 tweak_manpage
Changeset truncated at 500 lines:
diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml
index a9e4a3f8..61ce2d8e 100644
--- a/.github/workflows/ccpp.yml
+++ b/.github/workflows/ccpp.yml
@@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout at v2
- name: prepare-packages
- run: sudo apt-get install fakeroot acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev yodl
+ run: sudo apt-get install fakeroot acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm
- name: prepare-source
run: ./prepare-source
- name: configure
diff --git a/.gitignore b/.gitignore
index de495172..8f289931 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,8 +15,9 @@ config.status
aclocal.m4
/proto.h
/proto.h-tstamp
-/*.1
-/*.5
+/rsync*.1
+/rsync*.5
+/rsync*.html
/autom4te*.cache
/confdefs.h
/conftest*
diff --git a/Makefile.in b/Makefile.in
index b45f3f47..e7f2f644 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -33,7 +33,8 @@ VERSION=@RSYNC_VERSION@
SIMD_x86_64=simd-checksum-x86_64.o lib/md5-asm-x86_64.o
-GENFILES=configure.sh aclocal.m4 config.h.in proto.h proto.h-tstamp rsync.1 rsync-ssl.1 rsyncd.conf.5
+GENFILES=configure.sh aclocal.m4 config.h.in proto.h proto.h-tstamp rsync.1 rsync.1.html \
+ rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
lib/pool_alloc.h
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
@@ -67,7 +68,7 @@ CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
@OBJ_RESTORE@
-all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_MAN@
+all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf man
install: all
-${MKDIR_P} ${DESTDIR}${bindir}
@@ -214,31 +215,16 @@ proto.h: proto.h-tstamp
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c config.h
awk -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c
-man: rsync.1 rsync-ssl.1 rsyncd.conf.5 man-copy
+man: rsync.1 rsync-ssl.1 rsyncd.conf.5
-man-copy:
- @for fn in rsync.1 rsync-ssl.1 rsyncd.conf.5; do \
- if test -f $$fn; then \
- : ; \
- elif test -f $(srcdir)/$$fn; then \
- echo "Copying srcdir $$fn" ; \
- cp -p $(srcdir)/$$fn . ; \
- else \
- echo "NOTE: $$fn cannot be created." ; \
- fi ; \
- done
-
-rsync.1: rsync.yo $(srcdir)/tweak_manpage
- yodl2man -o rsync.1 $(srcdir)/rsync.yo
- -$(srcdir)/tweak_manpage rsync.1
+rsync.1: rsync.1.md md2man latest-year.h Makefile
+ @$(srcdir)/maybe-make-man $(srcdir) rsync.1.md
-rsync-ssl.1: rsync-ssl.yo $(srcdir)/tweak_manpage
- yodl2man -o rsync-ssl.1 $(srcdir)/rsync-ssl.yo
- -$(srcdir)/tweak_manpage rsync-ssl.1
+rsync-ssl.1: rsync-ssl.1.md md2man latest-year.h Makefile
+ @$(srcdir)/maybe-make-man $(srcdir) rsync-ssl.1.md
-rsyncd.conf.5: rsyncd.conf.yo $(srcdir)/tweak_manpage
- yodl2man -o rsyncd.conf.5 $(srcdir)/rsyncd.conf.yo
- -$(srcdir)/tweak_manpage rsyncd.conf.5
+rsyncd.conf.5: rsyncd.conf.5.md md2man latest-year.h Makefile
+ @$(srcdir)/maybe-make-man $(srcdir) rsyncd.conf.5.md
clean: cleantests
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
diff --git a/configure.ac b/configure.ac
index c2771190..372399c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -135,13 +135,6 @@ else
fi
AC_DEFINE_UNQUOTED(RSYNC_RSH, "$RSYNC_RSH", [default -e command])
-AC_CHECK_PROG(HAVE_YODL2MAN, yodl2man, 1, 0)
-if test x$HAVE_YODL2MAN = x1; then
- MAKE_MAN=man
-else
- MAKE_MAN=man-copy
-fi
-
# Some programs on solaris are only found in /usr/xpg4/bin (or work better than others versions).
AC_PATH_PROG(SHELL_PATH, sh, /bin/sh, [/usr/xpg4/bin$PATH_SEPARATOR$PATH])
AC_PATH_PROG(FAKEROOT_PATH, fakeroot, /usr/bin/fakeroot, [/usr/xpg4/bin$PATH_SEPARATOR$PATH])
@@ -1203,8 +1196,3 @@ AC_OUTPUT
AC_MSG_RESULT()
AC_MSG_RESULT([ rsync ${RSYNC_VERSION} configuration successful])
AC_MSG_RESULT()
-if test x$HAVE_YODL2MAN != x1; then
- AC_MSG_RESULT([ Note that yodl2man was not found, so pre-existing manpage files will be])
- AC_MSG_RESULT([ used w/o change (if available) -- no .yo file changes will be used.])
- AC_MSG_RESULT()
-fi
diff --git a/maybe-make-man b/maybe-make-man
new file mode 100755
index 00000000..334c3934
--- /dev/null
+++ b/maybe-make-man
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+if [ x"$2" = x ]; then
+ echo "Usage: $0 SRC_DIR NAME.NUM.md" 1>&2
+ exit 1
+fi
+
+srcdir="$1"
+inname="$2"
+
+if [ ! -d "$srcdir" ]; then
+ echo "The specified SRC_DIR is not a directory: $srcdir" 1>&2
+ exit 1
+fi
+
+if [ ! x"$RSYNC_ALWAYS_BUILD" ]; then
+ # We test our smallest manpage just to see if the python setup works.
+ if ! "$srcdir/md2man" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
+ outname=`echo "$inname" | sed 's/\.md$//'`
+ if [ -f "$outname" ]; then
+ exit 0
+ elif [ -f "$srcdir/$outname" ]; then
+ echo "Copying $srcdir/$outname"
+ cp -p "$srcdir/$outname" .
+ exit 0
+ else
+ echo "ERROR: $outname cannot be created."
+ if [ -f "$HOME/build_farm/build_test.fns" ]; then
+ exit 0 # No exit errorno to avoid a build failure in the samba build farm
+ else
+ exit 1
+ fi
+ fi
+ fi
+fi
+
+"$srcdir/md2man" "$srcdir/$inname"
diff --git a/md2man b/md2man
new file mode 100755
index 00000000..9b3c7c4b
--- /dev/null
+++ b/md2man
@@ -0,0 +1,314 @@
+#!/usr/bin/python3
+
+# This script takes a manpage written in github-flavored markdown and turns it
+# into a html web page and a nroff man page. The input file must have the name
+# of the program and the section in the format: NAME.NUM.md. The output files
+# are written into the current directory named NAME.NUM.html and NAME.NUM. The
+# input format has one extra extension: if a numbered list starts at 0, it is
+# turned into a description list. The dl's dt tag is taken from the contents of
+# the first tag inside the li, which is usually a p tag or a code tag. The
+# cmarkgfm lib is used to transforms the input file into html. The html.parser
+# is used as a state machine that both tweaks the html and outputs the nroff
+# data based on the html tags.
+#
+# Copyright (C) 2020 Wayne Davison
+#
+# This program is freely redistributable.
+
+import sys, os, re, argparse, time
+from html.parser import HTMLParser
+
+CONSUMES_TXT = set('h1 h2 p li pre'.split())
+
+HTML_START = """\
+<html><head>
+<title>%s</title>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 40em;
+ margin: auto;
+ font-size: 1.2em;
+ font-family: 'Roboto', sans-serif;
+}
+blockquote pre code {
+ background: #eee;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+"""
+
+HTML_END = """\
+<div style="float: right"><p><i>%s</i></p></div>
+</body></html>
+"""
+
+MAN_START = r"""
+.TH "%s" "%s" "%s" "" ""
+""".lstrip()
+
+MAN_END = """\
+"""
+
+NORM_FONT = ('\1', r"\fP")
+BOLD_FONT = ('\2', r"\fB")
+ULIN_FONT = ('\3', r"\fI")
+
+env_subs = { }
+
+def main():
+ mtime = None
+
+ fi = re.match(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+)\.(?P<sect>\d+))\.md)$', args.mdfile)
+ if not fi:
+ die('Failed to parse NAME.NUM.md out of input file:', args.mdfile)
+ fi = argparse.Namespace(**fi.groupdict())
+ if not fi.srcdir:
+ fi.srcdir = './'
+
+ chk_files = 'latest-year.h Makefile'.split()
+ for fn in chk_files:
+ try:
+ st = os.lstat(fi.srcdir + fn)
+ except:
+ die('Failed to find', fi.srcdir + fn)
+ if not mtime:
+ mtime = st.st_mtime
+
+ with open(fi.srcdir + 'Makefile', 'r', encoding='utf-8') as fh:
+ for line in fh:
+ m = re.match(r'^(\w+)=(.+)', line)
+ if not m:
+ continue
+ var, val = (m[1], m[2])
+ while re.search(r'\$\{', val):
+ val = re.sub(r'\$\{(\w+)\}', lambda m: env_subs[m[1]], val)
+ env_subs[var] = val
+ if var == 'VERSION':
+ break
+
+ MarkdownToManPage(fi, mtime)
+
+
+class MarkdownToManPage(HTMLParser):
+ def __init__(self, fi, mtime):
+ HTMLParser.__init__(self, convert_charrefs=True)
+
+ self.man_fh = self.html_fh = None
+ self.state = argparse.Namespace(
+ list_state = [ ],
+ p_macro = ".P\n",
+ first_li_tag = False,
+ first_dd_tag = False,
+ dt_from = None,
+ in_pre = False,
+ txt = '',
+ )
+
+ self.date = time.strftime('%d %b %Y', time.localtime(mtime))
+
+ with open(fi.fn, 'r', encoding='utf-8') as fh:
+ txt = re.sub(r'@VERSION@', env_subs['VERSION'], fh.read())
+ txt = re.sub(r'@LIBDIR@', env_subs['libdir'], txt)
+ html = cmarkgfm.github_flavored_markdown_to_html(txt)
+ txt = None
+
+ if args.test:
+ self.html_fh = open(os.devnull, 'w', encoding='utf-8')
+ self.man_fh = self.html_fh
+ else:
+ self.html_fn = fi.name + '.html'
+ self.html_fh = open(self.html_fn, 'w', encoding='utf-8')
+ self.html_fh.write(HTML_START % fi.prog + '(' + fi.sect + ') man page')
+
+ self.man_fn = fi.name
+ self.man_fh = open(self.man_fn, 'w', encoding='utf-8')
+ self.man_fh.write(MAN_START % (fi.prog, fi.sect, self.date))
+
+ self.feed(html)
+
+ def __del__(self):
+ if args.test:
+ print("The test was successful.")
+ return
+
+ if self.html_fh:
+ self.html_fh.write(HTML_END % self.date)
+ self.html_fh.close()
+ print("Output HTML page: ", self.html_fn)
+
+ if self.man_fh:
+ self.man_fh.write(MAN_END)
+ self.man_fh.close()
+ print("Output man page: ", self.man_fn)
+
+ def handle_starttag(self, tag, attrs_list):
+ st = self.state
+ if args.debug:
+ print('START', tag, attrs_list, st)
+ if st.first_li_tag:
+ if st.list_state[-1] == 'dl':
+ st.dt_from = tag
+ if tag == 'p':
+ tag = 'dt'
+ else:
+ self.html_fh.write('<dt>')
+ st.first_li_tag = False
+ if tag == 'p':
+ if not st.first_dd_tag:
+ self.man_fh.write(st.p_macro)
+ elif tag == 'li':
+ st.first_li_tag = True
+ lstate = st.list_state[-1]
+ if lstate == 'dl':
+ return
+ if lstate == 'o':
+ self.man_fh.write(".IP o\n")
+ else:
+ self.man_fh.write(".IP " + str(lstate) + ".\n")
+ st.list_state[-1] += 1
+ elif tag == 'blockquote':
+ self.man_fh.write(".RS 4\n")
+ elif tag == 'pre':
+ st.in_pre = True
+ self.man_fh.write(st.p_macro + ".nf\n")
+ elif tag == 'code' and not st.in_pre:
+ st.txt += BOLD_FONT[0]
+ elif tag == 'strong' or tag == 'bold':
+ st.txt += BOLD_FONT[0]
+ elif tag == 'i' or tag == 'em':
+ st.txt += ULIN_FONT[0]
+ elif tag == 'ol':
+ start = 1
+ for var, val in attrs_list:
+ if var == 'start':
+ start = int(val) # We only support integers.
+ break
+ if st.list_state:
+ self.man_fh.write(".RS\n")
+ if start == 0:
+ tag = 'dl'
+ attrs_list = [ ]
+ st.list_state.append('dl')
+ else:
+ st.list_state.append(start)
+ self.man_fh.write(st.p_macro)
+ st.p_macro = ".IP\n"
+ elif tag == 'ul':
+ self.man_fh.write(st.p_macro)
+ if st.list_state:
+ self.man_fh.write(".RS\n")
+ st.p_macro = ".IP\n"
+ st.list_state.append('o')
+ outer_tag = '<' + tag
+ for var, val in attrs_list:
+ outer_tag += ' ' + var + '=' + safeText(val) + '"'
+ self.html_fh.write(outer_tag + '>')
+ st.first_dd_tag = False
+
+ def handle_endtag(self, tag):
+ st = self.state
+ if args.debug:
+ print(' END', tag, st)
+ if tag in CONSUMES_TXT or st.dt_from == tag:
+ txt = st.txt.strip()
+ st.txt = ''
+ else:
+ txt = None
+ add_to_txt = None
+ if tag == 'h1':
+ self.man_fh.write(st.p_macro + '.SH "' + manify(txt) + '"\n')
+ elif tag == 'p':
+ if st.dt_from == 'p':
+ tag = 'dt'
+ self.man_fh.write('.IP "' + manify(txt) + '"\n')
+ st.dt_from = None
+ else:
+ self.man_fh.write(manify(txt) + "\n")
+ elif tag == 'li':
+ if st.list_state[-1] == 'dl':
+ if st.first_li_tag:
+ die("Invalid 0. -> td translation")
+ tag = 'dd'
+ if txt != '':
+ self.man_fh.write(manify(txt) + "\n")
+ st.first_li_tag = False
+ elif tag == 'blockquote':
+ self.man_fh.write(".RE\n")
+ elif tag == 'pre':
+ st.in_pre = False
+ self.man_fh.write(manify(txt) + "\n.fi\n")
+ elif tag == 'code' and not st.in_pre:
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'strong' or tag == 'bold':
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'i' or tag == 'em':
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'ol' or tag == 'ul':
+ if st.list_state.pop() == 'dl':
+ tag = 'dl'
+ if st.list_state:
+ self.man_fh.write(".RE\n")
+ else:
+ st.p_macro = ".P\n"
+ st.first_dd_tag = False
+ self.html_fh.write('</' + tag + '>')
+ if add_to_txt:
+ if txt is None:
+ st.txt += add_to_txt
+ else:
+ txt += add_to_txt
+ if st.dt_from == tag:
+ self.man_fh.write('.IP "' + manify(txt) + '"\n')
+ self.html_fh.write('</dt><dd>')
+ st.first_dd_tag = True
+ st.dt_from = None
+ elif tag == 'dt':
+ self.html_fh.write('<dd>')
+ st.first_dd_tag = True
+
+ def handle_data(self, data):
+ st = self.state
+ if args.debug:
+ print(' DATA', [data], st)
+ self.html_fh.write(safeText(data))
+ st.txt += data
+
+
+def manify(txt):
+ return re.sub(r"^(['.])", r'\&\1', txt.replace('\\', '\\\\')
+ .replace(NORM_FONT[0], NORM_FONT[1])
+ .replace(BOLD_FONT[0], BOLD_FONT[1])
+ .replace(ULIN_FONT[0], ULIN_FONT[1]), flags=re.M)
+
+
+def safeText(txt):
+ return txt.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
+
+
+def warn(*msg):
+ print(*msg, file=sys.stderr)
+
+
+def die(*msg):
+ warn(*msg)
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Transform a NAME.NUM.md markdown file into a NAME.NUM.html web page & a NAME.NUM man page.', add_help=False)
+ parser.add_argument('--test', action='store_true', help='Test if we can parse the input w/o updating any files.')
+ parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing.')
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument('mdfile', help="The NAME.NUM.md file to parse.")
+ args = parser.parse_args()
+
+ try:
+ import cmarkgfm
+ except:
+ die("The cmarkgfm library is not available for python3.")
+
+ main()
diff --git a/packaging/nightly-rsync b/packaging/nightly-rsync
index eb747432..3ec55190 100755
--- a/packaging/nightly-rsync
+++ b/packaging/nightly-rsync
@@ -34,11 +34,12 @@ def main():
die("$dest does not exist")
if not os.path.isdir('.git'):
die("There is no .git dir in the current directory.")
- if not os.path.exists('rsyncd.conf.yo'):
+ if not os.path.exists('rsyncd.conf.5.md'):
die("There is no rsync checkout in the current directory.")
if args.make_tar:
check_git_state('master')
+ cmd_chk(['touch', 'latest-year.h'])
cmd_chk(['make', gen_target])
extra_files = get_extra_files()
@@ -73,20 +74,13 @@ def main():
--
The rsync repository.
More information about the rsync-cvs
mailing list