%s
is non-breaking in CSS
+ st.html_out.append(html.replace(NBR_SPACE[0], ' ').replace(NBR_DASH[0], '-'))
+ st.txt += txt
+
+
+ def add_targets(self, tag, txt, suf=None):
+ st = self.state
+ tag = '<' + tag + '>'
+ targets = CODE_BLOCK_RE.findall(txt)
+ if not targets:
+ targets = [ txt ]
+ tag_pos = 0
+ for txt in targets:
+ txt = txt2target(txt, st.opt_prefix)
+ if not txt:
+ continue
+ if suf:
+ txt += suf
+ if txt in st.created_hashtags:
+ for j in range(2, 1000):
+ chk = txt + '-' + str(j)
+ if chk not in st.created_hashtags:
+ print('Made link target unique:', chk)
+ txt = chk
+ break
+ if tag_pos == 0:
+ tag_pos -= 1
+ while st.html_out[tag_pos] != tag:
+ tag_pos -= 1
+ st.html_out[tag_pos] = tag[:-1] + ' id="' + txt + '">'
+ st.html_out.append('')
+ tag_pos -= 1 # take into account the append
+ else:
+ st.html_out[tag_pos] = '' + st.html_out[tag_pos]
+ st.created_hashtags.add(txt)
+ st.latest_targets = targets
+
+
+ def output_debug(self, event, extra):
+ import pprint
+ st = self.state
+ if args.debug < 2:
+ st = argparse.Namespace(**vars(st))
+ if len(st.html_out) > 2:
+ st.html_out = ['...'] + st.html_out[-2:]
+ if len(st.man_out) > 2:
+ st.man_out = ['...'] + st.man_out[-2:]
+ print(event, extra)
+ pprint.PrettyPrinter(indent=2).pprint(vars(st))
+
+
+def txt2target(txt, opt_prefix):
+ txt = txt.strip().rstrip(':')
+ m = CODE_BLOCK_RE.search(txt)
+ if m:
+ txt = m.group(1)
+ txt = NBR_DASH_RE.sub('-', txt)
+ txt = BIN_CHARS_RE.sub('', txt)
+ txt = INVALID_TARGET_CHARS_RE.sub('_', txt)
+ if opt_prefix and txt.startswith('-'):
+ txt = opt_prefix + txt
+ else:
+ txt = INVALID_START_CHAR_RE.sub(r't\1', txt)
+ return txt
+
+
+def manify(txt):
+ return MANIFY_LINESTART_RE.sub(r'\&\1', txt.replace('\\', '\\\\')
+ .replace(NBR_SPACE[0], NBR_SPACE[1])
+ .replace(NBR_DASH[0], NBR_DASH[1])
+ .replace(NORM_FONT[0], NORM_FONT[1])
+ .replace(BOLD_FONT[0], BOLD_FONT[1])
+ .replace(UNDR_FONT[0], UNDR_FONT[1]))
+
+
+def htmlify(txt):
+ return txt.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
+
+
+def warn(*msg):
+ print(*msg, file=sys.stderr)
+ global warning_count
+ warning_count += 1
+
+
+def die(*msg):
+ warn(*msg)
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Convert markdown into html and (optionally) nroff. Each input filename must have a .md suffix, which is changed to .html for the output filename. If the input filename ends with .num.md (e.g. foo.1.md) then a nroff file is also output with the input filename's .md suffix removed (e.g. foo.1).", add_help=False)
+ parser.add_argument('--test', action='store_true', help="Just test the parsing without outputting any files.")
+ parser.add_argument('--dest', metavar='DIR', help="Create files in DIR instead of the current directory.")
+ parser.add_argument('--force-link-text', action='store_true', help="Don't remove the link text if it matches the link href. Useful when nroff doesn't understand .UR and .UE.")
+ parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
+ args = parser.parse_args()
+
+ try:
+ import cmarkgfm
+ md_parser = cmarkgfm.markdown_to_html
+ gfm_parser = cmarkgfm.github_flavored_markdown_to_html
+ except:
+ try:
+ import commonmark
+ md_parser = html_via_commonmark
+ except:
+ die("Failed to find cmarkgfm or commonmark for python3.")
+ gfm_parser = None
+
+ main()
+ if warning_count:
+ sys.exit(1)
diff --git a/md2man b/md2man
deleted file mode 100755
index fa1d2e824..000000000
--- a/md2man
+++ /dev/null
@@ -1,393 +0,0 @@
-#!/usr/bin/env python3
-
-# This script takes a manpage written in markdown and turns it into an html web
-# page and a nroff man page. The input file must have the name of the program
-# and the section in this 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, code, or strong tag. The
-# cmarkgfm or commonmark 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.
-#
-# We normally grab the prefix from the generated Makefile, which is then used
-# in the various other grabbed values (see the Makefile for its ${prefix}
-# paths). However, the maintainer can choose to override this prefix by
-# exporting RSYNC_OVERRIDE_PREFIX=/usr. This allows the man pages to refer to
-# /usr paths (and are thus compatible with the release-rsync script) while
-# still having the built rsync get installed into /usr/local for local testing.
-#
-# Copyright (C) 2020 Wayne Davison
-#
-# This program is freely redistributable.
-
-import sys, os, re, argparse, subprocess, time
-from html.parser import HTMLParser
-
-CONSUMES_TXT = set('h1 h2 p li pre'.split())
-
-HTML_START = """\
-
-%s
-
-
-
-"""
-
-HTML_END = """\
-%s
-
-"""
-
-MAN_START = r"""
-.TH "%s" "%s" "%s" "%s" "User Commands"
-.\" prefix=%s
-""".lstrip()
-
-MAN_END = """\
-"""
-
-NORM_FONT = ('\1', r"\fP")
-BOLD_FONT = ('\2', r"\fB")
-UNDR_FONT = ('\3', r"\fI")
-NBR_DASH = ('\4', r"\-")
-NBR_SPACE = ('\xa0', r"\ ")
-
-md_parser = None
-
-def main():
- fi = re.match(r'^(?P(?P.+/)?(?P(?P[^/]+)\.(?P\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 = './'
-
- fi.title = fi.prog + '(' + fi.sect + ') man page'
- fi.mtime = 0
-
- git_dir = fi.srcdir + '.git'
- if os.path.lexists(git_dir):
- fi.mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
-
- env_subs = { 'prefix': os.environ.get('RSYNC_OVERRIDE_PREFIX', None) }
-
- if args.test:
- env_subs['VERSION'] = '1.0.0'
- env_subs['bindir'] = '/usr/bin'
- env_subs['libdir'] = '/usr/lib/rsync'
- else:
- for fn in (fi.srcdir + 'version.h', 'Makefile'):
- try:
- st = os.lstat(fn)
- except:
- die('Failed to find', fi.srcdir + fn)
- if not fi.mtime:
- fi.mtime = st.st_mtime
-
- with open(fi.srcdir + 'version.h', 'r', encoding='utf-8') as fh:
- txt = fh.read()
- m = re.search(r'"(.+?)"', txt)
- env_subs['VERSION'] = m.group(1)
-
- with open('Makefile', 'r', encoding='utf-8') as fh:
- for line in fh:
- m = re.match(r'^(\w+)=(.+)', line)
- if not m:
- continue
- var, val = (m.group(1), m.group(2))
- if var == 'prefix' and env_subs[var] is not None:
- continue
- while re.search(r'\$\{', val):
- val = re.sub(r'\$\{(\w+)\}', lambda m: env_subs[m.group(1)], val)
- env_subs[var] = val
- if var == 'srcdir':
- break
-
- with open(fi.fn, 'r', encoding='utf-8') as fh:
- txt = fh.read()
-
- txt = re.sub(r'@VERSION@', env_subs['VERSION'], txt)
- txt = re.sub(r'@BINDIR@', env_subs['bindir'], txt)
- txt = re.sub(r'@LIBDIR@', env_subs['libdir'], txt)
-
- fi.html_in = md_parser(txt)
- txt = None
-
- fi.date = time.strftime('%d %b %Y', time.localtime(fi.mtime))
- fi.man_headings = (fi.prog, fi.sect, fi.date, fi.prog + ' ' + env_subs['VERSION'], env_subs['prefix'])
-
- HtmlToManPage(fi)
-
- if args.test:
- print("The test was successful.")
- return
-
- for fn, txt in ((fi.name + '.html', fi.html_out), (fi.name, fi.man_out)):
- print("Wrote:", fn)
- with open(fn, 'w', encoding='utf-8') as fh:
- fh.write(txt)
-
-
-def html_via_commonmark(txt):
- return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
-
-
-class HtmlToManPage(HTMLParser):
- def __init__(self, fi):
- HTMLParser.__init__(self, convert_charrefs=True)
-
- st = self.state = argparse.Namespace(
- list_state = [ ],
- p_macro = ".P\n",
- at_first_tag_in_li = False,
- at_first_tag_in_dd = False,
- dt_from = None,
- in_pre = False,
- in_code = False,
- html_out = [ HTML_START % fi.title ],
- man_out = [ MAN_START % fi.man_headings ],
- txt = '',
- )
-
- self.feed(fi.html_in)
- fi.html_in = None
-
- st.html_out.append(HTML_END % fi.date)
- st.man_out.append(MAN_END)
-
- fi.html_out = ''.join(st.html_out)
- st.html_out = None
-
- fi.man_out = ''.join(st.man_out)
- st.man_out = None
-
-
- def handle_starttag(self, tag, attrs_list):
- st = self.state
- if args.debug:
- self.output_debug('START', (tag, attrs_list))
- if st.at_first_tag_in_li:
- if st.list_state[-1] == 'dl':
- st.dt_from = tag
- if tag == 'p':
- tag = 'dt'
- else:
- st.html_out.append('')
- elif tag == 'p':
- st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
- st.at_first_tag_in_li = False
- if tag == 'p':
- if not st.at_first_tag_in_dd:
- st.man_out.append(st.p_macro)
- elif tag == 'li':
- st.at_first_tag_in_li = True
- lstate = st.list_state[-1]
- if lstate == 'dl':
- return
- if lstate == 'o':
- st.man_out.append(".IP o\n")
- else:
- st.man_out.append(".IP " + str(lstate) + ".\n")
- st.list_state[-1] += 1
- elif tag == 'blockquote':
- st.man_out.append(".RS 4\n")
- elif tag == 'pre':
- st.in_pre = True
- st.man_out.append(st.p_macro + ".nf\n")
- elif tag == 'code' and not st.in_pre:
- st.in_code = True
- st.txt += BOLD_FONT[0]
- elif tag == 'strong' or tag == 'b':
- st.txt += BOLD_FONT[0]
- elif tag == 'em' or tag == 'i':
- tag = 'u' # Change it into underline to be more like the man page
- st.txt += UNDR_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:
- st.man_out.append(".RS\n")
- if start == 0:
- tag = 'dl'
- attrs_list = [ ]
- st.list_state.append('dl')
- else:
- st.list_state.append(start)
- st.man_out.append(st.p_macro)
- st.p_macro = ".IP\n"
- elif tag == 'ul':
- st.man_out.append(st.p_macro)
- if st.list_state:
- st.man_out.append(".RS\n")
- st.p_macro = ".IP\n"
- st.list_state.append('o')
- st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
- st.at_first_tag_in_dd = False
-
-
- def handle_endtag(self, tag):
- st = self.state
- if args.debug:
- self.output_debug('END', (tag,))
- 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':
- st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
- elif tag == 'h2':
- st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
- elif tag == 'p':
- if st.dt_from == 'p':
- tag = 'dt'
- st.man_out.append('.IP "' + manify(txt) + '"\n')
- st.dt_from = None
- elif txt != '':
- st.man_out.append(manify(txt) + "\n")
- elif tag == 'li':
- if st.list_state[-1] == 'dl':
- if st.at_first_tag_in_li:
- die("Invalid 0. -> td translation")
- tag = 'dd'
- if txt != '':
- st.man_out.append(manify(txt) + "\n")
- st.at_first_tag_in_li = False
- elif tag == 'blockquote':
- st.man_out.append(".RE\n")
- elif tag == 'pre':
- st.in_pre = False
- st.man_out.append(manify(txt) + "\n.fi\n")
- elif (tag == 'code' and not st.in_pre):
- st.in_code = False
- add_to_txt = NORM_FONT[0]
- elif tag == 'strong' or tag == 'b':
- add_to_txt = NORM_FONT[0]
- elif tag == 'em' or tag == 'i':
- tag = 'u' # Change it into underline to be more like the man page
- add_to_txt = NORM_FONT[0]
- elif tag == 'ol' or tag == 'ul':
- if st.list_state.pop() == 'dl':
- tag = 'dl'
- if st.list_state:
- st.man_out.append(".RE\n")
- else:
- st.p_macro = ".P\n"
- st.at_first_tag_in_dd = False
- st.html_out.append('' + tag + '>')
- if add_to_txt:
- if txt is None:
- st.txt += add_to_txt
- else:
- txt += add_to_txt
- if st.dt_from == tag:
- st.man_out.append('.IP "' + manify(txt) + '"\n')
- st.html_out.append(' ')
- st.at_first_tag_in_dd = True
- st.dt_from = None
- elif tag == 'dt':
- st.html_out.append('')
- st.at_first_tag_in_dd = True
-
-
- def handle_data(self, txt):
- st = self.state
- if args.debug:
- self.output_debug('DATA', (txt,))
- if st.in_pre:
- html = htmlify(txt)
- else:
- txt = re.sub(r'\s--(\s)', NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
- txt = re.sub(r'(^|\W)-', r'\1' + NBR_DASH[0], txt)
- html = htmlify(txt)
- if st.in_code:
- txt = re.sub(r'\s', NBR_SPACE[0], txt)
- html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # is non-breaking in CSS
- st.html_out.append(html.replace(NBR_SPACE[0], ' ').replace(NBR_DASH[0], '-'))
- st.txt += txt
-
-
- def output_debug(self, event, extra):
- import pprint
- st = self.state
- if args.debug < 2:
- st = argparse.Namespace(**vars(st))
- if len(st.html_out) > 2:
- st.html_out = ['...'] + st.html_out[-2:]
- if len(st.man_out) > 2:
- st.man_out = ['...'] + st.man_out[-2:]
- print(event, extra)
- pprint.PrettyPrinter(indent=2).pprint(vars(st))
-
-
-def manify(txt):
- return re.sub(r"^(['.])", r'\&\1', txt.replace('\\', '\\\\')
- .replace(NBR_SPACE[0], NBR_SPACE[1])
- .replace(NBR_DASH[0], NBR_DASH[1])
- .replace(NORM_FONT[0], NORM_FONT[1])
- .replace(BOLD_FONT[0], BOLD_FONT[1])
- .replace(UNDR_FONT[0], UNDR_FONT[1]), flags=re.M)
-
-
-def htmlify(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. Repeat for even more.')
- 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
- md_parser = cmarkgfm.markdown_to_html
- except:
- try:
- import commonmark
- md_parser = html_via_commonmark
- except:
- die("Failed to find cmarkgfm or commonmark for python3.")
-
- main()
diff --git a/md2man b/md2man
new file mode 120000
index 000000000..5d1a8fcd9
--- /dev/null
+++ b/md2man
@@ -0,0 +1 @@
+md-convert
\ No newline at end of file
diff --git a/mkgitver b/mkgitver
index 49aa150be..0102b0894 100755
--- a/mkgitver
+++ b/mkgitver
@@ -1,14 +1,16 @@
#!/bin/sh
srcdir=`dirname $0`
-gitver=`git describe --abbrev=8 2>/dev/null`
if [ ! -f git-version.h ]; then
touch git-version.h
fi
-case "$gitver" in
- *.*)
+if test -d "$srcdir/.git" || test -f "$srcdir/.git"; then
+ gitver=`git describe --abbrev=8 2>/dev/null`
+ # NOTE: I'm avoiding "|" in sed since I'm not sure if sed -r is portable and "\|" fails on some OSes.
+ verchk=`echo "$gitver-" | sed -n '/^v3\.[0-9][0-9]*\.[0-9][0-9]*\(pre[0-9]*\)*-/p'`
+ if [ -n "$verchk" ]; then
echo "#define RSYNC_GITVER \"$gitver\"" >git-version.h.new
if ! diff git-version.h.new git-version.h >/dev/null; then
echo "Updating git-version.h"
@@ -16,5 +18,5 @@ case "$gitver" in
else
rm git-version.h.new
fi
- ;;
-esac
+ fi
+fi
diff --git a/options.c b/options.c
index 3f0354624..578507c6e 100644
--- a/options.c
+++ b/options.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998-2001 Andrew Tridgell
* Copyright (C) 2000, 2001, 2002 Martin Pool
- * Copyright (C) 2002-2020 Wayne Davison
+ * Copyright (C) 2002-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,8 @@
extern int module_id;
extern int local_server;
extern int sanitize_paths;
+extern int trust_sender_args;
+extern int trust_sender_filter;
extern unsigned int module_dirlen;
extern filter_rule_list filter_list;
extern filter_rule_list daemon_filter_list;
@@ -47,6 +49,7 @@ int append_mode = 0;
int keep_dirlinks = 0;
int copy_dirlinks = 0;
int copy_links = 0;
+int copy_devices = 0;
int write_devices = 0;
int preserve_links = 0;
int preserve_hard_links = 0;
@@ -63,6 +66,7 @@ int preserve_atimes = 0;
int preserve_crtimes = 0;
int omit_dir_times = 0;
int omit_link_times = 0;
+int trust_sender = 0;
int update_only = 0;
int open_noatime = 0;
int cvs_exclude = 0;
@@ -91,6 +95,7 @@ int implied_dirs = 1;
int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */
int numeric_ids = 0;
int msgs2stderr = 2; /* Default: send errors to stderr for local & remote-shell transfers */
+int saw_stderr_opt = 0;
int allow_8bit_chars = 0;
int force_delete = 0;
int io_timeout = 0;
@@ -101,6 +106,7 @@ int filesfrom_fd = -1;
char *filesfrom_host = NULL;
int eol_nulls = 0;
int protect_args = -1;
+int old_style_args = -1;
int human_readable = 1;
int recurse = 0;
int mkpath_dest_arg = 0;
@@ -194,6 +200,7 @@ int remote_option_cnt = 0;
const char **remote_options = NULL;
const char *checksum_choice = NULL;
const char *compress_choice = NULL;
+static const char *empty_argv[1];
int quiet = 0;
int output_motd = 1;
@@ -230,7 +237,7 @@ static const char *debug_verbosity[] = {
#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
static const char *info_verbosity[1+MAX_VERBOSITY] = {
- /*0*/ NULL,
+ /*0*/ "NONREG",
/*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
/*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
};
@@ -268,9 +275,10 @@ static struct output_struct info_words[COUNT_INFO+1] = {
INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information (levels 1-2)"),
INFO_WORD(MOUNT, W_SND|W_REC, "Mention mounts that were found or skipped"),
INFO_WORD(NAME, W_SND|W_REC, "Mention 1) updated file/dir names, 2) unchanged names"),
+ INFO_WORD(NONREG, W_REC, "Mention skipped non-regular files (default 1, 0 disables)"),
INFO_WORD(PROGRESS, W_CLI, "Mention 1) per-file progress or 2) total transfer progress"),
INFO_WORD(REMOVE, W_SND, "Mention files removed on the sending side"),
- INFO_WORD(SKIP, W_REC, "Mention files that are skipped due to options used (levels 1-2)"),
+ INFO_WORD(SKIP, W_REC, "Mention files skipped due to transfer overrides (levels 1-2)"),
INFO_WORD(STATS, W_CLI|W_SRV, "Mention statistics at end of run (levels 1-3)"),
INFO_WORD(SYMSAFE, W_SND|W_REC, "Mention symlinks that are unsafe"),
{ NULL, "--info", 0, 0, 0, 0 }
@@ -289,7 +297,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"),
- DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"),
+ DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"),
DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"),
DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"),
DEBUG_WORD(GENR, W_REC, "Debug generator functions"),
@@ -488,9 +496,9 @@ static void output_item_help(struct output_struct *words)
rprintf(FINFO, fmt, "HELP", "Output this help message");
rprintf(FINFO, "\n");
- rprintf(FINFO, "Options added for each increase in verbose level:\n");
+ rprintf(FINFO, "Options added at each level of verbosity:\n");
- for (j = 1; j <= MAX_VERBOSITY; j++) {
+ for (j = 0; j <= MAX_VERBOSITY; j++) {
parse_output_words(words, levels, verbosity[j], HELP_PRIORITY);
opt = make_output_option(words, levels, W_CLI|W_SRV|W_SND|W_REC);
if (opt) {
@@ -509,7 +517,7 @@ static void set_output_verbosity(int level, uchar priority)
if (level > MAX_VERBOSITY)
level = MAX_VERBOSITY;
- for (j = 1; j <= level; j++) {
+ for (j = 0; j <= level; j++) {
parse_output_words(info_words, info_levels, info_verbosity[j], priority);
parse_output_words(debug_words, debug_levels, debug_verbosity[j], priority);
}
@@ -528,7 +536,7 @@ void limit_output_verbosity(int level)
memset(debug_limits, 0, sizeof debug_limits);
/* Compute the level limits in the above arrays. */
- for (j = 1; j <= level; j++) {
+ for (j = 0; j <= level; j++) {
parse_output_words(info_words, info_limits, info_verbosity[j], LIMIT_PRIORITY);
parse_output_words(debug_words, debug_limits, debug_verbosity[j], LIMIT_PRIORITY);
}
@@ -575,7 +583,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
- OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS,
+ OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
OPT_STOP_AFTER, OPT_STOP_AT,
OPT_REFUSED_BASE = 9000};
@@ -653,6 +661,7 @@ static struct poptOption long_options[] = {
{"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 },
{"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 },
{"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 },
+ {"copy-devices", 0, POPT_ARG_NONE, ©_devices, 0, 0, 0 },
{"write-devices", 0, POPT_ARG_VAL, &write_devices, 1, 0, 0 },
{"no-write-devices", 0, POPT_ARG_VAL, &write_devices, 0, 0, 0 },
{"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 },
@@ -778,9 +787,14 @@ static struct poptOption long_options[] = {
{"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
{"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
{"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
- {"protect-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
+ {"old-args", 0, POPT_ARG_NONE, 0, OPT_OLD_ARGS, 0, 0},
+ {"no-old-args", 0, POPT_ARG_VAL, &old_style_args, 0, 0, 0},
+ {"secluded-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
+ {"no-secluded-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"protect-args", 0, POPT_ARG_VAL, &protect_args, 1, 0, 0},
{"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
{"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"trust-sender", 0, POPT_ARG_VAL, &trust_sender, 1, 0, 0},
{"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
{"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
{"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
@@ -939,11 +953,12 @@ static void set_refuse_options(void)
if (!am_daemon
|| op->shortName == 'e' /* Required for compatibility flags */
|| op->shortName == '0' /* --from0 just modifies --files-from, so refuse that instead (or not) */
- || op->shortName == 's' /* --protect-args is always OK */
+ || op->shortName == 's' /* --secluded-args is always OK */
|| op->shortName == 'n' /* --dry-run is always OK */
|| strcmp("iconv", longName) == 0
|| strcmp("no-iconv", longName) == 0
|| strcmp("checksum-seed", longName) == 0
+ || strcmp("copy-devices", longName) == 0 /* disable wild-match (it gets refused below) */
|| strcmp("write-devices", longName) == 0 /* disable wild-match (it gets refused below) */
|| strcmp("log-format", longName) == 0 /* aka out-format (NOT log-file-format) */
|| strcmp("sender", longName) == 0
@@ -955,6 +970,7 @@ static void set_refuse_options(void)
assert(list_end != NULL);
if (am_daemon) { /* Refused by default, but can be accepted via a negated exact match. */
+ parse_one_refuse_match(0, "copy-devices", list_end);
parse_one_refuse_match(0, "write-devices", list_end);
}
@@ -1332,7 +1348,7 @@ char *alt_dest_opt(int type)
**/
int parse_arguments(int *argc_p, const char ***argv_p)
{
- static poptContext pc;
+ poptContext pc;
const char *arg, **argv = *argv_p;
int argc = *argc_p;
int opt, want_dest_type;
@@ -1352,10 +1368,6 @@ int parse_arguments(int *argc_p, const char ***argv_p)
/* TODO: Call poptReadDefaultConfig; handle errors. */
- /* The context leaks in case of an error, but if there's a
- * problem we always exit anyhow. */
- if (pc)
- poptFreeContext(pc);
pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
if (!am_server) {
poptReadDefaultConfig(pc, 0);
@@ -1398,7 +1410,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
strlcpy(err_buf,
"Attempt to hack rsync thwarted!\n",
sizeof err_buf);
- return 0;
+ goto cleanup;
}
#ifdef ICONV_OPTION
iconv_opt = NULL;
@@ -1444,7 +1456,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
if (!daemon_opt) {
@@ -1454,8 +1466,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
exit_cleanup(RERR_SYNTAX);
}
- *argv_p = argv = poptGetArgs(pc);
- *argc_p = argc = count_args(argv);
+ argv = poptGetArgs(pc);
+ argc = count_args(argv);
+ if (!argc) {
+ *argv_p = empty_argv;
+ *argc_p = 0;
+ } else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
+ out_of_memory("parse_arguments");
+ argv = *argv_p;
+ poptFreeContext(pc);
+
am_starting_up = 0;
daemon_opt = 0;
am_daemon = 1;
@@ -1510,7 +1530,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'a':
if (refused_archive_part) {
create_refuse_error(refused_archive_part);
- return 0;
+ goto cleanup;
}
if (!recurse) /* preserve recurse == 2 */
recurse = 1;
@@ -1580,7 +1600,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'P':
if (refused_partial || refused_progress) {
create_refuse_error(refused_partial ? refused_partial : refused_progress);
- return 0;
+ goto cleanup;
}
do_progress = 1;
keep_partial = 1;
@@ -1603,12 +1623,19 @@ int parse_arguments(int *argc_p, const char ***argv_p)
compress_choice = NULL;
break;
+ case OPT_OLD_ARGS:
+ if (old_style_args <= 0)
+ old_style_args = 1;
+ else
+ old_style_args++;
+ break;
+
case 'M':
arg = poptGetOptArg(pc);
if (*arg != '-') {
snprintf(err_buf, sizeof err_buf,
"Remote option must start with a dash: %s\n", arg);
- return 0;
+ goto cleanup;
}
if (remote_option_cnt+2 >= remote_option_alloc) {
remote_option_alloc += 16;
@@ -1650,27 +1677,27 @@ int parse_arguments(int *argc_p, const char ***argv_p)
ssize_t size;
arg = poptGetOptArg(pc);
if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0)
- return 0;
+ goto cleanup;
block_size = (int32)size;
break;
}
case OPT_MAX_SIZE:
if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0)
- return 0;
+ goto cleanup;
max_size_arg = strdup(do_big_num(max_size, 0, NULL));
break;
case OPT_MIN_SIZE:
if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0)
- return 0;
+ goto cleanup;
min_size_arg = strdup(do_big_num(min_size, 0, NULL));
break;
case OPT_BWLIMIT: {
ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True);
if (size < 0)
- return 0;
+ goto cleanup;
bwlimit_arg = strdup(do_big_num(size, 0, NULL));
bwlimit = (size + 512) / 1024;
break;
@@ -1699,7 +1726,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: the %s option conflicts with the %s option\n",
alt_dest_opt(want_dest_type), alt_dest_opt(0));
- return 0;
+ goto cleanup;
}
alt_dest_type = want_dest_type;
@@ -1707,7 +1734,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: at most %d %s args may be specified\n",
MAX_BASIS_DIRS, alt_dest_opt(0));
- return 0;
+ goto cleanup;
}
/* We defer sanitizing this arg until we know what
* our destination directory is going to be. */
@@ -1720,7 +1747,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"Invalid argument passed to --chmod (%s)\n",
arg);
- return 0;
+ goto cleanup;
}
break;
@@ -1739,11 +1766,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--usermap conflicts with prior --chown.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --usermap once.\n");
- return 0;
+ goto cleanup;
}
usermap = (char *)poptGetOptArg(pc);
usermap_via_chown = False;
@@ -1755,11 +1782,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--groupmap conflicts with prior --chown.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --groupmap once.\n");
- return 0;
+ goto cleanup;
}
groupmap = (char *)poptGetOptArg(pc);
groupmap_via_chown = False;
@@ -1778,11 +1805,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --usermap.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a user-affecting --chown once.\n");
- return 0;
+ goto cleanup;
}
if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
out_of_memory("parse_arguments");
@@ -1794,11 +1821,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --groupmap.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a group-affecting --chown once.\n");
- return 0;
+ goto cleanup;
}
if (asprintf(&groupmap, "*:%s", arg) < 0)
out_of_memory("parse_arguments");
@@ -1826,7 +1853,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"ACLs are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
case 'X':
@@ -1837,7 +1864,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"extended attributes are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
case OPT_STOP_AFTER: {
@@ -1846,7 +1873,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
stop_at_utime = time(NULL);
if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg);
- return 0;
+ goto cleanup;
}
stop_at_utime += val;
break;
@@ -1857,11 +1884,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
arg = poptGetOptArg(pc);
if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg);
- return 0;
+ goto cleanup;
}
if (stop_at_utime <= time(NULL)) {
snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg);
- return 0;
+ goto cleanup;
}
break;
#endif
@@ -1879,8 +1906,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
else {
snprintf(err_buf, sizeof err_buf,
"--stderr mode \"%s\" is not one of errors, all, or client\n", arg);
- return 0;
+ goto cleanup;
}
+ saw_stderr_opt = 1;
break;
}
@@ -1889,18 +1917,21 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* turned this option off. */
if (opt >= OPT_REFUSED_BASE) {
create_refuse_error(opt);
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
am_server ? "on remote machine: " : "",
poptBadOption(pc, POPT_BADOPTION_NOALIAS),
poptStrerror(opt));
- return 0;
+ goto cleanup;
}
}
+ if (msgs2stderr != 2)
+ saw_stderr_opt = 1;
+
if (version_opt_cnt) {
- print_rsync_version(FINFO);
+ print_rsync_version(version_opt_cnt > 1 && !am_server ? FNONE : FINFO);
exit_cleanup(0);
}
@@ -1912,9 +1943,26 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (max_alloc_arg) {
ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True);
if (size < 0)
- return 0;
+ goto cleanup;
max_alloc = size;
}
+ if (!max_alloc)
+ max_alloc = SIZE_MAX;
+
+ if (old_style_args < 0) {
+ if (!am_server && protect_args <= 0 && (arg = getenv("RSYNC_OLD_ARGS")) != NULL && *arg) {
+ protect_args = 0;
+ old_style_args = atoi(arg);
+ } else
+ old_style_args = 0;
+ } else if (old_style_args) {
+ if (protect_args > 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--secluded-args conflicts with --old-args.\n");
+ goto cleanup;
+ }
+ protect_args = 0;
+ }
if (protect_args < 0) {
if (am_server)
@@ -1922,7 +1970,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
protect_args = atoi(arg) ? 1 : 0;
else {
-#ifdef RSYNC_USE_PROTECTED_ARGS
+#ifdef RSYNC_USE_SECLUDED_ARGS
protect_args = 1;
#else
protect_args = 0;
@@ -1956,7 +2004,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
do_compression = CPRES_AUTO;
if (do_compression && refused_compress) {
create_refuse_error(refused_compress);
- return 0;
+ goto cleanup;
}
}
@@ -1981,7 +2029,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
default:
snprintf(err_buf, sizeof err_buf,
"Invalid --outbuf setting -- specify N, L, or B.\n");
- return 0;
+ goto cleanup;
}
setvbuf(stdout, (char *)NULL, mode, 0);
}
@@ -2009,7 +2057,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
if (refused_no_iconv && !iconv_opt) {
create_refuse_error(refused_no_iconv);
- return 0;
+ goto cleanup;
}
#endif
@@ -2020,18 +2068,30 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (orig_protect_args == 2 && am_server)
protect_args = orig_protect_args;
- if (protect_args == 1 && am_server)
+ if (protect_args == 1 && am_server) {
+ poptFreeContext(pc);
return 1;
+ }
- *argv_p = argv = poptGetArgs(pc);
- *argc_p = argc = count_args(argv);
+ /* Because popt 1.19 has started to free the returned args data, we now
+ * make a copy of the array and then do an immediate cleanup. */
+ argv = poptGetArgs(pc);
+ argc = count_args(argv);
+ if (!argc) {
+ *argv_p = empty_argv;
+ *argc_p = 0;
+ } else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
+ out_of_memory("parse_arguments");
+ argv = *argv_p;
+ poptFreeContext(pc);
+ pc = NULL;
#ifndef SUPPORT_LINKS
if (preserve_links && !am_sender) {
snprintf(err_buf, sizeof err_buf,
"symlinks are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
}
#endif
@@ -2040,7 +2100,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"hard links are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
}
#endif
@@ -2048,20 +2108,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_root < 0 && preserve_xattrs > 1) {
snprintf(err_buf, sizeof err_buf,
"--fake-super conflicts with -XX\n");
- return 0;
+ goto cleanup;
}
#else
if (am_root < 0) {
snprintf(err_buf, sizeof err_buf,
"--fake-super requires an rsync with extended attributes enabled\n");
- return 0;
+ goto cleanup;
}
#endif
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
- return 0;
+ goto cleanup;
}
if (write_batch > 0 || read_batch) {
if (am_server) {
@@ -2080,25 +2140,25 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (read_batch && files_from) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --files-from\n");
- return 0;
+ goto cleanup;
}
if (read_batch && remove_source_files) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --remove-%s-files\n",
remove_source_files == 1 ? "source" : "sent");
- return 0;
+ goto cleanup;
}
if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
snprintf(err_buf, sizeof err_buf,
"the batch-file name must be %d characters or less.\n",
MAX_BATCH_NAME_LEN);
- return 0;
+ goto cleanup;
}
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
if (max_delete < 0 && max_delete != INT_MIN) {
@@ -2132,7 +2192,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (delete_before + !!delete_during + delete_after > 1) {
snprintf(err_buf, sizeof err_buf,
"You may not combine multiple --delete-WHEN options.\n");
- return 0;
+ goto cleanup;
}
if (delete_before || delete_during || delete_after)
delete_mode = 1;
@@ -2143,7 +2203,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
delete_during = 1;
else {
create_refuse_error(refused_delete_before);
- return 0;
+ goto cleanup;
}
} else if (refused_delete_during)
delete_before = 1;
@@ -2152,14 +2212,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!xfer_dirs && delete_mode) {
snprintf(err_buf, sizeof err_buf,
"--delete does not work without --recursive (-r) or --dirs (-d).\n");
- return 0;
+ goto cleanup;
}
if (missing_args == 3) /* simplify if both options were specified */
missing_args = 2;
if (refused_delete && (delete_mode || missing_args == 2)) {
create_refuse_error(refused_delete);
- return 0;
+ goto cleanup;
}
if (remove_source_files) {
@@ -2168,7 +2228,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* options. */
if (refused_delete && am_sender) {
create_refuse_error(refused_delete);
- return 0;
+ goto cleanup;
}
need_messages_from_generator = 1;
}
@@ -2222,7 +2282,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"--suffix cannot contain slashes: %s\n",
backup_suffix);
- return 0;
+ goto cleanup;
}
if (backup_dir) {
size_t len;
@@ -2235,7 +2295,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (len > sizeof backup_dir_buf - 128) {
snprintf(err_buf, sizeof err_buf,
"the --backup-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
backup_dir_len = (int)len;
if (!backup_dir_len) {
@@ -2254,7 +2314,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--suffix cannot be empty %s\n", backup_dir_len < 0
? "when --backup-dir is the same as the dest dir"
: "without a --backup-dir");
- return 0;
+ goto cleanup;
} else if (make_backups && delete_mode && !delete_excluded && !am_server) {
snprintf(backup_dir_buf, sizeof backup_dir_buf,
"P *%s", backup_suffix);
@@ -2282,7 +2342,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (do_progress && !am_server) {
if (!log_before_transfer && INFO_EQ(NAME, 0))
parse_output_words(info_words, info_levels, "name", DEFAULT_PRIORITY);
- parse_output_words(info_words, info_levels, "flist2,progress", DEFAULT_PRIORITY);
+ parse_output_words(info_words, info_levels, "FLIST2,PROGRESS", DEFAULT_PRIORITY);
}
if (dry_run)
@@ -2323,11 +2383,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (whole_file > 0) {
snprintf(err_buf, sizeof err_buf,
"--append cannot be used with --whole-file\n");
- return 0;
+ goto cleanup;
}
if (refused_inplace) {
create_refuse_error(refused_inplace);
- return 0;
+ goto cleanup;
}
inplace = 1;
}
@@ -2335,7 +2395,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (write_devices) {
if (refused_inplace) {
create_refuse_error(refused_inplace);
- return 0;
+ goto cleanup;
}
inplace = 1;
}
@@ -2350,13 +2410,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s cannot be used with --%s\n",
append_mode ? "append" : "inplace",
delay_updates ? "delay-updates" : "partial-dir");
- return 0;
+ goto cleanup;
}
/* --inplace implies --partial for refusal purposes, but we
* clear the keep_partial flag for internal logic purposes. */
if (refused_partial) {
create_refuse_error(refused_partial);
- return 0;
+ goto cleanup;
}
keep_partial = 0;
#else
@@ -2364,7 +2424,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s is not supported on this %s\n",
append_mode ? "append" : "inplace",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
} else {
if (keep_partial && !partial_dir && !am_server) {
@@ -2378,7 +2438,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
partial_dir = NULL;
if (!partial_dir && refused_partial) {
create_refuse_error(refused_partial);
- return 0;
+ goto cleanup;
}
keep_partial = 1;
}
@@ -2399,14 +2459,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_server) {
snprintf(err_buf, sizeof err_buf,
"The --files-from sent to the server cannot specify a host.\n");
- return 0;
+ goto cleanup;
}
files_from = p;
filesfrom_host = h;
if (strcmp(files_from, "-") == 0) {
snprintf(err_buf, sizeof err_buf,
"Invalid --files-from remote filename\n");
- return 0;
+ goto cleanup;
}
} else {
if (sanitize_paths)
@@ -2425,11 +2485,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"failed to open files-from file %s: %s\n",
files_from, strerror(errno));
- return 0;
+ goto cleanup;
}
}
}
+ if (trust_sender || am_server || read_batch)
+ trust_sender_args = trust_sender_filter = 1;
+ else if (old_style_args || filesfrom_host != NULL)
+ trust_sender_args = 1;
+
am_starting_up = 0;
return 1;
@@ -2437,10 +2502,80 @@ int parse_arguments(int *argc_p, const char ***argv_p)
options_rejected:
snprintf(err_buf, sizeof err_buf,
"Your options have been rejected by the server.\n");
+ cleanup:
+ if (pc)
+ poptFreeContext(pc);
return 0;
}
+static char SPLIT_ARG_WHEN_OLD[1];
+
+/**
+ * Do backslash quoting of any weird chars in "arg", append the resulting
+ * string to the end of the "opt" (which gets a "=" appended if it is not
+ * an empty or NULL string), and return the (perhaps malloced) result.
+ * If opt is NULL, arg is considered a filename arg that allows wildcards.
+ * If it is "" or any other value, it is considered an option.
+ **/
+char *safe_arg(const char *opt, const char *arg)
+{
+#define SHELL_CHARS "!#$&;|<>(){}\"'` \t\\"
+#define WILD_CHARS "*?[]" /* We don't allow remote brace expansion */
+ BOOL is_filename_arg = !opt;
+ char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS;
+ BOOL escape_leading_dash = is_filename_arg && *arg == '-';
+ BOOL escape_leading_tilde = 0;
+ int len1 = opt && *opt ? strlen(opt) + 1 : 0;
+ int len2 = strlen(arg);
+ int extras = escape_leading_dash ? 2 : 0;
+ char *ret;
+ if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
+ const char *f;
+ if (*arg == '~' && is_filename_arg && !am_sender && !trust_sender_args
+ && ((relative_paths && !strstr(arg, "/./"))
+ || !strchr(arg, '/'))) {
+ extras++;
+ escape_leading_tilde = 1;
+ }
+ for (f = arg; *f; f++) {
+ if (strchr(escapes, *f))
+ extras++;
+ }
+ }
+ if (!len1 && !extras)
+ return (char*)arg;
+ ret = new_array(char, len1 + len2 + extras + 1);
+ if (len1) {
+ memcpy(ret, opt, len1-1);
+ ret[len1-1] = '=';
+ }
+ if (escape_leading_dash) {
+ ret[len1++] = '.';
+ ret[len1++] = '/';
+ extras -= 2;
+ }
+ if (!extras)
+ memcpy(ret + len1, arg, len2);
+ else {
+ const char *f = arg;
+ char *t = ret + len1;
+ if (escape_leading_tilde)
+ *t++ = '\\';
+ while (*f) {
+ if (*f == '\\') {
+ if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
+ *t++ = '\\';
+ } else if (strchr(escapes, *f))
+ *t++ = '\\';
+ *t++ = *f++;
+ }
+ }
+ ret[len1+len2+extras] = '\0';
+ return ret;
+}
+
+
/**
* Construct a filtered list of options to pass through from the
* client to the server.
@@ -2584,9 +2719,7 @@ void server_options(char **args, int *argc_p)
set++;
else
set = iconv_opt;
- if (asprintf(&arg, "--iconv=%s", set) < 0)
- goto oom;
- args[ac++] = arg;
+ args[ac++] = safe_arg("--iconv", set);
}
#endif
@@ -2652,33 +2785,24 @@ void server_options(char **args, int *argc_p)
}
if (backup_dir) {
+ /* This split idiom allows for ~/path expansion via the shell. */
args[ac++] = "--backup-dir";
- args[ac++] = backup_dir;
+ args[ac++] = safe_arg("", backup_dir);
}
/* Only send --suffix if it specifies a non-default value. */
- if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
- /* We use the following syntax to avoid weirdness with '~'. */
- if (asprintf(&arg, "--suffix=%s", backup_suffix) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0)
+ args[ac++] = safe_arg("--suffix", backup_suffix);
- if (checksum_choice) {
- if (asprintf(&arg, "--checksum-choice=%s", checksum_choice) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ if (checksum_choice)
+ args[ac++] = safe_arg("--checksum-choice", checksum_choice);
if (do_compression == CPRES_ZLIBX)
args[ac++] = "--new-compress";
else if (compress_choice && do_compression == CPRES_ZLIB)
args[ac++] = "--old-compress";
- else if (compress_choice) {
- if (asprintf(&arg, "--compress-choice=%s", compress_choice) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ else if (compress_choice)
+ args[ac++] = safe_arg("--compress-choice", compress_choice);
if (am_sender) {
if (max_delete > 0) {
@@ -2687,14 +2811,10 @@ void server_options(char **args, int *argc_p)
args[ac++] = arg;
} else if (max_delete == 0)
args[ac++] = "--max-delete=-1";
- if (min_size >= 0) {
- args[ac++] = "--min-size";
- args[ac++] = min_size_arg;
- }
- if (max_size >= 0) {
- args[ac++] = "--max-size";
- args[ac++] = max_size_arg;
- }
+ if (min_size >= 0)
+ args[ac++] = safe_arg("--min-size", min_size_arg);
+ if (max_size >= 0)
+ args[ac++] = safe_arg("--max-size", max_size_arg);
if (delete_before)
args[ac++] = "--delete-before";
else if (delete_during == 2)
@@ -2718,17 +2838,12 @@ void server_options(char **args, int *argc_p)
if (do_stats)
args[ac++] = "--stats";
} else {
- if (skip_compress) {
- if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ if (skip_compress)
+ args[ac++] = safe_arg("--skip-compress", skip_compress);
}
- if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC) {
- args[ac++] = "--max-alloc";
- args[ac++] = max_alloc_arg;
- }
+ if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC)
+ args[ac++] = safe_arg("--max-alloc", max_alloc_arg);
/* --delete-missing-args needs the cooperation of both sides, but
* the sender can handle --ignore-missing-args by itself. */
@@ -2753,7 +2868,7 @@ void server_options(char **args, int *argc_p)
if (partial_dir && am_sender) {
if (partial_dir != tmp_partialdir) {
args[ac++] = "--partial-dir";
- args[ac++] = partial_dir;
+ args[ac++] = safe_arg("", partial_dir);
}
if (delay_updates)
args[ac++] = "--delay-updates";
@@ -2776,17 +2891,11 @@ void server_options(char **args, int *argc_p)
args[ac++] = "--use-qsort";
if (am_sender) {
- if (usermap) {
- if (asprintf(&arg, "--usermap=%s", usermap) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ if (usermap)
+ args[ac++] = safe_arg("--usermap", usermap);
- if (groupmap) {
- if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
- goto oom;
- args[ac++] = arg;
- }
+ if (groupmap)
+ args[ac++] = safe_arg("--groupmap", groupmap);
if (ignore_existing)
args[ac++] = "--ignore-existing";
@@ -2797,7 +2906,7 @@ void server_options(char **args, int *argc_p)
if (tmpdir) {
args[ac++] = "--temp-dir";
- args[ac++] = tmpdir;
+ args[ac++] = safe_arg("", tmpdir);
}
if (do_fsync)
@@ -2810,7 +2919,7 @@ void server_options(char **args, int *argc_p)
*/
for (i = 0; i < basis_dir_cnt; i++) {
args[ac++] = alt_dest_opt(0);
- args[ac++] = basis_dir[i];
+ args[ac++] = safe_arg("", basis_dir[i]);
}
}
}
@@ -2828,14 +2937,14 @@ void server_options(char **args, int *argc_p)
} else if (inplace) {
args[ac++] = "--inplace";
/* Work around a bug in older rsync versions (on the remote side) for --inplace --sparse */
- if (sparse_files && !whole_file)
+ if (sparse_files && !whole_file && am_sender)
args[ac++] = "--no-W";
}
if (files_from && (!am_sender || filesfrom_host)) {
if (filesfrom_host) {
args[ac++] = "--files-from";
- args[ac++] = files_from;
+ args[ac++] = safe_arg("", files_from);
if (eol_nulls)
args[ac++] = "--from0";
} else {
@@ -2857,6 +2966,9 @@ void server_options(char **args, int *argc_p)
else if (remove_source_files)
args[ac++] = "--remove-sent-files";
+ if (copy_devices && !am_sender)
+ args[ac++] = "--copy-devices";
+
if (preallocate_files && am_sender)
args[ac++] = "--preallocate";
@@ -2878,7 +2990,7 @@ void server_options(char **args, int *argc_p)
exit_cleanup(RERR_SYNTAX);
}
for (j = 1; j <= remote_option_cnt; j++)
- args[ac++] = (char*)remote_options[j];
+ args[ac++] = safe_arg(SPLIT_ARG_WHEN_OLD, remote_options[j]);
}
*argc_p = ac;
diff --git a/packaging/auto-Makefile b/packaging/auto-Makefile
index 29d2d6889..032913d57 100644
--- a/packaging/auto-Makefile
+++ b/packaging/auto-Makefile
@@ -1,6 +1,6 @@
-TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
+TARGETS := all install install-ssl-daemon install-all install-strip conf gen reconfigure restatus \
proto man clean cleantests distclean test check check29 check30 installcheck splint \
- doxygen doxygen-upload finddead
+ doxygen doxygen-upload finddead rrsync
.PHONY: $(TARGETS) auto-prep
diff --git a/packaging/branch-from-patch b/packaging/branch-from-patch
index 440b5835a..40e5653c4 100755
--- a/packaging/branch-from-patch
+++ b/packaging/branch-from-patch
@@ -154,7 +154,7 @@ def create_branch(patch):
s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."])
if not s.returncode:
break
- s = cmd_run(['/bin/zsh'])
+ s = cmd_run([os.environ.get('SHELL', '/bin/sh')])
if s.returncode:
die('Aborting due to shell error code')
diff --git a/packaging/cull-options b/packaging/cull-options
new file mode 100755
index 000000000..e71818cdb
--- /dev/null
+++ b/packaging/cull-options
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# This script outputs either perl or python code that parses all possible options
+# that the code in options.c might send to the server. The resulting code is then
+# included in the rrsync script.
+
+import re, argparse
+
+short_no_arg = { }
+short_with_num = { '@': 1 }
+long_opts = { # These include some extra long-args that BackupPC uses:
+ 'block-size': 1,
+ 'daemon': -1,
+ 'debug': 1,
+ 'fake-super': 0,
+ 'fuzzy': 0,
+ 'group': 0,
+ 'hard-links': 0,
+ 'ignore-times': 0,
+ 'info': 1,
+ 'links': 0,
+ 'log-file': 3,
+ 'munge-links': 0,
+ 'no-munge-links': -1,
+ 'one-file-system': 0,
+ 'owner': 0,
+ 'perms': 0,
+ 'recursive': 0,
+ 'stderr': 1,
+ 'times': 0,
+ 'copy-devices': -1,
+ 'write-devices': -1,
+ }
+
+def main():
+ last_long_opt = None
+
+ with open('../options.c') as fh:
+ for line in fh:
+ m = re.search(r"argstr\[x\+\+\] = '([^.ie])'", line)
+ if m:
+ short_no_arg[m.group(1)] = 1
+ last_long_opt = None
+ continue
+
+ m = re.search(r'asprintf\([^,]+, "-([a-zA-Z0-9])\%l?[ud]"', line)
+ if m:
+ short_with_num[m.group(1)] = 1
+ last_long_opt = None
+ continue
+
+ m = re.search(r'args\[ac\+\+\] = "--([^"=]+)"', line)
+ if m:
+ last_long_opt = m.group(1)
+ if last_long_opt not in long_opts:
+ long_opts[last_long_opt] = 0
+ else:
+ last_long_opt = None
+ continue
+
+ if last_long_opt:
+ m = re.search(r'args\[ac\+\+\] = safe_arg\("", ([^[("\s]+)\);', line)
+ if m:
+ long_opts[last_long_opt] = 2
+ last_long_opt = None
+ continue
+ if 'args[ac++] = ' in line:
+ last_long_opt = None
+
+ m = re.search(r'return "--([^"]+-dest)";', line)
+ if m:
+ long_opts[m.group(1)] = 2
+ last_long_opt = None
+ continue
+
+ m = re.search(r'asprintf\([^,]+, "--([^"=]+)=', line)
+ if not m:
+ m = re.search(r'args\[ac\+\+\] = "--([^"=]+)=', line)
+ if not m:
+ m = re.search(r'args\[ac\+\+\] = safe_arg\("--([^"=]+)"', line)
+ if not m:
+ m = re.search(r'fmt = .*: "--([^"=]+)=', line)
+ if m:
+ long_opts[m.group(1)] = 1
+ last_long_opt = None
+
+ long_opts['files-from'] = 3
+
+ txt = """\
+### START of options data produced by the cull-options script. ###
+
+# To disable a short-named option, add its letter to this string:
+"""
+
+ txt += str_assign('short_disabled', 's') + "\n"
+ txt += '# These are also disabled when the restricted dir is not "/":\n'
+ txt += str_assign('short_disabled_subdir', 'KLk') + "\n"
+ txt += '# These are all possible short options that we will accept (when not disabled above):\n'
+ txt += str_assign('short_no_arg', ''.join(sorted(short_no_arg)), 'DO NOT REMOVE ANY')
+ txt += str_assign('short_with_num', ''.join(sorted(short_with_num)), 'DO NOT REMOVE ANY')
+
+ txt += """
+# To disable a long-named option, change its value to a -1. The values mean:
+# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
+# check the arg when receiving; and 3 = always check the arg.
+"""
+
+ print(txt, end='')
+
+ if args.python:
+ print("long_opts = {")
+ sep = ':'
+ else:
+ print("our %long_opt = (")
+ sep = ' =>'
+
+ for opt in sorted(long_opts):
+ if opt.startswith(('min-', 'max-')):
+ val = 1
+ else:
+ val = long_opts[opt]
+ print(' ', repr(opt) + sep, str(val) + ',')
+
+ if args.python:
+ print("}")
+ else:
+ print(");")
+ print("\n### END of options data produced by the cull-options script. ###")
+
+
+def str_assign(name, val, comment=None):
+ comment = ' # ' + comment if comment else ''
+ if args.python:
+ return name + ' = ' + repr(val) + comment + "\n"
+ return 'our $' + name + ' = ' + repr(val) + ';' + comment + "\n"
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output culled rsync options for rrsync.", add_help=False)
+ out_group = parser.add_mutually_exclusive_group()
+ out_group.add_argument('--perl', action='store_true', help="Output perl code.")
+ out_group.add_argument('--python', action='store_true', help="Output python code (the default).")
+ parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
+ args = parser.parse_args()
+ if not args.perl:
+ args.python = True
+ main()
+
+# vim: sw=4 et
diff --git a/packaging/cull_options b/packaging/cull_options
deleted file mode 100755
index 7588002be..000000000
--- a/packaging/cull_options
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env perl
-# This script outputs some perl code that parses all possible options
-# that the code in options.c might send to the server. This perl code
-# is included in the rrsync script.
-use strict;
-
-our %short_no_arg;
-our %short_with_num = ( '@' => 1 );
-our %long_opt = ( # These include some extra long-args that BackupPC uses:
- 'block-size' => 1,
- 'daemon' => -1,
- 'debug' => 1,
- 'fake-super' => 0,
- 'fuzzy' => 0,
- 'group' => 0,
- 'hard-links' => 0,
- 'ignore-times' => 0,
- 'info' => 1,
- 'links' => 0,
- 'log-file' => 3,
- 'one-file-system' => 0,
- 'owner' => 0,
- 'perms' => 0,
- 'recursive' => 0,
- 'times' => 0,
- 'write-devices' => -1,
-);
-our $last_long_opt;
-
-open(IN, '../options.c') or die "Unable to open ../options.c: $!\n";
-
-while () {
- if (/\Qargstr[x++]\E = '([^.ie])'/) {
- $short_no_arg{$1} = 1;
- undef $last_long_opt;
- } elsif (/\Qasprintf(\E[^,]+, "-([a-zA-Z0-9])\%l?[ud]"/) {
- $short_with_num{$1} = 1;
- undef $last_long_opt;
- } elsif (/\Qargs[ac++]\E = "--([^"=]+)"/) {
- $last_long_opt = $1;
- $long_opt{$1} = 0 unless exists $long_opt{$1};
- } elsif (defined($last_long_opt)
- && /\Qargs[ac++]\E = ([^["\s]+);/) {
- $long_opt{$last_long_opt} = 2;
- undef $last_long_opt;
- } elsif (/return "--([^"]+-dest)";/) {
- $long_opt{$1} = 2;
- undef $last_long_opt;
- } elsif (/\Qasprintf(\E[^,]+, "--([^"=]+)=/ || /\Qargs[ac++]\E = "--([^"=]+)=/ || /fmt = .*: "--([^"=]+)=/) {
- $long_opt{$1} = 1;
- undef $last_long_opt;
- }
-}
-close IN;
-
-my $short_no_arg = join('', sort keys %short_no_arg);
-my $short_with_num = join('', sort keys %short_with_num);
-
-print < $val,\n";
-}
-
-print ");\n\n";
diff --git a/packaging/lsb/rsync.spec b/packaging/lsb/rsync.spec
index 04163c72b..845b188a9 100644
--- a/packaging/lsb/rsync.spec
+++ b/packaging/lsb/rsync.spec
@@ -1,6 +1,6 @@
Summary: A fast, versatile, remote (and local) file-copying tool
Name: rsync
-Version: 3.2.3
+Version: 3.4.1
%define fullversion %{version}
Release: 1
%define srcdir src
@@ -79,9 +79,5 @@ rm -rf $RPM_BUILD_ROOT
%dir /etc/rsync-ssl/certs
%changelog
-* Thu Aug 06 2020 Wayne Davison
-Released 3.2.3.
-
-* Fri Mar 21 2008 Wayne Davison
-Added installation of /etc/xinetd.d/rsync file and some commented-out
-lines that demonstrate how to use the rsync-patches tar file.
+* Thu Jan 16 2025 Rsync Project
+Released 3.4.1.
diff --git a/packaging/md2html b/packaging/md2html
deleted file mode 100755
index 21e42c66a..000000000
--- a/packaging/md2html
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2020 Wayne Davison
-#
-# This program is freely redistributable.
-
-import os, re, argparse
-
-HTML_START = """\
-
-%s
-
-
-
-"""
-
-HTML_END = """\
-
-"""
-
-md_parser = None
-
-def main():
- for mdfn in args.mdfiles:
- if not mdfn.endswith('.md'):
- print('Ignoring non-md input file:', mdfn)
- continue
- title = re.sub(r'.*/', '', mdfn).replace('.md', '')
- htfn = mdfn.replace('.md', '.html')
-
- print("Parsing", mdfn, '->', htfn)
-
- with open(mdfn, 'r', encoding='utf-8') as fh:
- txt = fh.read()
-
- txt = re.sub(r'\s--\s', '\xa0-- ', txt)
-
- html = md_parser(txt)
-
- html = re.sub(r'(?)()([\s\S]*?)(
)', lambda m: m[1] + re.sub(r'\s', '\xa0', m[2]) + m[3], html)
- html = html.replace('--', '‑‑').replace("\xa0-", ' ‑').replace("\xa0", ' ')
- html = re.sub(r'(\W)-', r'\1‑', html)
-
- if os.path.lexists(htfn):
- os.unlink(htfn)
-
- with open(htfn, 'w', encoding='utf-8') as fh:
- fh.write(HTML_START % title)
- fh.write(html)
- fh.write(HTML_END)
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='Output html for md pages.', add_help=False)
- parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
- parser.add_argument("mdfiles", nargs='+', help="The .md files to turn into .html files.")
- args = parser.parse_args()
-
- try:
- import cmarkgfm
- # Our NEWS.md file has a gfm table in it.
- md_parser = cmarkgfm.github_flavored_markdown_to_html
- except:
- die("Failed to find cmarkgfm for python3.")
-
- main()
diff --git a/packaging/openssl-rsync.cnf b/packaging/openssl-rsync.cnf
new file mode 100644
index 000000000..7432285df
--- /dev/null
+++ b/packaging/openssl-rsync.cnf
@@ -0,0 +1,18 @@
+# This config file can be used with rsync to enable legacy digests
+# (such as MD4) by using the OPENSSL_CONF environment variable.
+# See rsync's configure --with-openssl-conf=/path/name option.
+
+openssl_conf = openssl_init
+
+[openssl_init]
+providers = provider_sect
+
+[provider_sect]
+default = default_sect
+legacy = legacy_sect
+
+[default_sect]
+activate = 1
+
+[legacy_sect]
+activate = 1
diff --git a/packaging/pkglib.py b/packaging/pkglib.py
index 57cc2012d..c2b293074 100644
--- a/packaging/pkglib.py
+++ b/packaging/pkglib.py
@@ -32,7 +32,7 @@ def _maybe_set(o, **msa): # Only set a value if the user didn't already set it.
opts = opts.copy()
_maybe_set(opts, **maybe_set_args)
- if type(cmd) == str:
+ if isinstance(cmd, str):
_maybe_set(opts, shell=True)
want_raw = opts.pop('raw', False)
@@ -170,18 +170,7 @@ def get_patch_branches(base_branch):
return branches
-def mandate_gensend_hook():
- hook = '.git/hooks/pre-push'
- if not os.path.exists(hook):
- print('Creating hook file:', hook)
- cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
- else:
- ct = cmd_txt(['fgrep', 'make gensend', hook], discard='output')
- if ct.rc:
- die('Please add a "make gensend" into your', hook, 'script.')
-
-
-# Snag the GENFILES values out of the Makefile.in file and return them as a list.
+# Snag the GENFILES values out of the Makefile file and return them as a list.
def get_gen_files(want_dir_plus_list=False):
cont_re = re.compile(r'\\\n')
@@ -189,7 +178,7 @@ def get_gen_files(want_dir_plus_list=False):
auto_dir = os.path.join('auto-build-save', cmd_txt('git rev-parse --abbrev-ref HEAD').out.strip().replace('/', '%'))
- with open('Makefile.in', 'r', encoding='utf-8') as fh:
+ with open(auto_dir + '/Makefile', 'r', encoding='utf-8') as fh:
for line in fh:
if not gen_files:
chk = re.sub(r'^GENFILES=', '', line)
diff --git a/packaging/pre-push b/packaging/pre-push
deleted file mode 100755
index 8a713695e..000000000
--- a/packaging/pre-push
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash -e
-
-cat >/dev/null # Just discard stdin data
-
-if [[ -f /proc/$PPID/cmdline ]]; then
- while read -d $'\0' arg ; do
- if [[ "$arg" == '--tags' ]] ; then
- exit 0
- fi
- done &2
diff --git a/packaging/release-rsync b/packaging/release-rsync
index fa1da2349..2d1486b8d 100755
--- a/packaging/release-rsync
+++ b/packaging/release-rsync
@@ -3,7 +3,24 @@
# This script expects the directory ~/samba-rsync-ftp to exist and to be a
# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done,
# the git repository in the current directory will be updated, and the local
-# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org.
+# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org. See the
+# script samba-rsync for an easy way to initialize the local ftp copy and to
+# thereafter update the remote files from your local copy.
+
+# This script also expects to be able to gpg sign the resulting tar files
+# using your default gpg key. Make sure that the html download.html file
+# has a link to the relevant keys that are authorized to sign the tar files
+# and also make sure that the following commands work as expected:
+#
+# touch TeMp
+# gpg --sign TeMp
+# gpg --verify TeMp.gpg
+# gpg --sign TeMp
+# rm TeMp*
+#
+# The second time you sign the file it should NOT prompt you for your password
+# (unless the timeout period has passed). It will prompt about overriding the
+# existing TeMp.gpg file, though.
import os, sys, re, argparse, glob, shutil, signal
from datetime import datetime
@@ -21,13 +38,15 @@ def main():
if not os.path.isfile('packaging/release-rsync'):
die('You must run this script from the top of your rsync checkout.')
- now = datetime.now()
+ now = datetime.now().astimezone() # Requires python 3.6 or later
cl_today = now.strftime('* %a %b %d %Y')
year = now.strftime('%Y')
ztoday = now.strftime('%d %b %Y')
today = ztoday.lstrip('0')
- mandate_gensend_hook()
+ # The MAINTAINER_TZ_OFFSET is a float number of hours vs UTC. It can start with '-' but not '+'.
+ tz_now = now.strftime('%z')
+ tz_num = tz_now[0:1].replace('+', '') + str(float(tz_now[1:3]) + float(tz_now[3:]) / 60)
curdir = os.getcwd()
@@ -105,6 +124,8 @@ def main():
if not re.match(r'^del', ans, flags=re.I):
die("Aborted")
cmd_chk(['git', 'tag', '-d', v_ver])
+ if os.path.isdir('patches/.git'):
+ cmd_chk(f"cd patches && git tag -d '{v_ver}'")
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
if 'pre' in version and not curversion.endswith('dev'):
@@ -185,7 +206,7 @@ About to:
'%define srcdir': srcdir,
}
- tweak_files = 'version.h rsync.h NEWS.md'.split()
+ tweak_files = 'version.h rsync.h'.split()
tweak_files += glob.glob('packaging/*.spec')
tweak_files += glob.glob('packaging/*/*.spec')
@@ -193,7 +214,12 @@ About to:
with open(fn, 'r', encoding='utf-8') as fh:
old_txt = txt = fh.read()
if fn == 'version.h':
- txt = f'#define RSYNC_VERSION "{version}"\n'
+ x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
+ msg = f"Unable to update RSYNC_VERSION in {fn}"
+ txt = replace_or_die(x_re, r'\1 "%s"' % version, txt, msg)
+ x_re = re.compile(r'^(#define MAINTAINER_TZ_OFFSET).*', re.M)
+ msg = f"Unable to update MAINTAINER_TZ_OFFSET in {fn}"
+ txt = replace_or_die(x_re, r'\1 ' + tz_num, txt, msg)
elif '.spec' in fn:
for var, val in specvars.items():
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
@@ -201,15 +227,15 @@ About to:
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}")
elif fn == 'rsync.h':
- x_re = re.compile('(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
+ x_re = re.compile(r'(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
repl = lambda m: m[1] + ' ' + ('0' if not pre or not proto_changed else '1' if m[2] == '0' else m[2])
txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}")
elif fn == 'NEWS.md':
efv = re.escape(finalversion)
- x_re = re.compile(r'^<.+>\s+# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
+ x_re = re.compile(r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
rel_day = 'UNRELEASED' if pre else today
- repl = (f'\n\n# NEWS for rsync {finalversion} ({rel_day})\n\n'
+ repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
+ '## Changes in this version:\n')
if proto_changed:
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
@@ -230,7 +256,7 @@ About to:
cmd_chk(['packaging/year-tweak'])
print(dash_line)
- cmd_run("git diff --color | less -p '^diff .*'")
+ cmd_run("git diff".split())
srctar_name = f"{rsync_ver}.tar.gz"
pattar_name = f"rsync-patches-{version}.tar.gz"
@@ -245,20 +271,20 @@ About to:
About to:
- git commit all changes
- - generate the manpages
+ - run a full build, ensuring that the manpages & configure.sh are up-to-date
- merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches
- update the files in the "patches" dir and OPTIONALLY (if you type 'y') to
run patch-update with the --make option (which opens a shell on error)
""")
ans = input(" ")
- s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version}'])
+ s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
if s.returncode:
die('Aborting')
- cmd_chk('make gen')
+ cmd_chk('touch configure.ac && packaging/smart-make && make gen')
- print(f'Creating any missing patch branches.')
+ print('Creating any missing patch branches.')
s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
if s.returncode:
die('Aborting')
@@ -341,7 +367,7 @@ About to:
md_files = 'README.md NEWS.md INSTALL.md'.split()
html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ]
cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
- cmd_chk(["packaging/md2html"] + [ dest +'/'+ fn for fn in md_files ])
+ cmd_chk(["./md-convert", "--dest", dest, *md_files])
cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
diff --git a/packaging/samba-rsync b/packaging/samba-rsync
new file mode 100755
index 000000000..b291b276d
--- /dev/null
+++ b/packaging/samba-rsync
@@ -0,0 +1,124 @@
+#!/bin/bash
+# This script makes it easy to update the ftp & html directories on the samba.org server.
+# It expects the 2 *_DEST directories to contain updated files that need to be sent to
+# the remote server. If these directories don't exist yet, they will be copied from the
+# remote server (while also making the html dir a git checkout).
+
+FTP_SRC="$HOME/samba-rsync-ftp"
+HTML_SRC="$HOME/samba-rsync-html"
+
+FTP_DEST="/home/ftp/pub/rsync"
+HTML_DEST="/home/httpd/html/rsync"
+
+HTML_GIT='git.samba.org:/data/git/rsync-web.git'
+
+export RSYNC_PARTIAL_DIR=''
+
+case "$RSYNC_SAMBA_HOST" in
+ *.samba.org) ;;
+ *)
+ echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
+ exit 1
+ ;;
+esac
+
+MODE=''
+REVERSE=''
+while (( $# )); do
+ case "$1" in
+ -R|--reverse) REVERSE=yes ;;
+ f|ftp) MODE=ftp ;;
+ h|html) MODE=html ;;
+ -h|--help)
+ echo "Usage: [-R] [f|ftp|h|html]"
+ echo "-R --reverse Copy the files from the server to the local host."
+ echo " The default is to update the remote files."
+ echo "-h --help Output this help message."
+ echo " "
+ echo "The script will prompt if ftp or html is not specified on the command line."
+ echo "Only one category can be copied at a time. When pulling html files, a git"
+ echo "checkout will be either created or updated prior to the rsync copy."
+ exit
+ ;;
+ *)
+ echo "Invalid option: $1" >&2
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+while [ ! "$MODE" ]; do
+ if [ "$REVERSE" = yes ]; then
+ DIRECTION=FROM
+ else
+ DIRECTION=TO
+ fi
+ echo -n "Copy which files $DIRECTION the server? ftp or html? "
+ read ans
+ case "$ans" in
+ f*) MODE=ftp ;;
+ h*) MODE=html ;;
+ '') exit 1 ;;
+ *) echo "You must answer f or h to copy the ftp or html data." ;;
+ esac
+done
+
+if [ "$MODE" = ftp ]; then
+ SRC_DIR="$FTP_SRC"
+ DEST_DIR="$FTP_DEST"
+ FILT=".filt"
+else
+ SRC_DIR="$HTML_SRC"
+ DEST_DIR="$HTML_DEST"
+ FILT="filt"
+fi
+
+function do_rsync {
+ rsync --dry-run "${@}" | grep -v 'is uptodate$'
+ echo ''
+ echo -n "Run without --dry-run? [n] "
+ read ans
+ case "$ans" in
+ y*) rsync "${@}" | grep -v 'is uptodate$' ;;
+ esac
+}
+
+if [ -d "$SRC_DIR" ]; then
+ REVERSE_RSYNC=do_rsync
+else
+ echo "The directory $SRC_DIR does not exist yet."
+ echo -n "Do you want to create it? [n] "
+ read ans
+ case "$ans" in
+ y*) ;;
+ *) exit 1 ;;
+ esac
+ REVERSE=yes
+ REVERSE_RSYNC=rsync
+fi
+
+if [ "$REVERSE" = yes ]; then
+ OPTS='-aivOHP'
+ TMP_FILT="$SRC_DIR/tmp-filt"
+ echo "Copying files from $RSYNC_SAMBA_HOST to $SRC_DIR ..."
+ if [ "$MODE" = html ]; then
+ if [ $REVERSE_RSYNC = rsync ]; then
+ git clone "$HTML_GIT" "$SRC_DIR" || exit 1
+ else
+ cd "$SRC_DIR" || exit 1
+ git pull || exit 1
+ fi
+ sed -n -e 's/[-P]/H/p' "$SRC_DIR/$FILT" >"$TMP_FILT"
+ OPTS="${OPTS}f._$TMP_FILT"
+ else
+ OPTS="${OPTS}f:_$FILT"
+ fi
+ $REVERSE_RSYNC "$OPTS" "$RSYNC_SAMBA_HOST:$DEST_DIR/" "$SRC_DIR/"
+ rm -f "$TMP_FILT"
+ exit
+fi
+
+cd "$SRC_DIR" || exit 1
+echo "Copying files from $SRC_DIR to $RSYNC_SAMBA_HOST ..."
+do_rsync -aivOHP --chown=:rsync --del -f._$FILT . "$RSYNC_SAMBA_HOST:$DEST_DIR/"
diff --git a/packaging/send-news b/packaging/send-news
new file mode 100755
index 000000000..c83a74c03
--- /dev/null
+++ b/packaging/send-news
@@ -0,0 +1,33 @@
+#!/bin/bash -e
+
+# This script expects the ~/src/rsync directory to contain the rsync
+# source that has been updated. It also expects the auto-build-save
+# directory to have been created prior to the running of configure so
+# that each branch has its own build directory underneath. This supports
+# the maintainer workflow for the rsync-patches files maintenace.
+
+FTP_SRC="$HOME/samba-rsync-ftp"
+FTP_DEST="/home/ftp/pub/rsync"
+MD_FILES="README.md INSTALL.md NEWS.md"
+
+case "$RSYNC_SAMBA_HOST" in
+ *.samba.org) ;;
+ *)
+ echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
+ exit 1
+ ;;
+esac
+
+if [ ! -d "$FTP_SRC" ]; then
+ packaging/samba-rsync ftp # Ask to initialize the local ftp dir
+fi
+
+cd ~/src/rsync
+
+make man
+./md-convert --dest="$FTP_SRC" $MD_FILES
+rsync -aiic $MD_FILES auto-build-save/master/*.?.html "$FTP_SRC"
+
+cd "$FTP_SRC"
+
+rsync -aiic README.* INSTALL.* NEWS.* *.?.html "$RSYNC_SAMBA_HOST:$FTP_DEST/"
diff --git a/packaging/systemd/rsync.service b/packaging/systemd/rsync.service
index 8a0b5820a..8a867ca64 100644
--- a/packaging/systemd/rsync.service
+++ b/packaging/systemd/rsync.service
@@ -7,6 +7,7 @@ Documentation=man:rsync(1) man:rsyncd.conf(5)
[Service]
ExecStart=/usr/bin/rsync --daemon --no-detach
RestartSec=1
+Restart=on-failure
# Citing README.md:
#
diff --git a/packaging/var-checker b/packaging/var-checker
index f17c69a29..1573895c0 100755
--- a/packaging/var-checker
+++ b/packaging/var-checker
@@ -6,9 +6,10 @@
import os, sys, re, argparse, glob
-VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
+VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z][^ \n\t:]*\s+.*);', re.M)
EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
+types = { }
sizes = { }
def main():
@@ -68,19 +69,44 @@ def parse_vars(fn, lines):
for line in lines:
line = re.sub(r'\s*\{.*\}', '', line)
line = re.sub(r'\s*\(.*\)', '', line)
- for item in re.split(r'\s*,\s*', line):
- item = re.sub(r'\s*=.*', '', item)
- m = re.search(r'(?P\w+)(?P\[.*?\])?$', item)
+ line = re.sub(r'\s*=\s*[^,]*', '', line)
+ m = re.search(r'^(?:(?:static|extern)\s+)?(?P[^\[,]+?)(?P\w+([\[,].+)?)$', line)
+ if not m:
+ print(f"Bogus match? ({line})")
+ continue
+ items = m['vars']
+ main_type = m['type'].strip()
+ mt_len = len(main_type)
+ main_type = main_type.rstrip('*')
+ first_stars = '*' * (mt_len - len(main_type))
+ if first_stars:
+ main_type = main_type.rstrip()
+ items = first_stars + items
+ for item in re.split(r'\s*,\s*', items):
+ m = re.search(r'(?P\*+\s*)?(?P\w+)(?P\[.*?\])?$', item)
if not m:
print(f"Bogus match? ({item})")
continue
- if m['sz']:
- if m['var'] in sizes:
- if sizes[m['var']] != m['sz']:
+ typ = main_type
+ if m['stars']:
+ typ = typ + m['stars'].strip()
+ chk = [
+ 'type', typ, types,
+ 'size', m['sz'], sizes,
+ ]
+ while chk:
+ label = chk.pop(0)
+ new = chk.pop(0)
+ lst = chk.pop(0)
+ if label == 'type':
+ new = ' '.join(new.split()).replace(' *', '*')
+ if m['var'] in lst:
+ old = lst[m['var']]
+ if new != old:
var = m['var']
- print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
+ print(fn, f'has inconsistent {label} for "{var}":', new, 'vs', old)
else:
- sizes[m['var']] = m['sz']
+ lst[m['var']] = new
ret.append(m['var'])
return ret
diff --git a/packaging/year-tweak b/packaging/year-tweak
index 69d2f2ff9..8a7fb98e4 100755
--- a/packaging/year-tweak
+++ b/packaging/year-tweak
@@ -7,9 +7,6 @@
import sys, os, re, argparse, subprocess
from datetime import datetime
-MAINTAINER_NAME = 'Wayne Davison'
-MAINTAINER_SUF = ' ' + MAINTAINER_NAME + "\n"
-
def main():
latest_year = '2000'
@@ -22,10 +19,6 @@ def main():
m = argparse.Namespace(**m.groupdict())
if m.year > latest_year:
latest_year = m.year
- if m.fn.startswith('zlib/') or m.fn.startswith('popt/'):
- continue
- if re.search(r'\.(c|h|sh|test)$', m.fn):
- maybe_edit_copyright_year(m.fn, m.year)
proc.communicate()
fn = 'latest-year.h'
@@ -39,55 +32,8 @@ def main():
fh.write(txt)
-def maybe_edit_copyright_year(fn, year):
- opening_lines = [ ]
- copyright_line = None
-
- with open(fn, 'r', encoding='utf-8') as fh:
- for lineno, line in enumerate(fh):
- opening_lines.append(line)
- if lineno > 3 and not re.search(r'\S', line):
- break
- m = re.match(r'^(?P.*Copyright\s+\S+\s+)(?P\d\d\d\d(?:-\d\d\d\d)?(,\s+\d\d\d\d)*)(?P.+)', line)
- if not m:
- continue
- copyright_line = argparse.Namespace(**m.groupdict())
- copyright_line.lineno = len(opening_lines)
- copyright_line.is_maintainer_line = MAINTAINER_NAME in copyright_line.suf
- copyright_line.txt = line
- if copyright_line.is_maintainer_line:
- break
-
- if not copyright_line:
- return
-
- if copyright_line.is_maintainer_line:
- cyears = copyright_line.year.split('-')
- if year == cyears[0]:
- cyears = [ year ]
- else:
- cyears = [ cyears[0], year ]
- txt = copyright_line.pre + '-'.join(cyears) + MAINTAINER_SUF
- if txt == copyright_line.txt:
- return
- opening_lines[copyright_line.lineno - 1] = txt
- else:
- if fn.startswith('lib/') or fn.startswith('testsuite/'):
- return
- txt = copyright_line.pre + year + MAINTAINER_SUF
- opening_lines[copyright_line.lineno - 1] += txt
-
- remaining_txt = fh.read()
-
- print(f"Updating {fn} with year {year}")
-
- with open(fn, 'w', encoding='utf-8') as fh:
- fh.write(''.join(opening_lines))
- fh.write(remaining_txt)
-
-
if __name__ == '__main__':
- parser = argparse.ArgumentParser(description="Grab the year of last mod for our c & h files and make sure the Copyright comment is up-to-date.")
+ parser = argparse.ArgumentParser(description="Grab the year of the last mod for our c & h files and make sure the LATEST_YEAR value is accurate.")
args = parser.parse_args()
main()
diff --git a/popt/findme.c b/popt/findme.c
deleted file mode 100644
index ac4cbaed2..000000000
--- a/popt/findme.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/** \ingroup popt
- * \file popt/findme.c
- */
-
-/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
- file accompanying popt source distributions, available from
- ftp://ftp.rpm.org/pub/rpm/dist. */
-
-#include "system.h"
-#include "findme.h"
-
-const char * findProgramPath(const char * argv0)
-{
- char * path = getenv("PATH");
- char * pathbuf;
- char * start, * chptr;
- char * buf;
- size_t bufsize;
-
- if (argv0 == NULL) return NULL; /* XXX can't happen */
- /* If there is a / in the argv[0], it has to be an absolute path */
- if (strchr(argv0, '/'))
- return xstrdup(argv0);
-
- if (path == NULL) return NULL;
-
- bufsize = strlen(path) + 1;
- start = pathbuf = alloca(bufsize);
- if (pathbuf == NULL) return NULL; /* XXX can't happen */
- strlcpy(pathbuf, path, bufsize);
- bufsize += sizeof "/" - 1 + strlen(argv0);
- buf = malloc(bufsize);
- if (buf == NULL) return NULL; /* XXX can't happen */
-
- chptr = NULL;
- /*@-branchstate@*/
- do {
- if ((chptr = strchr(start, ':')))
- *chptr = '\0';
- snprintf(buf, bufsize, "%s/%s", start, argv0);
-
- if (!access(buf, X_OK))
- return buf;
-
- if (chptr)
- start = chptr + 1;
- else
- start = NULL;
- } while (start && *start);
- /*@=branchstate@*/
-
- free(buf);
-
- return NULL;
-}
diff --git a/popt/findme.h b/popt/findme.h
deleted file mode 100644
index a016b867e..000000000
--- a/popt/findme.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/** \ingroup popt
- * \file popt/findme.h
- */
-
-/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
- file accompanying popt source distributions, available from
- ftp://ftp.rpm.org/pub/rpm/dist. */
-
-#ifndef H_FINDME
-#define H_FINDME
-
-/**
- * Return absolute path to executable by searching PATH.
- * @param argv0 name of executable
- * @return (malloc'd) absolute path to executable (or NULL)
- */
-/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
- /*@*/;
-
-#endif
diff --git a/popt/lookup3.c b/popt/lookup3.c
new file mode 100644
index 000000000..e974cad87
--- /dev/null
+++ b/popt/lookup3.c
@@ -0,0 +1,959 @@
+/* -------------------------------------------------------------------- */
+/*
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * jlu32w(), jlu32l(), jlu32lpair(), jlu32b(), _JLU3_MIX(), and _JLU3_FINAL()
+ * are externally useful functions. Routines to test the hash are included
+ * if SELF_TEST is defined. You can use this free for any purpose. It's in
+ * the public domain. It has no warranty.
+ *
+ * You probably want to use jlu32l(). jlu32l() and jlu32b()
+ * hash byte arrays. jlu32l() is is faster than jlu32b() on
+ * little-endian machines. Intel and AMD are little-endian machines.
+ * On second thought, you probably want jlu32lpair(), which is identical to
+ * jlu32l() except it returns two 32-bit hashes for the price of one.
+ * You could implement jlu32bpair() if you wanted but I haven't bothered here.
+ *
+ * If you want to find a hash of, say, exactly 7 integers, do
+ * a = i1; b = i2; c = i3;
+ * _JLU3_MIX(a,b,c);
+ * a += i4; b += i5; c += i6;
+ * _JLU3_MIX(a,b,c);
+ * a += i7;
+ * _JLU3_FINAL(a,b,c);
+ * then use c as the hash value. If you have a variable size array of
+ * 4-byte integers to hash, use jlu32w(). If you have a byte array (like
+ * a character string), use jlu32l(). If you have several byte arrays, or
+ * a mix of things, see the comments above jlu32l().
+ *
+ * Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+ * then mix those integers. This is fast (you can do a lot more thorough
+ * mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+ * on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+*/
+/* -------------------------------------------------------------------- */
+
+#include
+
+#if defined(_JLU3_SELFTEST)
+# define _JLU3_jlu32w 1
+# define _JLU3_jlu32l 1
+# define _JLU3_jlu32lpair 1
+# define _JLU3_jlu32b 1
+#endif
+
+static const union _dbswap {
+ const uint32_t ui;
+ const unsigned char uc[4];
+} endian = { .ui = 0x11223344 };
+# define HASH_LITTLE_ENDIAN (endian.uc[0] == (unsigned char) 0x44)
+# define HASH_BIG_ENDIAN (endian.uc[0] == (unsigned char) 0x11)
+
+#ifndef ROTL32
+# define ROTL32(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
+#endif
+
+/* NOTE: The _size parameter should be in bytes. */
+#define _JLU3_INIT(_h, _size) (0xdeadbeef + ((uint32_t)(_size)) + (_h))
+
+/* -------------------------------------------------------------------- */
+/*
+ * _JLU3_MIX -- mix 3 32-bit values reversibly.
+ *
+ * This is reversible, so any information in (a,b,c) before _JLU3_MIX() is
+ * still in (a,b,c) after _JLU3_MIX().
+ *
+ * If four pairs of (a,b,c) inputs are run through _JLU3_MIX(), or through
+ * _JLU3_MIX() in reverse, there are at least 32 bits of the output that
+ * are sometimes the same for one pair and different for another pair.
+ * This was tested for:
+ * * pairs that differed by one bit, by two bits, in any combination
+ * of top bits of (a,b,c), or in any combination of bottom bits of
+ * (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ * is commonly produced by subtraction) look like a single 1-bit
+ * difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ * all zero plus a counter that starts at zero.
+ *
+ * Some k values for my "a-=c; a^=ROTL32(c,k); c+=b;" arrangement that
+ * satisfy this are
+ * 4 6 8 16 19 4
+ * 9 15 3 18 27 15
+ * 14 9 3 7 17 3
+ * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+ * for "differ" defined as + with a one-bit base and a two-bit delta. I
+ * used http://burtleburtle.net/bob/hash/avalanche.html to choose
+ * the operations, constants, and arrangements of the variables.
+ *
+ * This does not achieve avalanche. There are input bits of (a,b,c)
+ * that fail to affect some output bits of (a,b,c), especially of a. The
+ * most thoroughly mixed value is c, but it doesn't really even achieve
+ * avalanche in c.
+ *
+ * This allows some parallelism. Read-after-writes are good at doubling
+ * the number of bits affected, so the goal of mixing pulls in the opposite
+ * direction as the goal of parallelism. I did what I could. Rotates
+ * seem to cost as much as shifts on every machine I could lay my hands
+ * on, and rotates are much kinder to the top and bottom bits, so I used
+ * rotates.
+ */
+/* -------------------------------------------------------------------- */
+#define _JLU3_MIX(a,b,c) \
+{ \
+ a -= c; a ^= ROTL32(c, 4); c += b; \
+ b -= a; b ^= ROTL32(a, 6); a += c; \
+ c -= b; c ^= ROTL32(b, 8); b += a; \
+ a -= c; a ^= ROTL32(c,16); c += b; \
+ b -= a; b ^= ROTL32(a,19); a += c; \
+ c -= b; c ^= ROTL32(b, 4); b += a; \
+}
+
+/* -------------------------------------------------------------------- */
+/**
+ * _JLU3_FINAL -- final mixing of 3 32-bit values (a,b,c) into c
+ *
+ * Pairs of (a,b,c) values differing in only a few bits will usually
+ * produce values of c that look totally different. This was tested for
+ * * pairs that differed by one bit, by two bits, in any combination
+ * of top bits of (a,b,c), or in any combination of bottom bits of
+ * (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ * is commonly produced by subtraction) look like a single 1-bit
+ * difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ * all zero plus a counter that starts at zero.
+ *
+ * These constants passed:
+ * 14 11 25 16 4 14 24
+ * 12 14 25 16 4 14 24
+ * and these came close:
+ * 4 8 15 26 3 22 24
+ * 10 8 15 26 3 22 24
+ * 11 8 15 26 3 22 24
+ */
+/* -------------------------------------------------------------------- */
+#define _JLU3_FINAL(a,b,c) \
+{ \
+ c ^= b; c -= ROTL32(b,14); \
+ a ^= c; a -= ROTL32(c,11); \
+ b ^= a; b -= ROTL32(a,25); \
+ c ^= b; c -= ROTL32(b,16); \
+ a ^= c; a -= ROTL32(c,4); \
+ b ^= a; b -= ROTL32(a,14); \
+ c ^= b; c -= ROTL32(b,24); \
+}
+
+#if defined(_JLU3_jlu32w)
+uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size);
+/* -------------------------------------------------------------------- */
+/**
+ * This works on all machines. To be useful, it requires
+ * -- that the key be an array of uint32_t's, and
+ * -- that the size be the number of uint32_t's in the key
+ *
+ * The function jlu32w() is identical to jlu32l() on little-endian
+ * machines, and identical to jlu32b() on big-endian machines,
+ * except that the size has to be measured in uint32_ts rather than in
+ * bytes. jlu32l() is more complicated than jlu32w() only because
+ * jlu32l() has to dance around fitting the key bytes into registers.
+ *
+ * @param h the previous hash, or an arbitrary value
+ * @param *k the key, an array of uint32_t values
+ * @param size the size of the key, in uint32_ts
+ * @return the lookup3 hash
+ */
+/* -------------------------------------------------------------------- */
+uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size)
+{
+ uint32_t a = _JLU3_INIT(h, (size * sizeof(*k)));
+ uint32_t b = a;
+ uint32_t c = a;
+
+ if (k == NULL)
+ goto exit;
+
+ /*----------------------------------------------- handle most of the key */
+ while (size > 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ _JLU3_MIX(a,b,c);
+ size -= 3;
+ k += 3;
+ }
+
+ /*----------------------------------------- handle the last 3 uint32_t's */
+ switch (size) {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ _JLU3_FINAL(a,b,c);
+ /* fallthrough */
+ case 0:
+ break;
+ }
+ /*---------------------------------------------------- report the result */
+exit:
+ return c;
+}
+#endif /* defined(_JLU3_jlu32w) */
+
+#if defined(_JLU3_jlu32l)
+uint32_t jlu32l(uint32_t h, const void *key, size_t size);
+/* -------------------------------------------------------------------- */
+/*
+ * jlu32l() -- hash a variable-length key into a 32-bit value
+ * h : can be any 4-byte value
+ * k : the key (the unaligned variable-length array of bytes)
+ * size : the size of the key, counting by bytes
+ * Returns a 32-bit value. Every bit of the key affects every bit of
+ * the return value. Two keys differing by one or two bits will have
+ * totally different hash values.
+ *
+ * The best hash table sizes are powers of 2. There is no need to do
+ * mod a prime (mod is sooo slow!). If you need less than 32 bits,
+ * use a bitmask. For example, if you need only 10 bits, do
+ * h = (h & hashmask(10));
+ * In which case, the hash table should have hashsize(10) elements.
+ *
+ * If you are hashing n strings (uint8_t **)k, do it like this:
+ * for (i=0, h=0; i 12) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 3;
+ }
+
+ /*------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticeably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch (size) {
+ case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += k[1]&0xffffff; a+=k[0]; break;
+ case 6: b += k[1]&0xffff; a+=k[0]; break;
+ case 5: b += k[1]&0xff; a+=k[0]; break;
+ case 4: a += k[0]; break;
+ case 3: a += k[0]&0xffffff; break;
+ case 2: a += k[0]&0xffff; break;
+ case 1: a += k[0]&0xff; break;
+ case 0: goto exit;
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch (size) {
+ case 12: c += k[2]; b+=k[1]; a+=k[0] break;
+ case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
+ case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
+ case 9: c += k8[8]; /* fallthrough */
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
+ case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
+ case 5: b += k8[4]; /* fallthrough */
+ case 4: a += k[0]; break;
+ case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
+ case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
+ case 1: a += k8[0]; break;
+ case 0: goto exit;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*----------- all but last block: aligned reads and different mixing */
+ while (size > 12) {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 6;
+ }
+
+ /*------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch (size) {
+ case 12:
+ c += k[4]+(((uint32_t)k[5])<<16);
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11:
+ c += ((uint32_t)k8[10])<<16;
+ /* fallthrough */
+ case 10:
+ c += (uint32_t)k[4];
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9:
+ c += (uint32_t)k8[8];
+ /* fallthrough */
+ case 8:
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7:
+ b += ((uint32_t)k8[6])<<16;
+ /* fallthrough */
+ case 6:
+ b += (uint32_t)k[2];
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5:
+ b += (uint32_t)k8[4];
+ /* fallthrough */
+ case 4:
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3:
+ a += ((uint32_t)k8[2])<<16;
+ /* fallthrough */
+ case 2:
+ a += (uint32_t)k[0];
+ break;
+ case 1:
+ a += (uint32_t)k8[0];
+ break;
+ case 0:
+ goto exit;
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*----------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (size > 12) {
+ a += (uint32_t)k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += (uint32_t)k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += (uint32_t)k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 12;
+ }
+
+ /*---------------------------- last block: affect all 32 bits of (c) */
+ switch (size) {
+ case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
+ case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
+ case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
+ case 9: c += (uint32_t)k[8]; /* fallthrough */
+ case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
+ case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
+ case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
+ case 5: b += (uint32_t)k[4]; /* fallthrough */
+ case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
+ case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
+ case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
+ case 1: a += (uint32_t)k[0];
+ break;
+ case 0:
+ goto exit;
+ }
+ }
+
+ _JLU3_FINAL(a,b,c);
+
+exit:
+ return c;
+}
+#endif /* defined(_JLU3_jlu32l) */
+
+#if defined(_JLU3_jlu32lpair)
+/**
+ * jlu32lpair: return 2 32-bit hash values.
+ *
+ * This is identical to jlu32l(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ *
+ * @param h the previous hash, or an arbitrary value
+ * @param *key the key, an array of uint8_t values
+ * @param size the size of the key in bytes
+ * @retval *pc, IN: primary initval, OUT: primary hash
+ * *retval *pb IN: secondary initval, OUT: secondary hash
+ */
+void jlu32lpair(const void *key, size_t size, uint32_t *pc, uint32_t *pb)
+{
+ union { const void *ptr; size_t i; } u;
+ uint32_t a = _JLU3_INIT(*pc, size);
+ uint32_t b = a;
+ uint32_t c = a;
+
+ if (key == NULL)
+ goto exit;
+
+ c += *pb; /* Add the secondary hash. */
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+#ifdef VALGRIND
+ const uint8_t *k8;
+#endif
+
+ /*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (size > (size_t)12) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 3;
+ }
+ /*------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticeably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch (size) {
+ case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += k[1]&0xffffff; a+=k[0]; break;
+ case 6: b += k[1]&0xffff; a+=k[0]; break;
+ case 5: b += k[1]&0xff; a+=k[0]; break;
+ case 4: a += k[0]; break;
+ case 3: a += k[0]&0xffffff; break;
+ case 2: a += k[0]&0xffff; break;
+ case 1: a += k[0]&0xff; break;
+ case 0: goto exit;
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch (size) {
+ case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
+ case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
+ case 9: c += k8[8]; /* fallthrough */
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
+ case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
+ case 5: b += k8[4]; /* fallthrough */
+ case 4: a += k[0]; break;
+ case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
+ case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
+ case 1: a += k8[0]; break;
+ case 0: goto exit;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*----------- all but last block: aligned reads and different mixing */
+ while (size > (size_t)12) {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 6;
+ }
+
+ /*------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch (size) {
+ case 12:
+ c += k[4]+(((uint32_t)k[5])<<16);
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11:
+ c += ((uint32_t)k8[10])<<16;
+ /* fallthrough */
+ case 10:
+ c += k[4];
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9:
+ c += k8[8];
+ /* fallthrough */
+ case 8:
+ b += k[2]+(((uint32_t)k[3])<<16);
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7:
+ b += ((uint32_t)k8[6])<<16;
+ /* fallthrough */
+ case 6:
+ b += k[2];
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5:
+ b += k8[4];
+ /* fallthrough */
+ case 4:
+ a += k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3:
+ a += ((uint32_t)k8[2])<<16;
+ /* fallthrough */
+ case 2:
+ a += k[0];
+ break;
+ case 1:
+ a += k8[0];
+ break;
+ case 0:
+ goto exit;
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*----------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (size > (size_t)12) {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 12;
+ }
+
+ /*---------------------------- last block: affect all 32 bits of (c) */
+ switch (size) {
+ case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
+ case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
+ case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
+ case 9: c += k[8]; /* fallthrough */
+ case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
+ case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
+ case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
+ case 5: b += k[4]; /* fallthrough */
+ case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
+ case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
+ case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
+ case 1: a += k[0];
+ break;
+ case 0:
+ goto exit;
+ }
+ }
+
+ _JLU3_FINAL(a,b,c);
+
+exit:
+ *pc = c;
+ *pb = b;
+ return;
+}
+#endif /* defined(_JLU3_jlu32lpair) */
+
+#if defined(_JLU3_jlu32b)
+uint32_t jlu32b(uint32_t h, const void *key, size_t size);
+/*
+ * jlu32b():
+ * This is the same as jlu32w() on big-endian machines. It is different
+ * from jlu32l() on all machines. jlu32b() takes advantage of
+ * big-endian byte ordering.
+ *
+ * @param h the previous hash, or an arbitrary value
+ * @param *k the key, an array of uint8_t values
+ * @param size the size of the key
+ * @return the lookup3 hash
+ */
+uint32_t jlu32b(uint32_t h, const void *key, size_t size)
+{
+ union { const void *ptr; size_t i; } u;
+ uint32_t a = _JLU3_INIT(h, size);
+ uint32_t b = a;
+ uint32_t c = a;
+
+ if (key == NULL)
+ return h;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+#ifdef VALGRIND
+ const uint8_t *k8;
+#endif
+
+ /*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (size > 12) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 3;
+ }
+
+ /*------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticeably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch (size) {
+ case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c += k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c += k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9: c += k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += k[1]&0xffffff00; a+=k[0]; break;
+ case 6: b += k[1]&0xffff0000; a+=k[0]; break;
+ case 5: b += k[1]&0xff000000; a+=k[0]; break;
+ case 4: a += k[0]; break;
+ case 3: a += k[0]&0xffffff00; break;
+ case 2: a += k[0]&0xffff0000; break;
+ case 1: a += k[0]&0xff000000; break;
+ case 0: goto exit;
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch (size) { /* all the case statements fall through */
+ case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c += ((uint32_t)k8[10])<<8; /* fallthrough */
+ case 10: c += ((uint32_t)k8[9])<<16; /* fallthrough */
+ case 9: c += ((uint32_t)k8[8])<<24; /* fallthrough */
+ case 8: b += k[1]; a+=k[0]; break;
+ case 7: b += ((uint32_t)k8[6])<<8; /* fallthrough */
+ case 6: b += ((uint32_t)k8[5])<<16; /* fallthrough */
+ case 5: b += ((uint32_t)k8[4])<<24; /* fallthrough */
+ case 4: a += k[0]; break;
+ case 3: a += ((uint32_t)k8[2])<<8; /* fallthrough */
+ case 2: a += ((uint32_t)k8[1])<<16; /* fallthrough */
+ case 1: a += ((uint32_t)k8[0])<<24; break;
+ case 0: goto exit;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*----------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (size > 12) {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ _JLU3_MIX(a,b,c);
+ size -= 12;
+ k += 12;
+ }
+
+ /*---------------------------- last block: affect all 32 bits of (c) */
+ switch (size) { /* all the case statements fall through */
+ case 12: c += k[11]; /* fallthrough */
+ case 11: c += ((uint32_t)k[10])<<8; /* fallthrough */
+ case 10: c += ((uint32_t)k[9])<<16; /* fallthrough */
+ case 9: c += ((uint32_t)k[8])<<24; /* fallthrough */
+ case 8: b += k[7]; /* fallthrough */
+ case 7: b += ((uint32_t)k[6])<<8; /* fallthrough */
+ case 6: b += ((uint32_t)k[5])<<16; /* fallthrough */
+ case 5: b += ((uint32_t)k[4])<<24; /* fallthrough */
+ case 4: a += k[3]; /* fallthrough */
+ case 3: a += ((uint32_t)k[2])<<8; /* fallthrough */
+ case 2: a += ((uint32_t)k[1])<<16; /* fallthrough */
+ case 1: a += ((uint32_t)k[0])<<24; /* fallthrough */
+ break;
+ case 0:
+ goto exit;
+ }
+ }
+
+ _JLU3_FINAL(a,b,c);
+
+exit:
+ return c;
+}
+#endif /* defined(_JLU3_jlu32b) */
+
+#if defined(_JLU3_SELFTEST)
+
+/* used for timings */
+static void driver1(void)
+{
+ uint8_t buf[256];
+ uint32_t i;
+ uint32_t h=0;
+ time_t a,z;
+
+ time(&a);
+ for (i=0; i<256; ++i) buf[i] = 'x';
+ for (i=0; i<1; ++i) {
+ h = jlu32l(h, &buf[0], sizeof(buf[0]));
+ }
+ time(&z);
+ if (z-a > 0) printf("time %d %.8x\n", (int)(z-a), h);
+}
+
+/* check that every input bit changes every output bit half the time */
+#define HASHSTATE 1
+#define HASHLEN 1
+#define MAXPAIR 60
+#define MAXLEN 70
+static void driver2(void)
+{
+ uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
+ uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
+ uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
+ uint32_t x[HASHSTATE],y[HASHSTATE];
+ uint32_t hlen;
+
+ printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
+ for (hlen=0; hlen < MAXLEN; ++hlen) {
+ z=0;
+ for (i=0; i>(8-j));
+ c[0] = jlu32l(m, a, hlen);
+ b[i] ^= ((k+1)<>(8-j));
+ d[0] = jlu32l(m, b, hlen);
+ /* check every bit is 1, 0, set, and not set at least once */
+ for (l=0; lz) z=k;
+ if (k == MAXPAIR) {
+ printf("Some bit didn't change: ");
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
+ e[0],f[0],g[0],h[0],x[0],y[0]);
+ printf("i %u j %u m %u len %u\n", i, j, m, hlen);
+ }
+ if (z == MAXPAIR) goto done;
+ }
+ }
+ }
+ done:
+ if (z < MAXPAIR) {
+ printf("Mix success %2u bytes %2u initvals ",i,m);
+ printf("required %u trials\n", z/2);
+ }
+ }
+ printf("\n");
+}
+
+/* Check for reading beyond the end of the buffer and alignment problems */
+static void driver3(void)
+{
+ uint8_t buf[MAXLEN+20], *b;
+ uint32_t len;
+ uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
+ uint32_t h;
+ uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
+ uint32_t i;
+ uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
+ uint32_t j;
+ uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
+ uint32_t ref,x,y;
+ uint8_t *p;
+ uint32_t m = 13;
+
+ printf("Endianness. These lines should all be the same (for values filled in):\n");
+ printf("%.8x %.8x %.8x\n",
+ jlu32w(m, (const uint32_t *)q, (sizeof(q)-1)/4),
+ jlu32w(m, (const uint32_t *)q, (sizeof(q)-5)/4),
+ jlu32w(m, (const uint32_t *)q, (sizeof(q)-9)/4));
+ p = q;
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
+ jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
+ jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
+ jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
+ jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
+ jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
+ p = &qq[1];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
+ jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
+ jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
+ jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
+ jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
+ jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
+ p = &qqq[2];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
+ jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
+ jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
+ jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
+ jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
+ jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
+ p = &qqqq[3];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
+ jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
+ jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
+ jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
+ jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
+ jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
+ printf("\n");
+ for (h=0, b=buf+1; h<8; ++h, ++b) {
+ for (i=0; i
-#endif
#include
+#include
+#include
+#include
-#include "findme.h"
#include "poptint.h"
-#ifndef DBL_EPSILON
-#define DBL_EPSILON 2.2204460492503131e-16
+#ifdef HAVE_STDALIGN_H
+#include
+#define ALIGNOF(x) alignof(x)
+#elif defined __GNUC__
+#define ALIGNOF(x) __alignof__(x)
+#else
+#define ALIGNOF(x) sizeof(x)
#endif
#ifdef MYDEBUG
-/*@unchecked@*/
int _popt_debug = 0;
#endif
-#if !defined(HAVE_STRERROR) && !defined(__LCLINT__)
+unsigned int _poptArgMask = POPT_ARG_MASK;
+unsigned int _poptGroupMask = POPT_GROUP_MASK;
+
+#if !defined(HAVE_STRERROR)
static char * strerror(int errno)
{
extern int sys_nerr;
@@ -41,7 +48,6 @@ static char * strerror(int errno)
#endif
#ifdef MYDEBUG
-/*@unused@*/
static void prtcon(const char *msg, poptContext con)
{
if (msg) fprintf(stderr, "%s", msg);
@@ -60,119 +66,93 @@ void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
con->execPath = _free(con->execPath);
con->execPath = xstrdup(path);
con->execAbsolute = allowAbsolute;
- /*@-nullstate@*/ /* LCL: con->execPath not NULL */
return;
- /*@=nullstate@*/
}
static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
- /*@globals internalState@*/
- /*@modifies internalState@*/
{
if (opt != NULL)
for (; opt->longName || opt->shortName || opt->arg; opt++) {
- if (opt->arg == NULL) continue; /* XXX program error. */
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
- void * arg = opt->arg;
-/*@-branchstate@*/
- /* XXX sick hack to preserve pretense of ABI. */
- if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
-/*@=branchstate@*/
- /* Recurse on included sub-tables. */
- invokeCallbacksPRE(con, arg);
- } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
- (opt->argInfo & POPT_CBFLAG_PRE))
- { /*@-castfcnptr@*/
- poptCallbackType cb = (poptCallbackType)opt->arg;
- /*@=castfcnptr@*/
- /* Perform callback. */
- /*@-noeffectuncon @*/
- cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
- /*@=noeffectuncon @*/
+ poptArg arg = { .ptr = opt->arg };
+ if (arg.ptr)
+ switch (poptArgType(opt)) {
+ case POPT_ARG_INCLUDE_TABLE: /* Recurse on included sub-tables. */
+ poptSubstituteHelpI18N(arg.opt); /* XXX side effects */
+ invokeCallbacksPRE(con, arg.opt);
+ break;
+ case POPT_ARG_CALLBACK: /* Perform callback. */
+ if (!CBF_ISSET(opt, PRE))
+ break;
+ arg.cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
+ break;
}
}
}
static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
- /*@globals internalState@*/
- /*@modifies internalState@*/
{
if (opt != NULL)
for (; opt->longName || opt->shortName || opt->arg; opt++) {
- if (opt->arg == NULL) continue; /* XXX program error. */
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
- void * arg = opt->arg;
-/*@-branchstate@*/
- /* XXX sick hack to preserve pretense of ABI. */
- if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
-/*@=branchstate@*/
- /* Recurse on included sub-tables. */
- invokeCallbacksPOST(con, arg);
- } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
- (opt->argInfo & POPT_CBFLAG_POST))
- { /*@-castfcnptr@*/
- poptCallbackType cb = (poptCallbackType)opt->arg;
- /*@=castfcnptr@*/
- /* Perform callback. */
- /*@-noeffectuncon @*/
- cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
- /*@=noeffectuncon @*/
+ poptArg arg = { .ptr = opt->arg };
+ if (arg.ptr)
+ switch (poptArgType(opt)) {
+ case POPT_ARG_INCLUDE_TABLE: /* Recurse on included sub-tables. */
+ poptSubstituteHelpI18N(arg.opt); /* XXX side effects */
+ invokeCallbacksPOST(con, arg.opt);
+ break;
+ case POPT_ARG_CALLBACK: /* Perform callback. */
+ if (!CBF_ISSET(opt, POST))
+ break;
+ arg.cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
+ break;
}
}
}
static void invokeCallbacksOPTION(poptContext con,
- const struct poptOption * opt,
- const struct poptOption * myOpt,
- /*@null@*/ const void * myData, int shorty)
- /*@globals internalState@*/
- /*@modifies internalState@*/
+ const struct poptOption * opt,
+ const struct poptOption * myOpt,
+ const void * myData, int shorty)
{
const struct poptOption * cbopt = NULL;
+ poptArg cbarg = { .ptr = NULL };
if (opt != NULL)
for (; opt->longName || opt->shortName || opt->arg; opt++) {
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
- void * arg = opt->arg;
-/*@-branchstate@*/
- /* XXX sick hack to preserve pretense of ABI. */
- if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
-/*@=branchstate@*/
- /* Recurse on included sub-tables. */
- if (opt->arg != NULL) /* XXX program error */
+ poptArg arg = { .ptr = opt->arg };
+ switch (poptArgType(opt)) {
+ case POPT_ARG_INCLUDE_TABLE: /* Recurse on included sub-tables. */
+ poptSubstituteHelpI18N(arg.opt); /* XXX side effects */
+ if (opt->arg != NULL)
invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
- } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
- !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
- /* Save callback info. */
+ break;
+ case POPT_ARG_CALLBACK: /* Save callback info. */
+ if (CBF_ISSET(opt, SKIPOPTION))
+ break;
cbopt = opt;
- } else if (cbopt != NULL &&
- ((myOpt->shortName && opt->shortName && shorty &&
- myOpt->shortName == opt->shortName) ||
- (myOpt->longName && opt->longName &&
- /*@-nullpass@*/ /* LCL: opt->longName != NULL */
+ cbarg.ptr = opt->arg;
+ break;
+ default: /* Perform callback on matching option. */
+ if (cbopt == NULL || cbarg.cb == NULL)
+ break;
+ if ((myOpt->shortName && opt->shortName && shorty &&
+ myOpt->shortName == opt->shortName)
+ || (myOpt->longName != NULL && opt->longName != NULL &&
!strcmp(myOpt->longName, opt->longName)))
- /*@=nullpass@*/
- )
- { /*@-castfcnptr@*/
- poptCallbackType cb = (poptCallbackType)cbopt->arg;
- /*@=castfcnptr@*/
- const void * cbData = (cbopt->descrip ? cbopt->descrip : myData);
- /* Perform callback. */
- if (cb != NULL) { /* XXX program error */
- /*@-noeffectuncon @*/
- cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
- con->os->nextArg, cbData);
- /*@=noeffectuncon @*/
+ { const void *cbData = (cbopt->descrip ? cbopt->descrip : myData);
+ cbarg.cb(con, POPT_CALLBACK_REASON_OPTION,
+ myOpt, con->os->nextArg, cbData);
+ /* Terminate (unless explcitly continuing). */
+ if (!CBF_ISSET(cbopt, CONTINUE))
+ return;
}
- /* Terminate (unless explcitly continuing). */
- if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
- return;
+ break;
}
}
}
poptContext poptGetContext(const char * name, int argc, const char ** argv,
- const struct poptOption * options, int flags)
+ const struct poptOption * options, unsigned int flags)
{
poptContext con = malloc(sizeof(*con));
@@ -181,58 +161,44 @@ poptContext poptGetContext(const char * name, int argc, const char ** argv,
con->os = con->optionStack;
con->os->argc = argc;
- /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
con->os->argv = argv;
- /*@=dependenttrans =assignexpose@*/
con->os->argb = NULL;
if (!(flags & POPT_CONTEXT_KEEP_FIRST))
- con->os->next = 1; /* skip argv[0] */
+ con->os->next = 1; /* skip argv[0] */
- con->leftovers = calloc( (argc + 1), sizeof(*con->leftovers) );
- /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
+ con->leftovers = calloc( (size_t)(argc + 1), sizeof(*con->leftovers) );
+ con->allocLeftovers = argc + 1;
con->options = options;
- /*@=dependenttrans =assignexpose@*/
con->aliases = NULL;
con->numAliases = 0;
con->flags = flags;
con->execs = NULL;
con->numExecs = 0;
+ con->execFail = NULL;
con->finalArgvAlloced = argc * 2;
- con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+ con->finalArgv = calloc( (size_t)con->finalArgvAlloced, sizeof(*con->finalArgv) );
con->execAbsolute = 1;
con->arg_strip = NULL;
if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
con->flags |= POPT_CONTEXT_POSIXMEHARDER;
- if (name) {
- size_t bufsize = strlen(name) + 1;
- char * t = malloc(bufsize);
- if (t) {
- strlcpy(t, name, bufsize);
- con->appName = t;
- }
- }
+ if (name)
+ con->appName = xstrdup(name);
- /*@-internalglobs@*/
invokeCallbacksPRE(con, con->options);
- /*@=internalglobs@*/
return con;
}
-static void cleanOSE(/*@special@*/ struct optionStackEntry *os)
- /*@uses os @*/
- /*@releases os->nextArg, os->argv, os->argb @*/
- /*@modifies os @*/
+static void cleanOSE(struct optionStackEntry *os)
{
os->nextArg = _free(os->nextArg);
os->argv = _free(os->argv);
os->argb = PBM_FREE(os->argb);
}
-/*@-boundswrite@*/
void poptResetContext(poptContext con)
{
int i;
@@ -244,36 +210,34 @@ void poptResetContext(poptContext con)
con->os->argb = PBM_FREE(con->os->argb);
con->os->currAlias = NULL;
con->os->nextCharArg = NULL;
- con->os->nextArg = NULL;
- con->os->next = 1; /* skip argv[0] */
+ con->os->nextArg = _free(con->os->nextArg);
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+ else
+ con->os->next = 0;
+ for (i = 0; i < con->numLeftovers; i++) {
+ con->leftovers[i] = _free(con->leftovers[i]);
+ }
con->numLeftovers = 0;
con->nextLeftover = 0;
con->restLeftover = 0;
con->doExec = NULL;
+ con->execFail = _free(con->execFail);
if (con->finalArgv != NULL)
for (i = 0; i < con->finalArgvCount; i++) {
- /*@-unqualifiedtrans@*/ /* FIX: typedef double indirection. */
con->finalArgv[i] = _free(con->finalArgv[i]);
- /*@=unqualifiedtrans@*/
}
con->finalArgvCount = 0;
con->arg_strip = PBM_FREE(con->arg_strip);
- /*@-nullstate@*/ /* FIX: con->finalArgv != NULL */
return;
- /*@=nullstate@*/
}
-/*@=boundswrite@*/
/* Only one of longName, shortName should be set, not both. */
-/*@-boundswrite@*/
-static int handleExec(/*@special@*/ poptContext con,
- /*@null@*/ const char * longName, char shortName)
- /*@uses con->execs, con->numExecs, con->flags, con->doExec,
- con->finalArgv, con->finalArgvAlloced, con->finalArgvCount @*/
- /*@modifies con @*/
+static int handleExec(poptContext con,
+ const char * longName, char shortName)
{
poptItem item;
int i;
@@ -311,40 +275,75 @@ static int handleExec(/*@special@*/ poptContext con,
i = con->finalArgvCount++;
if (con->finalArgv != NULL) /* XXX can't happen */
- { size_t bufsize = (longName ? strlen(longName) : 0) + 3;
- char *s = malloc(bufsize);
+ { char *s = malloc((longName ? strlen(longName) : 0) + sizeof("--"));
if (s != NULL) { /* XXX can't happen */
+ con->finalArgv[i] = s;
+ *s++ = '-';
if (longName)
- snprintf(s, bufsize, "--%s", longName);
+ s = stpcpy( stpcpy(s, "-"), longName);
else
- snprintf(s, bufsize, "-%c", shortName);
- con->finalArgv[i] = s;
+ *s++ = shortName;
+ *s = '\0';
} else
con->finalArgv[i] = NULL;
}
- /*@-nullstate@*/ /* FIX: con->finalArgv[] == NULL */
return 1;
- /*@=nullstate@*/
}
-/*@=boundswrite@*/
+
+/**
+ * Compare long option for equality, adjusting for POPT_ARGFLAG_TOGGLE.
+ * @param opt option
+ * @param longName arg option
+ * @param longNameLen arg option length
+ * @return does long option match?
+ */
+static int
+longOptionStrcmp(const struct poptOption * opt,
+ const char * longName, size_t longNameLen)
+{
+ const char * optLongName = opt->longName;
+ int rc;
+
+ if (optLongName == NULL || longName == NULL) /* XXX can't heppen */
+ return 0;
+
+ if (F_ISSET(opt, TOGGLE)) {
+ if (optLongName[0] == 'n' && optLongName[1] == 'o') {
+ optLongName += sizeof("no") - 1;
+ if (optLongName[0] == '-')
+ optLongName++;
+ }
+ if (longName[0] == 'n' && longName[1] == 'o') {
+ longName += sizeof("no") - 1;
+ longNameLen -= sizeof("no") - 1;
+ if (longName[0] == '-') {
+ longName++;
+ longNameLen--;
+ }
+ }
+ }
+ rc = (int)(strlen(optLongName) == longNameLen);
+ if (rc)
+ rc = (int)(strncmp(optLongName, longName, longNameLen) == 0);
+ return rc;
+}
/* Only one of longName, shortName may be set at a time */
-static int handleAlias(/*@special@*/ poptContext con,
- /*@null@*/ const char * longName, char shortName,
- /*@exposed@*/ /*@null@*/ const char * nextCharArg)
- /*@uses con->aliases, con->numAliases, con->optionStack, con->os,
- con->os->currAlias, con->os->currAlias->option.longName @*/
- /*@modifies con @*/
+static int handleAlias(poptContext con,
+ const char * longName, size_t longNameLen,
+ char shortName,
+ const char * nextArg)
{
poptItem item = con->os->currAlias;
int rc;
int i;
if (item) {
- if (longName && (item->option.longName &&
- !strcmp(longName, item->option.longName)))
+ if (longName && item->option.longName != NULL
+ && longOptionStrcmp(&item->option, longName, longNameLen))
return 0;
+ else
if (shortName && shortName == item->option.shortName)
return 0;
}
@@ -354,10 +353,12 @@ static int handleAlias(/*@special@*/ poptContext con,
for (i = con->numAliases - 1; i >= 0; i--) {
item = con->aliases + i;
- if (longName && !(item->option.longName &&
- !strcmp(longName, item->option.longName)))
- continue;
- else if (shortName != item->option.shortName)
+ if (longName) {
+ if (item->option.longName == NULL)
+ continue;
+ if (!longOptionStrcmp(&item->option, longName, longNameLen))
+ continue;
+ } else if (shortName != item->option.shortName)
continue;
break;
}
@@ -366,10 +367,8 @@ static int handleAlias(/*@special@*/ poptContext con,
if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
return POPT_ERROR_OPTSTOODEEP;
-/*@-boundsread@*/
- if (nextCharArg && *nextCharArg)
- con->os->nextCharArg = nextCharArg;
-/*@=boundsread@*/
+ if (longName == NULL && nextArg != NULL && *nextArg != '\0')
+ con->os->nextCharArg = nextArg;
con->os++;
con->os->next = 0;
@@ -377,21 +376,82 @@ static int handleAlias(/*@special@*/ poptContext con,
con->os->nextArg = NULL;
con->os->nextCharArg = NULL;
con->os->currAlias = con->aliases + i;
- rc = poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
- &con->os->argc, &con->os->argv);
+ { const char ** av;
+ int ac = con->os->currAlias->argc;
+ /* Append --foo=bar arg to alias argv array (if present). */
+ if (longName && nextArg != NULL && *nextArg != '\0') {
+ av = malloc((ac + 1 + 1) * sizeof(*av));
+ if (av != NULL) { /* XXX won't happen. */
+ for (i = 0; i < ac; i++) {
+ av[i] = con->os->currAlias->argv[i];
+ }
+ av[ac++] = nextArg;
+ av[ac] = NULL;
+ } else /* XXX revert to old popt behavior if malloc fails. */
+ av = con->os->currAlias->argv;
+ } else
+ av = con->os->currAlias->argv;
+ rc = poptDupArgv(ac, av, &con->os->argc, &con->os->argv);
+ if (av != NULL && av != con->os->currAlias->argv)
+ free(av);
+ }
con->os->argb = NULL;
return (rc ? rc : 1);
}
-/*@-bounds -boundswrite @*/
+/**
+ * Return absolute path to executable by searching PATH.
+ * @param argv0 name of executable
+ * @return (malloc'd) absolute path to executable (or NULL)
+ */
+static
+const char * findProgramPath(const char * argv0)
+{
+ char *path = NULL, *s = NULL, *se;
+ char *t = NULL;
+
+ if (argv0 == NULL) return NULL; /* XXX can't happen */
+
+ /* If there is a / in argv[0], it has to be an absolute path. */
+ /* XXX Hmmm, why not if (argv0[0] == '/') ... instead? */
+ if (strchr(argv0, '/'))
+ return xstrdup(argv0);
+
+ if ((path = getenv("PATH")) == NULL || (path = xstrdup(path)) == NULL)
+ return NULL;
+
+ /* The return buffer in t is big enough for any path. */
+ if ((t = malloc(strlen(path) + strlen(argv0) + sizeof("/"))) != NULL)
+ for (s = path; s && *s; s = se) {
+
+ /* Snip PATH element into [s,se). */
+ if ((se = strchr(s, ':')))
+ *se++ = '\0';
+
+ /* Append argv0 to PATH element. */
+ (void) stpcpy(stpcpy(stpcpy(t, s), "/"), argv0);
+
+ /* If file is executable, bingo! */
+ if (!access(t, X_OK))
+ break;
+ }
+
+ /* If no executable was found in PATH, return NULL. */
+ if (!(s && *s) && t != NULL)
+ t = _free(t);
+ path = _free(path);
+
+ return t;
+}
+
static int execCommand(poptContext con)
- /*@globals internalState @*/
- /*@modifies internalState @*/
{
poptItem item = con->doExec;
- const char ** argv;
+ poptArgv argv = NULL;
int argc = 0;
+ int rc;
+ int ec = POPT_ERROR_ERRNO;
if (item == NULL) /*XXX can't happen*/
return POPT_ERROR_NOARG;
@@ -405,13 +465,17 @@ static int execCommand(poptContext con)
if (argv == NULL) return POPT_ERROR_MALLOC;
if (!strchr(item->argv[0], '/') && con->execPath != NULL) {
- size_t bufsize = strlen(con->execPath) + strlen(item->argv[0]) + sizeof "/";
- char *s = alloca(bufsize);
- snprintf(s, bufsize, "%s/%s", con->execPath, item->argv[0]);
+ char *s = malloc(strlen(con->execPath) + strlen(item->argv[0]) + sizeof("/"));
+ if (s)
+ (void)stpcpy(stpcpy(stpcpy(s, con->execPath), "/"), item->argv[0]);
+
argv[argc] = s;
} else
argv[argc] = findProgramPath(item->argv[0]);
- if (argv[argc++] == NULL) return POPT_ERROR_NOARG;
+ if (argv[argc++] == NULL) {
+ ec = POPT_ERROR_NOARG;
+ goto exit;
+ }
if (item->argc > 1) {
memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
@@ -431,12 +495,11 @@ static int execCommand(poptContext con)
argv[argc] = NULL;
- {
-#ifdef __hpux
- int rc = setresgid(getgid(), getgid(),-1);
- if (rc) return POPT_ERROR_ERRNO;
+#if defined(hpux) || defined(__hpux)
+ rc = setresgid(getgid(), getgid(),-1);
+ if (rc) goto exit;
rc = setresuid(getuid(), getuid(),-1);
- if (rc) return POPT_ERROR_ERRNO;
+ if (rc) goto exit;
#else
/*
* XXX " ... on BSD systems setuid() should be preferred over setreuid()"
@@ -444,27 +507,27 @@ static int execCommand(poptContext con)
* XXX from Norbert Warmuth
*/
#if defined(HAVE_SETUID)
- int rc = setgid(getgid());
- if (rc) return POPT_ERROR_ERRNO;
+ rc = setgid(getgid());
+ if (rc) goto exit;
rc = setuid(getuid());
- if (rc) return POPT_ERROR_ERRNO;
+ if (rc) goto exit;
#elif defined (HAVE_SETREUID)
- int rc = setregid(getgid(), getgid());
- if (rc) return POPT_ERROR_ERRNO;
+ rc = setregid(getgid(), getgid());
+ if (rc) goto exit;
rc = setreuid(getuid(), getuid());
- if (rc) return POPT_ERROR_ERRNO;
+ if (rc) goto exit;
#else
- ; /* Can't drop privileges */
+ /* refuse to exec if we cannot drop suid/sgid privileges */
+ if (getuid() != geteuid() || getgid() != getegid()) {
+ errno = ENOTSUP;
+ goto exit;
+ }
#endif
#endif
- }
-
- if (argv[0] == NULL)
- return POPT_ERROR_NOARG;
#ifdef MYDEBUG
if (_popt_debug)
- { const char ** avp;
+ { poptArgv avp;
fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
for (avp = argv; *avp; avp++)
fprintf(stderr, " '%s'", *avp);
@@ -472,56 +535,65 @@ if (_popt_debug)
}
#endif
- execvp(argv[0], (char *const *)argv);
+ rc = execvp(argv[0], (char *const *)argv);
- return POPT_ERROR_ERRNO;
+ /* only reached on execvp() failure */
+ con->execFail = xstrdup(argv[0]);
+
+exit:
+ if (argv) {
+ if (argv[0])
+ free((void *)argv[0]);
+ free(argv);
+ }
+ return ec;
}
-/*@=bounds =boundswrite @*/
-/*@-boundswrite@*/
-/*@observer@*/ /*@null@*/ static const struct poptOption *
-findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
+static const struct poptOption *
+findOption(const struct poptOption * opt,
+ const char * longName, size_t longNameLen,
char shortName,
- /*@null@*/ /*@out@*/ poptCallbackType * callback,
- /*@null@*/ /*@out@*/ const void ** callbackData,
- int singleDash)
- /*@modifies *callback, *callbackData */
+ poptCallbackType * callback,
+ const void ** callbackData,
+ unsigned int argInfo)
{
const struct poptOption * cb = NULL;
+ poptArg cbarg = { .ptr = NULL };
/* This happens when a single - is given */
- if (singleDash && !shortName && (longName && *longName == '\0'))
+ if (LF_ISSET(ONEDASH) && !shortName && (longName && *longName == '\0'))
shortName = '-';
for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ poptArg arg = { .ptr = opt->arg };
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
- const struct poptOption * opt2;
- void * arg = opt->arg;
-
-/*@-branchstate@*/
- /* XXX sick hack to preserve pretense of ABI. */
- if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
-/*@=branchstate@*/
- /* Recurse on included sub-tables. */
- if (arg == NULL) continue; /* XXX program error */
- opt2 = findOption(arg, longName, shortName, callback,
- callbackData, singleDash);
+ switch (poptArgType(opt)) {
+ case POPT_ARG_INCLUDE_TABLE: /* Recurse on included sub-tables. */
+ { const struct poptOption * opt2;
+
+ poptSubstituteHelpI18N(arg.opt); /* XXX side effects */
+ if (arg.ptr == NULL) continue; /* XXX program error */
+ opt2 = findOption(arg.opt, longName, longNameLen, shortName, callback,
+ callbackData, argInfo);
if (opt2 == NULL) continue;
/* Sub-table data will be inheirited if no data yet. */
- if (!(callback && *callback)) return opt2;
- if (!(callbackData && *callbackData == NULL)) return opt2;
- /*@-observertrans -dependenttrans @*/
- *callbackData = opt->descrip;
- /*@=observertrans =dependenttrans @*/
+ if (callback && *callback
+ && callbackData && *callbackData == NULL)
+ *callbackData = opt->descrip;
return opt2;
- } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ } break;
+ case POPT_ARG_CALLBACK:
cb = opt;
- } else if (longName && opt->longName &&
- (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
- /*@-nullpass@*/ /* LCL: opt->longName != NULL */
- !strcmp(longName, opt->longName))
- /*@=nullpass@*/
+ cbarg.ptr = opt->arg;
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ if (longName != NULL && opt->longName != NULL &&
+ (!LF_ISSET(ONEDASH) || F_ISSET(opt, ONEDASH)) &&
+ longOptionStrcmp(opt, longName, longNameLen))
{
break;
} else if (shortName && shortName == opt->shortName) {
@@ -529,34 +601,19 @@ findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
}
}
- if (!opt->longName && !opt->shortName)
+ if (opt->longName == NULL && !opt->shortName)
return NULL;
- /*@-modobserver -mods @*/
- if (callback) *callback = NULL;
- if (callbackData) *callbackData = NULL;
- if (cb) {
- if (callback)
- /*@-castfcnptr@*/
- *callback = (poptCallbackType)cb->arg;
- /*@=castfcnptr@*/
- if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
- if (callbackData)
- /*@-observertrans@*/ /* FIX: typedef double indirection. */
- *callbackData = cb->descrip;
- /*@=observertrans@*/
- }
- }
- /*@=modobserver =mods @*/
+
+ if (callback)
+ *callback = (cb ? cbarg.cb : NULL);
+ if (callbackData)
+ *callbackData = (cb && !CBF_ISSET(cb, INC_DATA) ? cb->descrip : NULL);
return opt;
}
-/*@=boundswrite@*/
-static const char * findNextArg(/*@special@*/ poptContext con,
+static const char * findNextArg(poptContext con,
unsigned argx, int delete_arg)
- /*@uses con->optionStack, con->os,
- con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
- /*@modifies con @*/
{
struct optionStackEntry * os = con->os;
const char * arg;
@@ -568,151 +625,586 @@ static const char * findNextArg(/*@special@*/ poptContext con,
if (os->next == os->argc && os == con->optionStack) break;
if (os->argv != NULL)
for (i = os->next; i < os->argc; i++) {
- /*@-sizeoftype@*/
if (os->argb && PBM_ISSET(i, os->argb))
- /*@innercontinue@*/ continue;
+ continue;
if (*os->argv[i] == '-')
- /*@innercontinue@*/ continue;
+ continue;
if (--argx > 0)
- /*@innercontinue@*/ continue;
+ continue;
arg = os->argv[i];
if (delete_arg) {
if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
if (os->argb != NULL) /* XXX can't happen */
- PBM_SET(i, os->argb);
+ PBM_SET(i, os->argb);
}
- /*@innerbreak@*/ break;
- /*@=sizeoftype@*/
+ break;
}
if (os > con->optionStack) os--;
} while (arg == NULL);
return arg;
}
-/*@-boundswrite@*/
-static /*@only@*/ /*@null@*/ const char *
-expandNextArg(/*@special@*/ poptContext con, const char * s)
- /*@uses con->optionStack, con->os,
- con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
- /*@modifies con @*/
+static const char *
+expandNextArg(poptContext con, const char * s)
{
const char * a = NULL;
- size_t alen, pos;
- char *t, *te;
+ char *t, *t_tmp, *te;
size_t tn = strlen(s) + 1;
char c;
- te = t = malloc(tn);;
+ te = t = malloc(tn);
if (t == NULL) return NULL; /* XXX can't happen */
+ *t = '\0';
while ((c = *s++) != '\0') {
switch (c) {
#if 0 /* XXX can't do this */
case '\\': /* escape */
c = *s++;
- /*@switchbreak@*/ break;
+ break;
#endif
case '!':
if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
- /*@switchbreak@*/ break;
+ break;
/* XXX Make sure that findNextArg deletes only next arg. */
if (a == NULL) {
- if ((a = findNextArg(con, 1, 1)) == NULL)
- /*@switchbreak@*/ break;
+ if ((a = findNextArg(con, 1U, 1)) == NULL)
+ break;
+ }
+ s += sizeof("#:+") - 1;
+
+ tn += strlen(a);
+ { size_t pos = (size_t) (te - t);
+ if ((t_tmp = realloc(t, tn)) == NULL) { /* XXX can't happen */
+ free(t);
+ return NULL;
+ }
+ t = t_tmp;
+ te = stpcpy(t + pos, a);
}
- s += 3;
-
- alen = strlen(a);
- tn += alen;
- pos = te - t;
- t = realloc(t, tn);
- te = t + pos;
- memcpy(te, a, alen+1); te += alen;
continue;
- /*@notreached@*/ /*@switchbreak@*/ break;
+ break;
default:
- /*@switchbreak@*/ break;
+ break;
}
*te++ = c;
}
- *te = '\0';
- t = realloc(t, strlen(t) + 1); /* XXX memory leak, hard to plug */
+ *te++ = '\0';
+ /* If the new string is longer than needed, shorten. */
+ if ((t + tn) > te) {
+ if ((te = realloc(t, (size_t)(te - t))) == NULL)
+ free(t);
+ t = te;
+ }
return t;
}
-/*@=boundswrite@*/
-static void poptStripArg(/*@special@*/ poptContext con, int which)
- /*@uses con->arg_strip, con->optionStack @*/
- /*@defines con->arg_strip @*/
- /*@modifies con @*/
+static void poptStripArg(poptContext con, int which)
{
- /*@-sizeoftype@*/
if (con->arg_strip == NULL)
con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
if (con->arg_strip != NULL) /* XXX can't happen */
PBM_SET(which, con->arg_strip);
- /*@=sizeoftype@*/
- /*@-compdef@*/ /* LCL: con->arg_strip udefined? */
return;
- /*@=compdef@*/
}
-int poptSaveLong(long * arg, int argInfo, long aLong)
+unsigned int _poptBitsN = _POPT_BITS_N;
+unsigned int _poptBitsM = _POPT_BITS_M;
+unsigned int _poptBitsK = _POPT_BITS_K;
+
+static int _poptBitsNew(poptBits *bitsp)
+{
+ if (bitsp == NULL)
+ return POPT_ERROR_NULLARG;
+
+ /* XXX handle negated initialization. */
+ if (*bitsp == NULL) {
+ if (_poptBitsN == 0) {
+ _poptBitsN = _POPT_BITS_N;
+ _poptBitsM = _POPT_BITS_M;
+ }
+ if (_poptBitsM == 0U) _poptBitsM = (3 * _poptBitsN) / 2;
+ if (_poptBitsK == 0U || _poptBitsK > 32U) _poptBitsK = _POPT_BITS_K;
+ *bitsp = PBM_ALLOC(_poptBitsM-1);
+ }
+ return 0;
+}
+
+int poptBitsAdd(poptBits bits, const char * s)
+{
+ size_t ns = (s ? strlen(s) : 0);
+ uint32_t h0 = 0;
+ uint32_t h1 = 0;
+
+ if (bits == NULL || ns == 0)
+ return POPT_ERROR_NULLARG;
+
+ poptJlu32lpair(s, ns, &h0, &h1);
+
+ for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
+ uint32_t h = h0 + ns * h1;
+ uint32_t ix = (h % _poptBitsM);
+ PBM_SET(ix, bits);
+ }
+ return 0;
+}
+
+int poptBitsChk(poptBits bits, const char * s)
+{
+ size_t ns = (s ? strlen(s) : 0);
+ uint32_t h0 = 0;
+ uint32_t h1 = 0;
+ int rc = 1;
+
+ if (bits == NULL || ns == 0)
+ return POPT_ERROR_NULLARG;
+
+ poptJlu32lpair(s, ns, &h0, &h1);
+
+ for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
+ uint32_t h = h0 + ns * h1;
+ uint32_t ix = (h % _poptBitsM);
+ if (PBM_ISSET(ix, bits))
+ continue;
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+int poptBitsClr(poptBits bits)
+{
+ static size_t nbw = (__PBM_NBITS/8);
+ size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
+
+ if (bits == NULL)
+ return POPT_ERROR_NULLARG;
+ memset(bits, 0, nw * nbw);
+ return 0;
+}
+
+int poptBitsDel(poptBits bits, const char * s)
+{
+ size_t ns = (s ? strlen(s) : 0);
+ uint32_t h0 = 0;
+ uint32_t h1 = 0;
+
+ if (bits == NULL || ns == 0)
+ return POPT_ERROR_NULLARG;
+
+ poptJlu32lpair(s, ns, &h0, &h1);
+
+ for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
+ uint32_t h = h0 + ns * h1;
+ uint32_t ix = (h % _poptBitsM);
+ PBM_CLR(ix, bits);
+ }
+ return 0;
+}
+
+int poptBitsIntersect(poptBits *ap, const poptBits b)
+{
+ __pbm_bits *abits;
+ __pbm_bits *bbits;
+ __pbm_bits rc = 0;
+ size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
+ size_t i;
+
+ if (ap == NULL || b == NULL || _poptBitsNew(ap))
+ return POPT_ERROR_NULLARG;
+ abits = __PBM_BITS(*ap);
+ bbits = __PBM_BITS(b);
+
+ for (i = 0; i < nw; i++) {
+ abits[i] &= bbits[i];
+ rc |= abits[i];
+ }
+ return (rc ? 1 : 0);
+}
+
+int poptBitsUnion(poptBits *ap, const poptBits b)
+{
+ __pbm_bits *abits;
+ __pbm_bits *bbits;
+ __pbm_bits rc = 0;
+ size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
+ size_t i;
+
+ if (ap == NULL || b == NULL || _poptBitsNew(ap))
+ return POPT_ERROR_NULLARG;
+ abits = __PBM_BITS(*ap);
+ bbits = __PBM_BITS(b);
+
+ for (i = 0; i < nw; i++) {
+ abits[i] |= bbits[i];
+ rc |= abits[i];
+ }
+ return (rc ? 1 : 0);
+}
+
+int poptBitsArgs(poptContext con, poptBits *ap)
+{
+ const char ** av;
+ int rc = 0;
+
+ if (con == NULL || ap == NULL || _poptBitsNew(ap) ||
+ con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
+ return POPT_ERROR_NULLARG;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ for (av = con->leftovers + con->nextLeftover; *av != NULL; av++) {
+ if ((rc = poptBitsAdd(*ap, *av)) != 0)
+ break;
+ }
+ return rc;
+}
+
+int poptSaveBits(poptBits * bitsp,
+ UNUSED(unsigned int argInfo), const char * s)
+{
+ char *tbuf = NULL;
+ char *t, *te;
+ int rc = 0;
+
+ if (bitsp == NULL || s == NULL || *s == '\0' || _poptBitsNew(bitsp))
+ return POPT_ERROR_NULLARG;
+
+ /* Parse comma separated attributes. */
+ te = tbuf = xstrdup(s);
+ while ((t = te) != NULL && *t) {
+ while (*te != '\0' && *te != ',')
+ te++;
+ if (*te != '\0')
+ *te++ = '\0';
+ /* XXX Ignore empty strings. */
+ if (*t == '\0')
+ continue;
+ /* XXX Permit negated attributes. caveat emptor: false negatives. */
+ if (*t == '!') {
+ t++;
+ if ((rc = poptBitsChk(*bitsp, t)) > 0)
+ rc = poptBitsDel(*bitsp, t);
+ } else
+ rc = poptBitsAdd(*bitsp, t);
+ if (rc)
+ break;
+ }
+ tbuf = _free(tbuf);
+ return rc;
+}
+
+int poptSaveString(const char *** argvp,
+ UNUSED(unsigned int argInfo), const char * val)
+{
+ int argc = 0;
+
+ if (argvp == NULL || val == NULL)
+ return POPT_ERROR_NULLARG;
+
+ /* XXX likely needs an upper bound on argc. */
+ if (*argvp != NULL)
+ while ((*argvp)[argc] != NULL)
+ argc++;
+
+ if ((*argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp))) != NULL) {
+ (*argvp)[argc++] = xstrdup(val);
+ (*argvp)[argc ] = NULL;
+ }
+ return 0;
+}
+
+static long long poptRandomValue(long long limit)
+{
+#if defined(HAVE_SRANDOM)
+ static int seed = 1;
+
+ if (seed) {
+ srandom((unsigned)getpid());
+ srandom((unsigned)random());
+ seed = 0;
+ }
+
+ return random() % limit + 1;
+#else
+ /* XXX avoid adding POPT_ERROR_UNIMPLEMENTED to minimize i18n churn. */
+ return POPT_ERROR_BADOPERATION;
+#endif
+}
+
+int poptSaveLongLong(long long * arg, unsigned int argInfo, long long aLongLong)
{
/* XXX Check alignment, may fail on funky platforms. */
- if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
return POPT_ERROR_NULLARG;
- if (argInfo & POPT_ARGFLAG_NOT)
- aLong = ~aLong;
- switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+ if (aLongLong != 0 && LF_ISSET(RANDOM)) {
+ aLongLong = poptRandomValue(aLongLong);
+ if (aLongLong < 0)
+ return aLongLong;
+ }
+ if (LF_ISSET(NOT))
+ aLongLong = ~aLongLong;
+ switch (LF_ISSET(LOGICALOPS)) {
case 0:
- *arg = aLong;
+ *arg = aLongLong;
break;
case POPT_ARGFLAG_OR:
- *arg |= aLong;
+ *(unsigned long long *)arg |= (unsigned long long)aLongLong;
break;
case POPT_ARGFLAG_AND:
- *arg &= aLong;
+ *(unsigned long long *)arg &= (unsigned long long)aLongLong;
break;
case POPT_ARGFLAG_XOR:
- *arg ^= aLong;
+ *(unsigned long long *)arg ^= (unsigned long long)aLongLong;
break;
default:
return POPT_ERROR_BADOPERATION;
- /*@notreached@*/ break;
+ break;
}
return 0;
}
-int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+int poptSaveLong(long * arg, unsigned int argInfo, long aLong)
{
/* XXX Check alignment, may fail on funky platforms. */
- if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
return POPT_ERROR_NULLARG;
- if (argInfo & POPT_ARGFLAG_NOT)
+ if (aLong != 0 && LF_ISSET(RANDOM)) {
+ aLong = (long)poptRandomValue(aLong);
+ if (aLong < 0)
+ return aLong;
+ }
+ if (LF_ISSET(NOT))
aLong = ~aLong;
- switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
- case 0:
- *arg = aLong;
+ switch (LF_ISSET(LOGICALOPS)) {
+ case 0: *arg = aLong; break;
+ case POPT_ARGFLAG_OR: *(unsigned long *)arg |= (unsigned long)aLong; break;
+ case POPT_ARGFLAG_AND: *(unsigned long *)arg &= (unsigned long)aLong; break;
+ case POPT_ARGFLAG_XOR: *(unsigned long *)arg ^= (unsigned long)aLong; break;
+ default:
+ return POPT_ERROR_BADOPERATION;
break;
- case POPT_ARGFLAG_OR:
- *arg |= aLong;
+ }
+ return 0;
+}
+
+int poptSaveInt(int * arg, unsigned int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (aLong != 0 && LF_ISSET(RANDOM)) {
+ aLong = (int)poptRandomValue(aLong);
+ if (aLong < 0)
+ return aLong;
+ }
+ if (LF_ISSET(NOT))
+ aLong = ~aLong;
+ switch (LF_ISSET(LOGICALOPS)) {
+ case 0: *arg = (int) aLong; break;
+ case POPT_ARGFLAG_OR: *(unsigned int *)arg |= (unsigned int) aLong; break;
+ case POPT_ARGFLAG_AND: *(unsigned int *)arg &= (unsigned int) aLong; break;
+ case POPT_ARGFLAG_XOR: *(unsigned int *)arg ^= (unsigned int) aLong; break;
+ default:
+ return POPT_ERROR_BADOPERATION;
break;
- case POPT_ARGFLAG_AND:
- *arg &= aLong;
+ }
+ return 0;
+}
+
+int poptSaveShort(short * arg, unsigned int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (aLong != 0 && LF_ISSET(RANDOM)) {
+ aLong = (short)poptRandomValue(aLong);
+ if (aLong < 0)
+ return aLong;
+ }
+ if (LF_ISSET(NOT))
+ aLong = ~aLong;
+ switch (LF_ISSET(LOGICALOPS)) {
+ case 0: *arg = (short) aLong;
break;
- case POPT_ARGFLAG_XOR:
- *arg ^= aLong;
+ case POPT_ARGFLAG_OR: *(unsigned short *)arg |= (unsigned short) aLong;
+ break;
+ case POPT_ARGFLAG_AND: *(unsigned short *)arg &= (unsigned short) aLong;
+ break;
+ case POPT_ARGFLAG_XOR: *(unsigned short *)arg ^= (unsigned short) aLong;
+ break;
+ default: return POPT_ERROR_BADOPERATION;
break;
- default:
- return POPT_ERROR_BADOPERATION;
- /*@notreached@*/ break;
}
return 0;
}
-/*@-boundswrite@*/
+/**
+ * Return argInfo field, handling POPT_ARGFLAG_TOGGLE overrides.
+ * @param con context
+ * @param opt option
+ * @return argInfo
+ */
+static unsigned int poptArgInfo(poptContext con, const struct poptOption * opt)
+{
+ unsigned int argInfo = opt->argInfo;
+
+ if (con->os->argv != NULL && con->os->next > 0 && opt->longName != NULL)
+ if (LF_ISSET(TOGGLE)) {
+ const char * longName = con->os->argv[con->os->next-1];
+ while (*longName == '-') longName++;
+ /* XXX almost good enough but consider --[no]nofoo corner cases. */
+ if (longName[0] != opt->longName[0] || longName[1] != opt->longName[1])
+ {
+ if (!LF_ISSET(XOR)) { /* XXX dont toggle with XOR */
+ /* Toggle POPT_BIT_SET <=> POPT_BIT_CLR. */
+ if (LF_ISSET(LOGICALOPS))
+ argInfo ^= (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND);
+ argInfo ^= POPT_ARGFLAG_NOT;
+ }
+ }
+ }
+ return argInfo;
+}
+
+/**
+ * Parse an integer expression.
+ * @retval *llp integer expression value
+ * @param argInfo integer expression type
+ * @param val integer expression string
+ * @return 0 on success, otherwise POPT_* error.
+ */
+static int poptParseInteger(long long * llp,
+ UNUSED(unsigned int argInfo),
+ const char * val)
+{
+ if (val) {
+ char *end = NULL;
+ *llp = strtoll(val, &end, 0);
+
+ /* XXX parse scaling suffixes here. */
+
+ if (!(end && *end == '\0'))
+ return POPT_ERROR_BADNUMBER;
+ } else
+ *llp = 0;
+ return 0;
+}
+
+/**
+ * Save the option argument through the (*opt->arg) pointer.
+ * @param con context
+ * @param opt option
+ * @return 0 on success, otherwise POPT_* error.
+ */
+static int poptSaveArg(poptContext con, const struct poptOption * opt)
+{
+ poptArg arg = { .ptr = opt->arg };
+ int rc = 0; /* assume success */
+
+ switch (poptArgType(opt)) {
+ case POPT_ARG_BITSET:
+ /* XXX memory leak, application is responsible for free. */
+ rc = poptSaveBits(arg.ptr, opt->argInfo, con->os->nextArg);
+ break;
+ case POPT_ARG_ARGV:
+ /* XXX memory leak, application is responsible for free. */
+ rc = poptSaveString(arg.ptr, opt->argInfo, con->os->nextArg);
+ break;
+ case POPT_ARG_STRING:
+ /* XXX memory leak, application is responsible for free. */
+ arg.argv[0] = (con->os->nextArg) ? xstrdup(con->os->nextArg) : NULL;
+ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_SHORT:
+ case POPT_ARG_LONG:
+ case POPT_ARG_LONGLONG:
+ { unsigned int argInfo = poptArgInfo(con, opt);
+ long long aNUM = 0;
+
+ if ((rc = poptParseInteger(&aNUM, argInfo, con->os->nextArg)) != 0)
+ break;
+
+ switch (poptArgType(opt)) {
+ case POPT_ARG_LONGLONG:
+/* XXX let's not demand C99 compiler flags for quite yet. */
+#if !defined(LLONG_MAX)
+# define LLONG_MAX 9223372036854775807LL
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+#endif
+ rc = !(aNUM == LLONG_MIN || aNUM == LLONG_MAX)
+ ? poptSaveLongLong(arg.longlongp, argInfo, aNUM)
+ : POPT_ERROR_OVERFLOW;
+ break;
+ case POPT_ARG_LONG:
+ rc = !(aNUM < (long long)LONG_MIN || aNUM > (long long)LONG_MAX)
+ ? poptSaveLong(arg.longp, argInfo, (long)aNUM)
+ : POPT_ERROR_OVERFLOW;
+ break;
+ case POPT_ARG_INT:
+ rc = !(aNUM < (long long)INT_MIN || aNUM > (long long)INT_MAX)
+ ? poptSaveInt(arg.intp, argInfo, (long)aNUM)
+ : POPT_ERROR_OVERFLOW;
+ break;
+ case POPT_ARG_SHORT:
+ rc = !(aNUM < (long long)SHRT_MIN || aNUM > (long long)SHRT_MAX)
+ ? poptSaveShort(arg.shortp, argInfo, (long)aNUM)
+ : POPT_ERROR_OVERFLOW;
+ break;
+ }
+ } break;
+
+ case POPT_ARG_FLOAT:
+ case POPT_ARG_DOUBLE:
+ { char *end = NULL;
+ double aDouble = 0.0;
+
+ if (con->os->nextArg) {
+ int saveerrno = errno;
+ errno = 0;
+ aDouble = strtod(con->os->nextArg, &end);
+ if (errno == ERANGE) {
+ rc = POPT_ERROR_OVERFLOW;
+ break;
+ }
+ errno = saveerrno;
+ if (*end != '\0') {
+ rc = POPT_ERROR_BADNUMBER;
+ break;
+ }
+ }
+
+ switch (poptArgType(opt)) {
+ case POPT_ARG_DOUBLE:
+ arg.doublep[0] = aDouble;
+ break;
+ case POPT_ARG_FLOAT:
+#define POPT_ABS(a) ((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
+ if ((FLT_MIN - POPT_ABS(aDouble)) > DBL_EPSILON
+ || (POPT_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
+ rc = POPT_ERROR_OVERFLOW;
+ else
+ arg.floatp[0] = (float) aDouble;
+ break;
+ }
+ } break;
+ case POPT_ARG_MAINCALL:
+ con->maincall = opt->arg;
+ break;
+ default:
+ fprintf(stdout, POPT_("option type (%u) not implemented in popt\n"),
+ poptArgType(opt));
+ exit(EXIT_FAILURE);
+ break;
+ }
+ return rc;
+}
+
/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
int poptGetNextOpt(poptContext con)
{
@@ -734,24 +1226,27 @@ int poptGetNextOpt(poptContext con)
cleanOSE(con->os--);
}
if (!con->os->nextCharArg && con->os->next == con->os->argc) {
- /*@-internalglobs@*/
invokeCallbacksPOST(con, con->options);
- /*@=internalglobs@*/
+
+ if (con->maincall) {
+ (void) (*con->maincall) (con->finalArgvCount, con->finalArgv);
+ return -1;
+ }
+
if (con->doExec) return execCommand(con);
return -1;
}
/* Process next long option */
if (!con->os->nextCharArg) {
- char * localOptString, * optString;
+ const char * optString;
+ size_t optStringLen;
int thisopt;
- /*@-sizeoftype@*/
if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
con->os->next++;
continue;
}
- /*@=sizeoftype@*/
thisopt = con->os->next;
if (con->os->argv != NULL) /* XXX can't happen */
origOptString = con->os->argv[con->os->next++];
@@ -759,25 +1254,35 @@ int poptGetNextOpt(poptContext con)
if (origOptString == NULL) /* XXX can't happen */
return POPT_ERROR_BADOPT;
- if (con->restLeftover || *origOptString != '-') {
+ if (con->restLeftover || *origOptString != '-' ||
+ (*origOptString == '-' && origOptString[1] == '\0'))
+ {
if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
con->restLeftover = 1;
if (con->flags & POPT_CONTEXT_ARG_OPTS) {
con->os->nextArg = xstrdup(origOptString);
return 0;
}
- if (con->leftovers != NULL) /* XXX can't happen */
- con->leftovers[con->numLeftovers++] = origOptString;
+ if (con->leftovers != NULL) { /* XXX can't happen */
+ /* One might think we can never overflow the leftovers
+ array. Actually, that's true, as long as you don't
+ use poptStuffArgs()... */
+ if ((con->numLeftovers + 1) >= (con->allocLeftovers)) {
+ con->allocLeftovers += 10;
+ con->leftovers =
+ realloc(con->leftovers,
+ sizeof(*con->leftovers) * con->allocLeftovers);
+ }
+ con->leftovers[con->numLeftovers++]
+ = xstrdup(origOptString); /* so a free of a stuffed
+ argv doesn't give us a
+ dangling pointer */
+ }
continue;
}
/* Make a copy we can hack at */
- { size_t bufsize = strlen(origOptString) + 1;
- localOptString = optString = alloca(bufsize);
- if (optString == NULL) /* XXX can't happen */
- return POPT_ERROR_BADOPT;
- strlcpy(optString, origOptString, bufsize);
- }
+ optString = origOptString;
if (optString[0] == '\0')
return POPT_ERROR_BADOPT;
@@ -786,46 +1291,42 @@ int poptGetNextOpt(poptContext con)
con->restLeftover = 1;
continue;
} else {
- char *oe;
- int singleDash;
+ const char *oe;
+ unsigned int argInfo = 0;
optString++;
if (*optString == '-')
- singleDash = 0, optString++;
+ optString++;
else
- singleDash = 1;
+ argInfo |= POPT_ARGFLAG_ONEDASH;
+
+ /* Check for "--long=arg" option. */
+ for (oe = optString; *oe && *oe != '='; oe++)
+ {};
+ optStringLen = (size_t)(oe - optString);
+ if (*oe == '=')
+ longArg = oe + 1;
/* XXX aliases with arg substitution need "--alias=arg" */
- if (handleAlias(con, optString, '\0', NULL))
+ if (handleAlias(con, optString, optStringLen, '\0', longArg)) {
+ longArg = NULL;
continue;
+ }
if (handleExec(con, optString, '\0'))
continue;
- /* Check for "--long=arg" option. */
- for (oe = optString; *oe && *oe != '='; oe++)
- {};
- if (*oe == '=') {
- *oe++ = '\0';
- /* XXX longArg is mapped back to persistent storage. */
- longArg = origOptString + (oe - localOptString);
- } else
- oe = NULL;
-
- opt = findOption(con->options, optString, '\0', &cb, &cbData,
- singleDash);
- if (!opt && !singleDash)
+ opt = findOption(con->options, optString, optStringLen, '\0', &cb, &cbData,
+ argInfo);
+ if (!opt && !LF_ISSET(ONEDASH))
return POPT_ERROR_BADOPT;
- if (!opt && oe)
- oe[-1] = '='; /* restore overwritten '=' */
}
if (!opt) {
con->os->nextCharArg = origOptString + 1;
longArg = NULL;
} else {
- if (con->os == con->optionStack &&
- opt->argInfo & POPT_ARGFLAG_STRIP)
+ if (con->os == con->optionStack && F_ISSET(opt, STRIP))
{
canstrip = 1;
poptStripArg(con, thisopt);
@@ -835,66 +1336,63 @@ int poptGetNextOpt(poptContext con)
}
/* Process next short option */
- /*@-branchstate@*/ /* FIX: W2DO? */
if (con->os->nextCharArg) {
- origOptString = con->os->nextCharArg;
+ const char * nextCharArg = con->os->nextCharArg;
con->os->nextCharArg = NULL;
- if (handleAlias(con, NULL, *origOptString, origOptString + 1))
+ if (handleAlias(con, NULL, 0, *nextCharArg, nextCharArg + 1))
continue;
- if (handleExec(con, NULL, *origOptString)) {
+ if (handleExec(con, NULL, *nextCharArg)) {
/* Restore rest of short options for further processing */
- origOptString++;
- if (*origOptString != '\0')
- con->os->nextCharArg = origOptString;
+ nextCharArg++;
+ if (*nextCharArg != '\0')
+ con->os->nextCharArg = nextCharArg;
continue;
}
- opt = findOption(con->options, NULL, *origOptString, &cb,
+ opt = findOption(con->options, NULL, 0, *nextCharArg, &cb,
&cbData, 0);
if (!opt)
return POPT_ERROR_BADOPT;
shorty = 1;
- origOptString++;
- if (*origOptString != '\0')
- con->os->nextCharArg = origOptString;
+ nextCharArg++;
+ if (*nextCharArg != '\0')
+ con->os->nextCharArg = nextCharArg;
}
- /*@=branchstate@*/
if (opt == NULL) return POPT_ERROR_BADOPT; /* XXX can't happen */
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE
- || (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (poptArgType(opt) == POPT_ARG_NONE || poptArgType(opt) == POPT_ARG_VAL) {
if (longArg || (con->os->nextCharArg && con->os->nextCharArg[0] == '='))
return POPT_ERROR_UNWANTEDARG;
if (opt->arg) {
- long val = (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL ? opt->val : 1;
- if (poptSaveInt((int *)opt->arg, opt->argInfo, val))
+ long val = poptArgType(opt) == POPT_ARG_VAL ? opt->val : 1;
+ unsigned int argInfo = poptArgInfo(con, opt);
+ if (poptSaveInt((int *)opt->arg, argInfo, val))
return POPT_ERROR_BADOPERATION;
}
} else {
+ int rc;
+
con->os->nextArg = _free(con->os->nextArg);
- /*@-usedef@*/ /* FIX: W2DO? */
if (longArg) {
- /*@=usedef@*/
longArg = expandNextArg(con, longArg);
- con->os->nextArg = longArg;
+ con->os->nextArg = (char *) longArg;
} else if (con->os->nextCharArg) {
- longArg = expandNextArg(con, con->os->nextCharArg + (con->os->nextCharArg[0] == '='));
- con->os->nextArg = longArg;
+ longArg = expandNextArg(con, con->os->nextCharArg + (int)(*con->os->nextCharArg == '='));
+ con->os->nextArg = (char *) longArg;
con->os->nextCharArg = NULL;
} else {
while (con->os->next == con->os->argc &&
- con->os > con->optionStack) {
+ con->os > con->optionStack)
+ {
cleanOSE(con->os--);
}
if (con->os->next == con->os->argc) {
- if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
- /*@-compdef@*/ /* FIX: con->os->argv not defined */
+ if (!F_ISSET(opt, OPTIONAL))
return POPT_ERROR_NOARG;
- /*@=compdef@*/
con->os->nextArg = NULL;
} else {
@@ -902,98 +1400,35 @@ int poptGetNextOpt(poptContext con)
* Make sure this isn't part of a short arg or the
* result of an alias expansion.
*/
- if (con->os == con->optionStack &&
- (opt->argInfo & POPT_ARGFLAG_STRIP) &&
- canstrip) {
+ if (con->os == con->optionStack
+ && F_ISSET(opt, STRIP) && canstrip)
+ {
poptStripArg(con, con->os->next);
}
if (con->os->argv != NULL) { /* XXX can't happen */
- /* XXX watchout: subtle side-effects live here. */
- longArg = con->os->argv[con->os->next++];
- longArg = expandNextArg(con, longArg);
- con->os->nextArg = longArg;
+ if (F_ISSET(opt, OPTIONAL) &&
+ con->os->argv[con->os->next][0] == '-') {
+ con->os->nextArg = NULL;
+ } else {
+ /* XXX watchout: subtle side-effects live here. */
+ longArg = con->os->argv[con->os->next++];
+ longArg = expandNextArg(con, longArg);
+ con->os->nextArg = (char *) longArg;
+ }
}
}
}
longArg = NULL;
- if (opt->arg) {
- switch (opt->argInfo & POPT_ARG_MASK) {
- case POPT_ARG_STRING:
- /* XXX memory leak, hard to plug */
- *((const char **) opt->arg) = (con->os->nextArg)
- ? xstrdup(con->os->nextArg) : NULL;
- /*@switchbreak@*/ break;
-
- case POPT_ARG_INT:
- case POPT_ARG_LONG:
- { long aLong = 0;
- char *end;
-
- if (con->os->nextArg) {
- aLong = strtol(con->os->nextArg, &end, 0);
- if (!(end && *end == '\0'))
- return POPT_ERROR_BADNUMBER;
- }
-
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
- if (aLong == LONG_MIN || aLong == LONG_MAX)
- return POPT_ERROR_OVERFLOW;
- if (poptSaveLong((long *)opt->arg, opt->argInfo, aLong))
- return POPT_ERROR_BADOPERATION;
- } else {
- if (aLong > INT_MAX || aLong < INT_MIN)
- return POPT_ERROR_OVERFLOW;
- if (poptSaveInt((int *)opt->arg, opt->argInfo, aLong))
- return POPT_ERROR_BADOPERATION;
- }
- } /*@switchbreak@*/ break;
-
- case POPT_ARG_FLOAT:
- case POPT_ARG_DOUBLE:
- { double aDouble = 0.0;
- char *end;
-
- if (con->os->nextArg) {
- /*@-mods@*/
- int saveerrno = errno;
- errno = 0;
- aDouble = strtod(con->os->nextArg, &end);
- if (errno == ERANGE)
- return POPT_ERROR_OVERFLOW;
- errno = saveerrno;
- /*@=mods@*/
- if (*end != '\0')
- return POPT_ERROR_BADNUMBER;
- }
-
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_DOUBLE) {
- *((double *) opt->arg) = aDouble;
- } else {
-#define MY_ABS(a) ((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
- if ((MY_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
- return POPT_ERROR_OVERFLOW;
- if ((FLT_MIN - MY_ABS(aDouble)) > DBL_EPSILON)
- return POPT_ERROR_OVERFLOW;
- *((float *) opt->arg) = aDouble;
- }
- } /*@switchbreak@*/ break;
- default:
- fprintf(stdout,
- POPT_("option type (%d) not implemented in popt\n"),
- (opt->argInfo & POPT_ARG_MASK));
- exit(EXIT_FAILURE);
- /*@notreached@*/ /*@switchbreak@*/ break;
- }
- }
+ /* Save the option argument through a (*opt->arg) pointer. */
+ if (opt->arg != NULL && (rc = poptSaveArg(con, opt)) != 0)
+ return rc;
}
- if (cb) {
- /*@-internalglobs@*/
+ if (cb)
invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
- /*@=internalglobs@*/
- } else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ else if (opt->val && (poptArgType(opt) != POPT_ARG_VAL))
done = 1;
if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
@@ -1003,46 +1438,43 @@ int poptGetNextOpt(poptContext con)
}
if (con->finalArgv != NULL)
- { ssize_t bufsize = (opt->longName ? strlen(opt->longName) : 0) + 3;
- char *s = malloc(bufsize);
+ { char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + sizeof("--"));
if (s != NULL) { /* XXX can't happen */
- if (opt->longName)
- snprintf(s, bufsize, "%s%s",
- ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
- opt->longName);
- else
- snprintf(s, bufsize, "-%c", opt->shortName);
con->finalArgv[con->finalArgvCount++] = s;
+ *s++ = '-';
+ if (opt->longName) {
+ if (!F_ISSET(opt, ONEDASH))
+ *s++ = '-';
+ s = stpcpy(s, opt->longName);
+ } else {
+ *s++ = opt->shortName;
+ *s = '\0';
+ }
} else
con->finalArgv[con->finalArgvCount++] = NULL;
}
- if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE)
- /*@-ifempty@*/ ; /*@=ifempty@*/
- else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL)
- /*@-ifempty@*/ ; /*@=ifempty@*/
- else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
- if (con->finalArgv != NULL && con->os->nextArg)
+ if (opt->arg && poptArgType(opt) == POPT_ARG_NONE)
+ ;
+ else if (poptArgType(opt) == POPT_ARG_VAL)
+ ;
+ else if (poptArgType(opt) != POPT_ARG_NONE) {
+ if (con->finalArgv != NULL && con->os->nextArg != NULL)
con->finalArgv[con->finalArgvCount++] =
- /*@-nullpass@*/ /* LCL: con->os->nextArg != NULL */
xstrdup(con->os->nextArg);
- /*@=nullpass@*/
}
}
return (opt ? opt->val : -1); /* XXX can't happen */
}
-/*@=boundswrite@*/
-const char * poptGetOptArg(poptContext con)
+char * poptGetOptArg(poptContext con)
{
- const char * ret = NULL;
- /*@-branchstate@*/
+ char * ret = NULL;
if (con) {
ret = con->os->nextArg;
con->os->nextArg = NULL;
}
- /*@=branchstate@*/
return ret;
}
@@ -1062,7 +1494,6 @@ const char * poptPeekArg(poptContext con)
return ret;
}
-/*@-boundswrite@*/
const char ** poptGetArgs(poptContext con)
{
if (con == NULL ||
@@ -1072,46 +1503,44 @@ const char ** poptGetArgs(poptContext con)
/* some apps like [like RPM ;-) ] need this NULL terminated */
con->leftovers[con->numLeftovers] = NULL;
- /*@-nullret -nullstate @*/ /* FIX: typedef double indirection. */
return (con->leftovers + con->nextLeftover);
- /*@=nullret =nullstate @*/
}
-/*@=boundswrite@*/
+
+static
+poptItem poptFreeItems(poptItem items, int nitems)
+{
+ if (items != NULL) {
+ poptItem item = items;
+ while (--nitems >= 0) {
+ item->option.longName = _free(item->option.longName);
+ item->option.descrip = _free(item->option.descrip);
+ item->option.argDescrip = _free(item->option.argDescrip);
+ item->argv = _free(item->argv);
+ item++;
+ }
+ _free(items);
+ }
+ return NULL;
+}
poptContext poptFreeContext(poptContext con)
{
- poptItem item;
int i;
if (con == NULL) return con;
poptResetContext(con);
- con->os->argb = _free(con->os->argb);
- if (con->aliases != NULL)
- for (i = 0; i < con->numAliases; i++) {
- item = con->aliases + i;
- /*@-modobserver -observertrans -dependenttrans@*/
- item->option.longName = _free(item->option.longName);
- item->option.descrip = _free(item->option.descrip);
- item->option.argDescrip = _free(item->option.argDescrip);
- /*@=modobserver =observertrans =dependenttrans@*/
- item->argv = _free(item->argv);
- }
- con->aliases = _free(con->aliases);
+ con->aliases = poptFreeItems(con->aliases, con->numAliases);
+ con->numAliases = 0;
- if (con->execs != NULL)
- for (i = 0; i < con->numExecs; i++) {
- item = con->execs + i;
- /*@-modobserver -observertrans -dependenttrans@*/
- item->option.longName = _free(item->option.longName);
- item->option.descrip = _free(item->option.descrip);
- item->option.argDescrip = _free(item->option.argDescrip);
- /*@=modobserver =observertrans =dependenttrans@*/
- item->argv = _free(item->argv);
- }
- con->execs = _free(con->execs);
+ con->execs = poptFreeItems(con->execs, con->numExecs);
+ con->numExecs = 0;
+ for (i = 0; i < con->numLeftovers; i++) {
+ con->leftovers[i] = _free(con->leftovers[i]);
+ }
con->leftovers = _free(con->leftovers);
+
con->finalArgv = _free(con->finalArgv);
con->appName = _free(con->appName);
con->otherHelp = _free(con->otherHelp);
@@ -1123,9 +1552,10 @@ poptContext poptFreeContext(poptContext con)
}
int poptAddAlias(poptContext con, struct poptAlias alias,
- /*@unused@*/ UNUSED(int flags))
+ UNUSED(int flags))
{
- poptItem item = (poptItem) alloca(sizeof(*item));
+ struct poptItem_s item_buf;
+ poptItem item = &item_buf;
memset(item, 0, sizeof(*item));
item->option.longName = alias.longName;
item->option.shortName = alias.shortName;
@@ -1139,11 +1569,9 @@ int poptAddAlias(poptContext con, struct poptAlias alias,
return poptAddItem(con, item, 0);
}
-/*@-boundswrite@*/
-/*@-mustmod@*/ /* LCL: con not modified? */
int poptAddItem(poptContext con, poptItem newItem, int flags)
{
- poptItem * items, item;
+ poptItem * items, item_tmp, item;
int * nitems;
switch (flags) {
@@ -1157,12 +1585,13 @@ int poptAddItem(poptContext con, poptItem newItem, int flags)
break;
default:
return 1;
- /*@notreached@*/ break;
+ break;
}
- *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
- if ((*items) == NULL)
+ item_tmp = realloc((*items), ((*nitems) + 1) * sizeof(**items));
+ if (item_tmp == NULL)
return 1;
+ *items = item_tmp;
item = (*items) + (*nitems);
@@ -1183,19 +1612,23 @@ int poptAddItem(poptContext con, poptItem newItem, int flags)
return 0;
}
-/*@=mustmod@*/
-/*@=boundswrite@*/
-const char * poptBadOption(poptContext con, int flags)
+const char * poptBadOption(poptContext con, unsigned int flags)
{
struct optionStackEntry * os = NULL;
+ const char *badOpt = NULL;
+
+ if (con != NULL) {
+ /* Stupid hack to return something semi-meaningful from exec failure */
+ if (con->execFail) {
+ badOpt = con->execFail;
+ } else {
+ os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
+ badOpt = os->argv[os->next - 1];
+ }
+ }
- if (con != NULL)
- os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
-
- /*@-nullderef@*/ /* LCL: os->argv != NULL */
- return (os && os->argv ? os->argv[os->next - 1] : NULL);
- /*@=nullderef@*/
+ return badOpt;
}
const char * poptStrerror(const int error)
@@ -1221,6 +1654,8 @@ const char * poptStrerror(const int error)
return POPT_("number too large or too small");
case POPT_ERROR_MALLOC:
return POPT_("memory allocation failed");
+ case POPT_ERROR_BADCONFIG:
+ return POPT_("config file failed sanity test");
case POPT_ERROR_ERRNO:
return strerror(errno);
default:
@@ -1256,14 +1691,12 @@ const char * poptGetInvocationName(poptContext con)
return (con->os->argv ? con->os->argv[0] : "");
}
-/*@-boundswrite@*/
int poptStrippedArgv(poptContext con, int argc, char ** argv)
{
int numargs = argc;
int j = 1;
int i;
- /*@-sizeoftype@*/
if (con->arg_strip)
for (i = 1; i < argc; i++) {
if (PBM_ISSET(i, con->arg_strip))
@@ -1276,8 +1709,6 @@ int poptStrippedArgv(poptContext con, int argc, char ** argv)
argv[j] = (j < numargs) ? argv[i] : NULL;
j++;
}
- /*@=sizeoftype@*/
return numargs;
}
-/*@=boundswrite@*/
diff --git a/popt/popt.h b/popt/popt.h
index 8d85f7312..bd1606110 100644
--- a/popt/popt.h
+++ b/popt/popt.h
@@ -1,5 +1,4 @@
-/** \file popt/popt.h
- * \ingroup popt
+/** @file
*/
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -13,45 +12,49 @@
#define POPT_OPTION_DEPTH 10
-/** \ingroup popt
+/**
* \name Arg type identifiers
*/
-/*@{*/
-#define POPT_ARG_NONE 0 /*!< no arg */
-#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
-#define POPT_ARG_INT 2 /*!< arg will be converted to int */
-#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
-#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
-#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
+#define POPT_ARG_NONE 0U /*!< no arg */
+#define POPT_ARG_STRING 1U /*!< arg will be saved as string */
+#define POPT_ARG_INT 2U /*!< arg ==> int */
+#define POPT_ARG_LONG 3U /*!< arg ==> long */
+#define POPT_ARG_INCLUDE_TABLE 4U /*!< arg points to table */
+#define POPT_ARG_CALLBACK 5U /*!< table-wide callback... must be
set first in table; arg points
to callback, descrip points to
callback data to pass */
-#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
+#define POPT_ARG_INTL_DOMAIN 6U /*!< set the translation domain
for this table and any
included tables; arg points
to the domain string */
-#define POPT_ARG_VAL 7 /*!< arg should take value val */
-#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
-#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
+#define POPT_ARG_VAL 7U /*!< arg should take value val */
+#define POPT_ARG_FLOAT 8U /*!< arg ==> float */
+#define POPT_ARG_DOUBLE 9U /*!< arg ==> double */
+#define POPT_ARG_LONGLONG 10U /*!< arg ==> long long */
+
+#define POPT_ARG_MAINCALL (16U+11U) /*!< EXPERIMENTAL: return (*arg) (argc, argv) */
+#define POPT_ARG_ARGV 12U /*!< dupe'd arg appended to realloc'd argv array. */
+#define POPT_ARG_SHORT 13U /*!< arg ==> short */
+#define POPT_ARG_BITSET (16U+14U) /*!< arg ==> bit set */
-#define POPT_ARG_MASK 0x0000FFFF
-/*@}*/
+#define POPT_ARG_MASK 0x000000FFU
+#define POPT_GROUP_MASK 0x0000FF00U
-/** \ingroup popt
+/**
* \name Arg modifiers
*/
-/*@{*/
-#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
-#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
-#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
-#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
-
-#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
-#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
-#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
-#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
-#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
-#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
+#define POPT_ARGFLAG_ONEDASH 0x80000000U /*!< allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000U /*!< don't show in help/usage */
+#define POPT_ARGFLAG_STRIP 0x20000000U /*!< strip this arg from argv(only applies to long args) */
+#define POPT_ARGFLAG_OPTIONAL 0x10000000U /*!< arg may be missing */
+
+#define POPT_ARGFLAG_OR 0x08000000U /*!< arg will be or'ed */
+#define POPT_ARGFLAG_NOR 0x09000000U /*!< arg will be nor'ed */
+#define POPT_ARGFLAG_AND 0x04000000U /*!< arg will be and'ed */
+#define POPT_ARGFLAG_NAND 0x05000000U /*!< arg will be nand'ed */
+#define POPT_ARGFLAG_XOR 0x02000000U /*!< arg will be xor'ed */
+#define POPT_ARGFLAG_NOT 0x01000000U /*!< arg will be negated */
#define POPT_ARGFLAG_LOGICALOPS \
(POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
@@ -60,158 +63,126 @@
#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
/*!< clear arg bit(s) */
-#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
-
-/*@}*/
+#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000U /*!< show default value in --help */
+#define POPT_ARGFLAG_RANDOM 0x00400000U /*!< random value in [1,arg] */
+#define POPT_ARGFLAG_TOGGLE 0x00200000U /*!< permit --[no]opt prefix toggle */
-/** \ingroup popt
+/**
* \name Callback modifiers
*/
-/*@{*/
-#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
-#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
-#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
+#define POPT_CBFLAG_PRE 0x80000000U /*!< call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000U /*!< call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000U /*!< use data from the include line,
not the subtable */
-#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
-#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
-/*@}*/
+#define POPT_CBFLAG_SKIPOPTION 0x10000000U /*!< don't callback with option */
+#define POPT_CBFLAG_CONTINUE 0x08000000U /*!< continue callbacks with option */
-/** \ingroup popt
+/**
* \name Error return values
*/
-/*@{*/
#define POPT_ERROR_NOARG -10 /*!< missing argument */
#define POPT_ERROR_BADOPT -11 /*!< unknown option */
#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
-#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
+#define POPT_ERROR_BADQUOTE -15 /*!< error in parameter quoting */
#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
-/*@}*/
+#define POPT_ERROR_BADCONFIG -22 /*!< config file failed sanity test */
-/** \ingroup popt
+/**
* \name poptBadOption() flags
*/
-/*@{*/
-#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
-/*@}*/
+#define POPT_BADOPTION_NOALIAS (1U << 0) /*!< don't go into an alias */
-/** \ingroup popt
+/**
* \name poptGetContext() flags
*/
-/*@{*/
-#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
-#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
-#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
-#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
-/*@}*/
+#define POPT_CONTEXT_NO_EXEC (1U << 0) /*!< ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1U << 1) /*!< pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1U << 2) /*!< options can't follow args */
+#define POPT_CONTEXT_ARG_OPTS (1U << 4) /*!< return args as options with value 0 */
-/** \ingroup popt
+/**
*/
struct poptOption {
-/*@observer@*/ /*@null@*/
const char * longName; /*!< may be NULL */
- char shortName; /*!< may be NUL */
- int argInfo;
-/*@shared@*/ /*@null@*/
+ char shortName; /*!< may be '\0' */
+ unsigned int argInfo; /*!< type of argument expected after the option */
void * arg; /*!< depends on argInfo */
- int val; /*!< 0 means don't return, just update flag */
-/*@observer@*/ /*@null@*/
+ int val; /*!< 0 means don't return, just update arg */
const char * descrip; /*!< description for autohelp -- may be NULL */
-/*@observer@*/ /*@null@*/
- const char * argDescrip; /*!< argument description for autohelp */
+ const char * argDescrip; /*!< argument description for autohelp -- may be NULL */
};
-/** \ingroup popt
+/**
* A popt alias argument for poptAddAlias().
*/
struct poptAlias {
-/*@owned@*/ /*@null@*/
const char * longName; /*!< may be NULL */
char shortName; /*!< may be NUL */
int argc;
-/*@owned@*/
const char ** argv; /*!< must be free()able */
};
-/** \ingroup popt
+/**
* A popt alias or exec argument for poptAddItem().
*/
-/*@-exporttype@*/
typedef struct poptItem_s {
struct poptOption option; /*!< alias/exec name(s) and description. */
int argc; /*!< (alias) no. of args. */
-/*@owned@*/
const char ** argv; /*!< (alias) args, must be free()able. */
} * poptItem;
-/*@=exporttype@*/
-/** \ingroup popt
+/**
* \name Auto-generated help/usage
*/
-/*@{*/
/**
* Empty table marker to enable displaying popt alias/exec options.
*/
-/*@-exportvar@*/
-/*@unchecked@*/ /*@observer@*/
extern struct poptOption poptAliasOptions[];
-/*@=exportvar@*/
#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
0, "Options implemented via popt alias/exec:", NULL },
/**
* Auto help table options.
*/
-/*@-exportvar@*/
-/*@unchecked@*/ /*@observer@*/
extern struct poptOption poptHelpOptions[];
-/*@=exportvar@*/
-/*@-exportvar@*/
-/*@unchecked@*/ /*@observer@*/
extern struct poptOption * poptHelpOptionsI18N;
-/*@=exportvar@*/
#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
0, "Help options:", NULL },
-#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
-/*@}*/
+#define POPT_TABLEEND { NULL, '\0', 0, NULL, 0, NULL, NULL }
-/** \ingroup popt
+/**
*/
-/*@-exporttype@*/
-typedef /*@abstract@*/ struct poptContext_s * poptContext;
-/*@=exporttype@*/
+typedef struct poptContext_s * poptContext;
-/** \ingroup popt
+/**
*/
#ifndef __cplusplus
-/*@-exporttype -typeuse@*/
typedef struct poptOption * poptOption;
-/*@=exporttype =typeuse@*/
#endif
-/*@-exportconst@*/
+/**
+ */
enum poptCallbackReason {
POPT_CALLBACK_REASON_PRE = 0,
POPT_CALLBACK_REASON_POST = 1,
POPT_CALLBACK_REASON_OPTION = 2
};
-/*@=exportconst@*/
#ifdef __cplusplus
extern "C" {
#endif
-/*@-type@*/
-/** \ingroup popt
+/**
* Table callback prototype.
* @param con context
* @param reason reason for callback
@@ -221,13 +192,18 @@ extern "C" {
*/
typedef void (*poptCallbackType) (poptContext con,
enum poptCallbackReason reason,
- /*@null@*/ const struct poptOption * opt,
- /*@null@*/ const char * arg,
- /*@null@*/ const void * data)
- /*@globals internalState @*/
- /*@modifies internalState @*/;
+ const struct poptOption * opt,
+ const char * arg,
+ const void * data);
-/** \ingroup popt
+/**
+ * Destroy context.
+ * @param con context
+ * @return NULL always
+ */
+poptContext poptFreeContext( poptContext con);
+
+/**
* Initialize popt context.
* @param name context name (usually argv[0] program name)
* @param argc no. of arguments
@@ -236,97 +212,90 @@ typedef void (*poptCallbackType) (poptContext con,
* @param flags or'd POPT_CONTEXT_* bits
* @return initialized popt context
*/
-/*@only@*/ /*@null@*/
poptContext poptGetContext(
- /*@dependent@*/ /*@keep@*/ const char * name,
- int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
- /*@dependent@*/ /*@keep@*/ const struct poptOption * options,
- int flags)
- /*@*/;
+ const char * name,
+ int argc, const char ** argv,
+ const struct poptOption * options,
+ unsigned int flags);
-/** \ingroup popt
+/**
+ * Destroy context (alternative implementation).
+ * @param con context
+ * @return NULL always
+ */
+poptContext poptFini( poptContext con);
+
+/**
+ * Initialize popt context (alternative implementation).
+ * This routine does poptGetContext() and then poptReadConfigFiles().
+ * @param argc no. of arguments
+ * @param argv argument array
+ * @param options address of popt option table
+ * @param configPaths colon separated file path(s) to read.
+ * @return initialized popt context (NULL on error).
+ */
+poptContext poptInit(int argc, const char ** argv,
+ const struct poptOption * options,
+ const char * configPaths);
+
+/**
* Reinitialize popt context.
* @param con context
*/
-/*@unused@*/
-void poptResetContext(/*@null@*/poptContext con)
- /*@modifies con @*/;
+void poptResetContext(poptContext con);
-/** \ingroup popt
+/**
* Return value of next option found.
* @param con context
* @return next option val, -1 on last item, POPT_ERROR_* on error
*/
-int poptGetNextOpt(/*@null@*/poptContext con)
- /*@globals fileSystem, internalState @*/
- /*@modifies con, fileSystem, internalState @*/;
+int poptGetNextOpt(poptContext con);
-/** \ingroup popt
+/**
* Return next option argument (if any).
* @param con context
* @return option argument, NULL if no argument is available
*/
-/*@observer@*/ /*@null@*/ /*@unused@*/
-const char * poptGetOptArg(/*@null@*/poptContext con)
- /*@modifies con @*/;
+char * poptGetOptArg(poptContext con);
-/** \ingroup popt
+/**
* Return next argument.
* @param con context
* @return next argument, NULL if no argument is available
*/
-/*@observer@*/ /*@null@*/ /*@unused@*/
-const char * poptGetArg(/*@null@*/poptContext con)
- /*@modifies con @*/;
+const char * poptGetArg(poptContext con);
-/** \ingroup popt
+/**
* Peek at current argument.
* @param con context
* @return current argument, NULL if no argument is available
*/
-/*@observer@*/ /*@null@*/ /*@unused@*/
-const char * poptPeekArg(/*@null@*/poptContext con)
- /*@*/;
+const char * poptPeekArg(poptContext con);
-/** \ingroup popt
+/**
* Return remaining arguments.
* @param con context
* @return argument array, NULL terminated
*/
-/*@observer@*/ /*@null@*/
-const char ** poptGetArgs(/*@null@*/poptContext con)
- /*@modifies con @*/;
+const char ** poptGetArgs(poptContext con);
-/** \ingroup popt
+/**
* Return the option which caused the most recent error.
* @param con context
* @param flags
* @return offending option
*/
-/*@observer@*/
-const char * poptBadOption(/*@null@*/poptContext con, int flags)
- /*@*/;
-
-/** \ingroup popt
- * Destroy context.
- * @param con context
- * @return NULL always
- */
-/*@null@*/
-poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
- /*@modifies con @*/;
+const char * poptBadOption(poptContext con, unsigned int flags);
-/** \ingroup popt
+/**
* Add arguments to context.
* @param con context
* @param argv argument array, NULL terminated
* @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
*/
-/*@unused@*/
-int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
- /*@modifies con @*/;
+int poptStuffArgs(poptContext con, const char ** argv);
-/** \ingroup popt
+/**
* Add alias to context.
* @todo Pass alias by reference, not value.
* @deprecated Use poptAddItem instead.
@@ -335,44 +304,64 @@ int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
* @param flags (unused)
* @return 0 on success
*/
-/*@unused@*/
-int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
- /*@modifies con @*/;
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
-/** \ingroup popt
+/**
* Add alias/exec item to context.
* @param con context
* @param newItem alias/exec item to add
* @param flags 0 for alias, 1 for exec
* @return 0 on success
*/
-int poptAddItem(poptContext con, poptItem newItem, int flags)
- /*@modifies con @*/;
+int poptAddItem(poptContext con, poptItem newItem, int flags);
-/** \ingroup popt
+/**
+ * Test path/file for config file sanity (regular file, permissions etc)
+ * @param fn file name
+ * @return 1 on OK, 0 on NOTOK.
+ */
+int poptSaneFile(const char * fn);
+
+/**
+ * Read a file into a buffer.
+ * @param fn file name
+ * @retval *bp buffer (malloc'd) (or NULL)
+ * @retval *nbp no. of bytes in buffer (including final NUL) (or NULL)
+ * @param flags 1 to trim escaped newlines
+ * return 0 on success
+ */
+int poptReadFile(const char * fn, char ** bp,
+ size_t * nbp, int flags);
+#define POPT_READFILE_TRIMNEWLINES 1
+
+/**
* Read configuration file.
* @param con context
* @param fn file name to read
* @return 0 on success, POPT_ERROR_ERRNO on failure
*/
-int poptReadConfigFile(poptContext con, const char * fn)
- /*@globals errno, fileSystem, internalState @*/
- /*@modifies con->execs, con->numExecs,
- errno, fileSystem, internalState @*/;
+int poptReadConfigFile(poptContext con, const char * fn);
-/** \ingroup popt
+/**
+ * Read configuration file(s).
+ * Colon separated files to read, looping over poptReadConfigFile().
+ * Note that an '@' character preceding a path in the list will
+ * also perform additional sanity checks on the file before reading.
+ * @param con context
+ * @param paths colon separated file name(s) to read
+ * @return 0 on success, POPT_ERROR_BADCONFIG on failure
+ */
+int poptReadConfigFiles(poptContext con, const char * paths);
+
+/**
* Read default configuration from /etc/popt and $HOME/.popt.
* @param con context
* @param useEnv (unused)
* @return 0 on success, POPT_ERROR_ERRNO on failure
*/
-/*@unused@*/
-int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
- /*@globals fileSystem, internalState @*/
- /*@modifies con->execs, con->numExecs,
- fileSystem, internalState @*/;
+int poptReadDefaultConfig(poptContext con, int useEnv);
-/** \ingroup popt
+/**
* Duplicate an argument array.
* @note: The argument array is malloc'd as a single area, so only argv must
* be free'd.
@@ -383,12 +372,11 @@ int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
* @retval argvPtr address of returned argument array
* @return 0 on success, POPT_ERROR_NOARG on failure
*/
-int poptDupArgv(int argc, /*@null@*/ const char **argv,
- /*@null@*/ /*@out@*/ int * argcPtr,
- /*@null@*/ /*@out@*/ const char *** argvPtr)
- /*@modifies *argcPtr, *argvPtr @*/;
+int poptDupArgv(int argc, const char **argv,
+ int * argcPtr,
+ const char *** argvPtr);
-/** \ingroup popt
+/**
* Parse a string into an argument array.
* The parse allows ', ", and \ quoting, but ' is treated the same as " and
* both may include \ quotes.
@@ -400,10 +388,9 @@ int poptDupArgv(int argc, /*@null@*/ const char **argv,
* @retval argvPtr address of returned argument array
*/
int poptParseArgvString(const char * s,
- /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
- /*@modifies *argcPtr, *argvPtr @*/;
+ int * argcPtr, const char *** argvPtr);
-/** \ingroup popt
+/**
* Parses an input configuration file and returns an string that is a
* command line. For use with popt. You must free the return value when done.
*
@@ -418,8 +405,8 @@ bla=bla
this_is = fdsafdas
bad_line=
- reall bad line
- reall bad line = again
+ really bad line
+ really bad line = again
5555= 55555
test = with lots of spaces
\endverbatim
@@ -449,83 +436,82 @@ this_is = fdsafdas
* @return 0 on success
* @see poptParseArgvString
*/
-/*@-fcnuse@*/
-int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
- /*@globals fileSystem @*/
- /*@modifies *fp, *argstrp, fileSystem @*/;
-/*@=fcnuse@*/
+int poptConfigFileToString(FILE *fp, char ** argstrp, int flags);
-/** \ingroup popt
+/**
* Return formatted error string for popt failure.
* @param error popt error
* @return error string
*/
-/*@observer@*/
-const char * poptStrerror(const int error)
- /*@*/;
+const char * poptStrerror(const int error);
-/** \ingroup popt
+/**
* Limit search for executables.
* @param con context
* @param path single path to search for executables
* @param allowAbsolute absolute paths only?
*/
-/*@unused@*/
-void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
- /*@modifies con @*/;
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
-/** \ingroup popt
+/**
* Print detailed description of options.
* @param con context
- * @param fp ouput file handle
+ * @param fp output file handle
* @param flags (unused)
*/
-void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/;
+void poptPrintHelp(poptContext con, FILE * fp, int flags);
-/** \ingroup popt
+/**
* Print terse description of options.
* @param con context
- * @param fp ouput file handle
+ * @param fp output file handle
* @param flags (unused)
*/
-void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/;
+void poptPrintUsage(poptContext con, FILE * fp, int flags);
-/** \ingroup popt
+/**
* Provide text to replace default "[OPTION...]" in help/usage output.
* @param con context
* @param text replacement text
*/
-/*@-fcnuse@*/
-void poptSetOtherOptionHelp(poptContext con, const char * text)
- /*@modifies con @*/;
-/*@=fcnuse@*/
+void poptSetOtherOptionHelp(poptContext con, const char * text);
-/** \ingroup popt
+/**
* Return argv[0] from context.
* @param con context
* @return argv[0]
*/
-/*@-fcnuse@*/
-/*@observer@*/
-const char * poptGetInvocationName(poptContext con)
- /*@*/;
-/*@=fcnuse@*/
+const char * poptGetInvocationName(poptContext con);
-/** \ingroup popt
+/**
* Shuffle argv pointers to remove stripped args, returns new argc.
* @param con context
* @param argc no. of args
* @param argv arg vector
* @return new argc
*/
-/*@-fcnuse@*/
-int poptStrippedArgv(poptContext con, int argc, char ** argv)
- /*@modifies *argv @*/;
-/*@=fcnuse@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv);
+
+/**
+ * Add a string to an argv array.
+ * @retval *argvp argv array
+ * @param argInfo (unused)
+ * @param val string arg to add (using strdup)
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+int poptSaveString(const char *** argvp, unsigned int argInfo,
+ const char * val);
+
+/**
+ * Save a long long, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg integer pointer, aligned on int boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLongLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+int poptSaveLongLong(long long * arg, unsigned int argInfo,
+ long long aLongLong);
/**
* Save a long, performing logical operation with value.
@@ -535,12 +521,17 @@ int poptStrippedArgv(poptContext con, int argc, char ** argv)
* @param aLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
-/*@-incondefs@*/
-/*@unused@*/
-int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
- /*@modifies *arg @*/
- /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
-/*@=incondefs@*/
+int poptSaveLong(long * arg, unsigned int argInfo, long aLong);
+
+/**
+ * Save a short integer, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg short pointer, aligned on short boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+int poptSaveShort(short * arg, unsigned int argInfo, long aLong);
/**
* Save an integer, performing logical operation with value.
@@ -550,14 +541,40 @@ int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
* @param aLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
-/*@-incondefs@*/
-/*@unused@*/
-int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
- /*@modifies *arg @*/
- /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
-/*@=incondefs@*/
+int poptSaveInt(int * arg, unsigned int argInfo, long aLong);
+
+/* The bit set typedef. */
+typedef struct poptBits_s {
+ unsigned int bits[1];
+} * poptBits;
+
+#define _POPT_BITS_N 1024U /*!< estimated population */
+#define _POPT_BITS_M ((3U * _POPT_BITS_N) / 2U)
+#define _POPT_BITS_K 16U /*!< no. of linear hash combinations */
+
+extern unsigned int _poptBitsN;
+extern unsigned int _poptBitsM;
+extern unsigned int _poptBitsK;
+
+int poptBitsAdd(poptBits bits, const char * s);
+int poptBitsChk(poptBits bits, const char * s);
+int poptBitsClr(poptBits bits);
+int poptBitsDel(poptBits bits, const char * s);
+int poptBitsIntersect(poptBits * ap, const poptBits b);
+int poptBitsUnion(poptBits * ap, const poptBits b);
+int poptBitsArgs(poptContext con, poptBits * ap);
+
+/**
+ * Save a string into a bit set (experimental).
+ * @retval *bits bit set (lazily malloc'd if NULL)
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param s string to add to bit set
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+int poptSaveBits(poptBits * bitsp, unsigned int argInfo,
+ const char * s);
+
-/*@=type@*/
#ifdef __cplusplus
}
#endif
diff --git a/popt/poptconfig.c b/popt/poptconfig.c
index 9733d1529..bf201e26f 100644
--- a/popt/poptconfig.c
+++ b/popt/poptconfig.c
@@ -1,5 +1,5 @@
/** \ingroup popt
- * \file popt/poptconfig.c
+ * @file
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -8,54 +8,300 @@
#include "system.h"
#include "poptint.h"
-/*@access poptContext @*/
+#include
+#include
+#include
+#include
-/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
-static void configLine(poptContext con, char * line)
- /*@modifies con @*/
+#if defined(HAVE_FNMATCH_H)
+#include
+
+#endif
+
+#if defined(HAVE_GLOB_H)
+#include
+
+#if !defined(HAVE_GLOB_PATTERN_P)
+/* Return nonzero if PATTERN contains any metacharacters.
+ Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
+static int
+glob_pattern_p (const char * pattern, int quote)
+{
+ const char * p;
+ int open = 0;
+
+ for (p = pattern; *p != '\0'; ++p)
+ switch (*p) {
+ case '?':
+ case '*':
+ return 1;
+ break;
+ case '\\':
+ if (quote && p[1] != '\0')
+ ++p;
+ break;
+ case '[':
+ open = 1;
+ break;
+ case ']':
+ if (open)
+ return 1;
+ break;
+ }
+ return 0;
+}
+#endif /* !defined(__GLIBC__) */
+
+static int poptGlobFlags = 0;
+
+static int poptGlob_error(UNUSED(const char * epath),
+ UNUSED(int eerrno))
+{
+ return 1;
+}
+#endif /* HAVE_GLOB_H */
+
+/**
+ * Return path(s) from a glob pattern.
+ * @param con context
+ * @param pattern glob pattern
+ * @retval *acp no. of paths
+ * @retval *avp array of paths
+ * @return 0 on success
+ */
+static int poptGlob(UNUSED(poptContext con), const char * pattern,
+ int * acp, const char *** avp)
+{
+ const char * pat = pattern;
+ int rc = 0; /* assume success */
+
+#if defined(HAVE_GLOB_H)
+ if (glob_pattern_p(pat, 0)) {
+ glob_t _g, *pglob = &_g;
+
+ if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
+ if (acp) {
+ *acp = (int) pglob->gl_pathc;
+ pglob->gl_pathc = 0;
+ }
+ if (avp) {
+ *avp = (const char **) pglob->gl_pathv;
+ pglob->gl_pathv = NULL;
+ }
+ globfree(pglob);
+ } else if (rc == GLOB_NOMATCH) {
+ *avp = NULL;
+ *acp = 0;
+ rc = 0;
+ } else
+ rc = POPT_ERROR_ERRNO;
+ } else
+#endif /* HAVE_GLOB_H */
+ {
+ if (acp)
+ *acp = 1;
+ if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
+ (*avp)[0] = xstrdup(pat);
+ }
+
+ return rc;
+}
+
+
+int poptSaneFile(const char * fn)
+{
+ struct stat sb;
+
+ if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
+ return 0;
+ if (stat(fn, &sb) == -1)
+ return 0;
+ if (!S_ISREG(sb.st_mode))
+ return 0;
+ if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+ return 0;
+ return 1;
+}
+
+int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
+{
+ int fdno;
+ char * b = NULL;
+ off_t nb = 0;
+ char * s, * t, * se;
+ int rc = POPT_ERROR_ERRNO; /* assume failure */
+
+ fdno = open(fn, O_RDONLY);
+ if (fdno < 0)
+ goto exit;
+
+ if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
+ || (uintmax_t)nb >= SIZE_MAX
+ || lseek(fdno, 0, SEEK_SET) == (off_t)-1
+ || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
+ || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
+ {
+ int oerrno = errno;
+ (void) close(fdno);
+ if (nb != (off_t)-1 && (uintmax_t)nb >= SIZE_MAX)
+ errno = -EOVERFLOW;
+ else
+ errno = oerrno;
+ goto exit;
+ }
+ if (close(fdno) == -1)
+ goto exit;
+ if (b == NULL) {
+ rc = POPT_ERROR_MALLOC;
+ goto exit;
+ }
+ rc = 0;
+
+ /* Trim out escaped newlines. */
+ if (flags & POPT_READFILE_TRIMNEWLINES)
+ {
+ for (t = b, s = b, se = b + nb; *s && s < se; s++) {
+ switch (*s) {
+ case '\\':
+ if (s[1] == '\n') {
+ s++;
+ continue;
+ }
+ /* fallthrough */
+ default:
+ *t++ = *s;
+ break;
+ }
+ }
+ *t++ = '\0';
+ nb = (off_t)(t - b);
+ }
+
+exit:
+ if (rc != 0) {
+ if (b)
+ free(b);
+ b = NULL;
+ nb = 0;
+ }
+ if (bp)
+ *bp = b;
+ else if (b)
+ free(b);
+ if (nbp)
+ *nbp = (size_t)nb;
+ return rc;
+}
+
+/**
+ * Check for application match.
+ * @param con context
+ * @param s config application name
+ * return 0 if config application matches
+ */
+static int configAppMatch(poptContext con, const char * s)
{
- size_t nameLength;
+ int rc = 1;
+
+ if (con->appName == NULL) /* XXX can't happen. */
+ return rc;
+
+#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
+ if (glob_pattern_p(s, 1)) {
+ static int flags = FNM_PATHNAME | FNM_PERIOD;
+#ifdef FNM_EXTMATCH
+ flags |= FNM_EXTMATCH;
+#endif
+ rc = fnmatch(s, con->appName, flags);
+ } else
+#endif
+ rc = strcmp(s, con->appName);
+ return rc;
+}
+
+static int poptConfigLine(poptContext con, char * line)
+{
+ char *b = NULL;
+ size_t nb = 0;
+ char * se = line;
+ const char * appName;
const char * entryType;
const char * opt;
- poptItem item = (poptItem) alloca(sizeof(*item));
+ struct poptItem_s item_buf;
+ poptItem item = &item_buf;
int i, j;
+ int rc = POPT_ERROR_BADCONFIG;
if (con->appName == NULL)
- return;
- nameLength = strlen(con->appName);
+ goto exit;
-/*@-boundswrite@*/
memset(item, 0, sizeof(*item));
- if (strncmp(line, con->appName, nameLength)) return;
+ appName = se;
+ while (*se != '\0' && !_isspaceptr(se)) se++;
+ if (*se == '\0')
+ goto exit;
+ else
+ *se++ = '\0';
- line += nameLength;
- if (*line == '\0' || !isSpace(line)) return;
+ if (configAppMatch(con, appName)) goto exit;
- while (*line != '\0' && isSpace(line)) line++;
- entryType = line;
- while (*line == '\0' || !isSpace(line)) line++;
- *line++ = '\0';
+ while (*se != '\0' && _isspaceptr(se)) se++;
+ entryType = se;
+ while (*se != '\0' && !_isspaceptr(se)) se++;
+ if (*se != '\0') *se++ = '\0';
- while (*line != '\0' && isSpace(line)) line++;
- if (*line == '\0') return;
- opt = line;
- while (*line == '\0' || !isSpace(line)) line++;
- *line++ = '\0';
+ while (*se != '\0' && _isspaceptr(se)) se++;
+ if (*se == '\0') goto exit;
+ opt = se;
+ while (*se != '\0' && !_isspaceptr(se)) se++;
+ if (opt[0] == '-' && *se == '\0') goto exit;
+ if (*se != '\0') *se++ = '\0';
- while (*line != '\0' && isSpace(line)) line++;
- if (*line == '\0') return;
+ while (*se != '\0' && _isspaceptr(se)) se++;
+ if (opt[0] == '-' && *se == '\0') goto exit;
- /*@-temptrans@*/ /* FIX: line alias is saved */
if (opt[0] == '-' && opt[1] == '-')
item->option.longName = opt + 2;
else if (opt[0] == '-' && opt[2] == '\0')
item->option.shortName = opt[1];
- /*@=temptrans@*/
+ else {
+ const char * fn = opt;
+
+ /* XXX handle globs and directories in fn? */
+ if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
+ goto exit;
+ if (b == NULL || nb == 0)
+ goto exit;
+
+ /* Append remaining text to the interpolated file option text. */
+ if (*se != '\0') {
+ size_t nse = strlen(se) + 1;
+ if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
+ goto exit;
+ (void) stpcpy( stpcpy(&b[nb-1], " "), se);
+ nb += nse;
+ }
+ se = b;
+
+ /* Use the basename of the path as the long option name. */
+ { const char * longName = strrchr(fn, '/');
+ if (longName != NULL)
+ longName++;
+ else
+ longName = fn;
+ if (longName == NULL) /* XXX can't happen. */
+ goto exit;
+ /* Single character basenames are treated as short options. */
+ if (longName[1] != '\0')
+ item->option.longName = longName;
+ else
+ item->option.shortName = longName[0];
+ }
+ }
- if (poptParseArgvString(line, &item->argc, &item->argv)) return;
+ if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
- /*@-modobserver@*/
item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
for (i = 0, j = 0; i < item->argc; i++, j++) {
const char * f;
@@ -81,103 +327,183 @@ static void configLine(poptContext con, char * line)
item->argv[j] = NULL;
item->argc = j;
}
- /*@=modobserver@*/
-/*@=boundswrite@*/
- /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
if (!strcmp(entryType, "alias"))
- (void) poptAddItem(con, item, 0);
+ rc = poptAddItem(con, item, 0);
else if (!strcmp(entryType, "exec"))
- (void) poptAddItem(con, item, 1);
- /*@=nullstate@*/
+ rc = poptAddItem(con, item, 1);
+exit:
+ rc = 0; /* XXX for now, always return success */
+ if (b)
+ free(b);
+ return rc;
}
-/*@=compmempass@*/
int poptReadConfigFile(poptContext con, const char * fn)
{
- const char * file, * chptr, * end;
- char * buf;
-/*@dependent@*/ char * dst;
- int fd, rc;
- off_t fileLength;
-
- fd = open(fn, O_RDONLY);
- if (fd < 0)
- return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
-
- fileLength = lseek(fd, 0, SEEK_END);
- if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
- rc = errno;
- (void) close(fd);
- errno = rc;
- return POPT_ERROR_ERRNO;
- }
+ char * b = NULL, *be;
+ size_t nb = 0;
+ const char *se;
+ char *t = NULL, *te;
+ int rc;
- file = alloca(fileLength + 1);
- if (read(fd, (char *)file, fileLength) != fileLength) {
- rc = errno;
- (void) close(fd);
- errno = rc;
- return POPT_ERROR_ERRNO;
+ if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
+ return (errno == ENOENT ? 0 : rc);
+ if (b == NULL || nb == 0) {
+ rc = POPT_ERROR_BADCONFIG;
+ goto exit;
}
- if (close(fd) == -1)
- return POPT_ERROR_ERRNO;
-/*@-boundswrite@*/
- dst = buf = alloca(fileLength + 1);
+ if ((t = malloc(nb + 1)) == NULL)
+ goto exit;
+ te = t;
- chptr = file;
- end = (file + fileLength);
- /*@-infloops@*/ /* LCL: can't detect chptr++ */
- while (chptr < end) {
- switch (*chptr) {
+ be = (b + nb);
+ for (se = b; se < be; se++) {
+ switch (*se) {
case '\n':
- *dst = '\0';
- dst = buf;
- while (*dst && isSpace(dst)) dst++;
- if (*dst && *dst != '#')
- configLine(con, dst);
- chptr++;
- /*@switchbreak@*/ break;
+ *te = '\0';
+ te = t;
+ while (*te && _isspaceptr(te)) te++;
+ if (*te && *te != '#')
+ if ((rc = poptConfigLine(con, te)) != 0)
+ goto exit;
+ break;
case '\\':
- *dst++ = *chptr++;
- if (chptr < end) {
- if (*chptr == '\n')
- dst--, chptr++;
- /* \ at the end of a line does not insert a \n */
- else
- *dst++ = *chptr++;
+ *te = *se++;
+ /* \ at the end of a line does not insert a \n */
+ if (se < be && *se != '\n') {
+ te++;
+ *te++ = *se;
}
- /*@switchbreak@*/ break;
+ break;
default:
- *dst++ = *chptr++;
- /*@switchbreak@*/ break;
+ *te++ = *se;
+ break;
}
}
- /*@=infloops@*/
-/*@=boundswrite@*/
+ rc = 0;
- return 0;
+exit:
+ free(t);
+ if (b)
+ free(b);
+ return rc;
}
-int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
+int poptReadConfigFiles(poptContext con, const char * paths)
{
- char * fn, * home;
- int rc;
+ char * buf = (paths ? xstrdup(paths) : NULL);
+ const char * p;
+ char * pe;
+ int rc = 0; /* assume success */
+
+ for (p = buf; p != NULL && *p != '\0'; p = pe) {
+ const char ** av = NULL;
+ int ac = 0;
+ int i;
+ int xx;
+
+ /* locate start of next path element */
+ pe = strchr(p, ':');
+ if (pe != NULL && *pe == ':')
+ *pe++ = '\0';
+ else
+ pe = (char *) (p + strlen(p));
+
+ xx = poptGlob(con, p, &ac, &av);
+
+ /* work-off each resulting file from the path element */
+ for (i = 0; i < ac; i++) {
+ const char * fn = av[i];
+ if (!poptSaneFile(fn))
+ continue;
+ xx = poptReadConfigFile(con, fn);
+ if (xx && rc == 0)
+ rc = xx;
+ free((void *)av[i]);
+ av[i] = NULL;
+ }
+ free(av);
+ av = NULL;
+ }
- if (con->appName == NULL) return 0;
+ if (buf)
+ free(buf);
- rc = poptReadConfigFile(con, "/etc/popt");
- if (rc) return rc;
+ return rc;
+}
+
+int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
+{
+ char * home;
+ struct stat sb;
+ int rc = 0; /* assume success */
+
+ if (con->appName == NULL) goto exit;
+
+ rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
+ if (rc) goto exit;
+
+#if defined(HAVE_GLOB_H)
+ if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
+ const char ** av = NULL;
+ int ac = 0;
+ int i;
+
+ if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
+ for (i = 0; rc == 0 && i < ac; i++) {
+ const char * fn = av[i];
+ if (!poptSaneFile(fn))
+ continue;
+ rc = poptReadConfigFile(con, fn);
+ free((void *)av[i]);
+ av[i] = NULL;
+ }
+ free(av);
+ av = NULL;
+ }
+ }
+ if (rc) goto exit;
+#endif
if ((home = getenv("HOME"))) {
- size_t bufsize = strlen(home) + 20;
- fn = alloca(bufsize);
- if (fn == NULL) return 0;
- snprintf(fn, bufsize, "%s/.popt", home);
- rc = poptReadConfigFile(con, fn);
- if (rc) return rc;
+ char * fn = malloc(strlen(home) + 20);
+ if (fn != NULL) {
+ (void) stpcpy(stpcpy(fn, home), "/.popt");
+ rc = poptReadConfigFile(con, fn);
+ free(fn);
+ } else
+ rc = POPT_ERROR_ERRNO;
+ if (rc) goto exit;
}
- return 0;
+exit:
+ return rc;
+}
+
+poptContext
+poptFini(poptContext con)
+{
+ return poptFreeContext(con);
+}
+
+poptContext
+poptInit(int argc, const char ** argv,
+ const struct poptOption * options, const char * configPaths)
+{
+ poptContext con = NULL;
+ const char * argv0;
+
+ if (argv == NULL || argv[0] == NULL || options == NULL)
+ return con;
+
+ if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
+ else argv0 = argv[0];
+
+ con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
+ if (con != NULL&& poptReadConfigFiles(con, configPaths))
+ con = poptFini(con);
+
+ return con;
}
diff --git a/popt/popthelp.c b/popt/popthelp.c
index 6a009766d..6738f6add 100644
--- a/popt/popthelp.c
+++ b/popt/popthelp.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/** \ingroup popt
- * \file popt/popthelp.c
+ * @file
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -10,14 +10,16 @@
#include "system.h"
-/*#define POPT_WCHAR_HACK*/
-#ifdef POPT_WCHAR_HACK
+#define POPT_USE_TIOCGWINSZ
+#ifdef POPT_USE_TIOCGWINSZ
+#include
+#endif
+
+#ifdef HAVE_MBSRTOWCS
#include /* for mbsrtowcs */
-/*@access mbstate_t @*/
#endif
#include "poptint.h"
-/*@access poptContext@*/
/**
* Display arguments.
@@ -27,29 +29,29 @@
* @param arg (unused)
* @param data (unused)
*/
+NORETURN
static void displayArgs(poptContext con,
- /*@unused@*/ UNUSED(enum poptCallbackReason foo),
+ UNUSED(enum poptCallbackReason foo),
struct poptOption * key,
- /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data))
- /*@globals fileSystem@*/
- /*@modifies fileSystem@*/
+ UNUSED(const char * arg),
+ UNUSED(void * data))
{
if (key->shortName == '?')
poptPrintHelp(con, stdout, 0);
else
poptPrintUsage(con, stdout, 0);
+
+ poptFreeContext(con);
exit(0);
}
#ifdef NOTYET
-/*@unchecked@*/
static int show_option_defaults = 0;
#endif
/**
* Empty table marker to enable displaying popt alias/exec options.
*/
-/*@observer@*/ /*@unchecked@*/
struct poptOption poptAliasOptions[] = {
POPT_TABLEEND
};
@@ -57,45 +59,88 @@ struct poptOption poptAliasOptions[] = {
/**
* Auto help table options.
*/
-/*@-castfcnptr@*/
-/*@observer@*/ /*@unchecked@*/
struct poptOption poptHelpOptions[] = {
- { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
- { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
- { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
+ { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
POPT_TABLEEND
} ;
-/*@observer@*/ /*@unchecked@*/
static struct poptOption poptHelpOptions2[] = {
-/*@-readonlytrans@*/
- { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
-/*@=readonlytrans@*/
- { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
- { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
- { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ { NULL, '\0', POPT_ARG_INTL_DOMAIN, (void *)PACKAGE, 0, NULL, NULL},
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
+ { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
#ifdef NOTYET
{ "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
N_("Display option defaults in message"), NULL },
#endif
+ { NULL, '\0', 0, NULL, 0, N_("Terminate options"), NULL },
POPT_TABLEEND
} ;
-/*@observer@*/ /*@unchecked@*/
struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
-/*@=castfcnptr@*/
+
+#define _POPTHELP_MAXLINE ((size_t)79)
+
+typedef struct columns_s {
+ size_t cur;
+ size_t max;
+} * columns_t;
+
+/**
+ * Return no. of columns in output window.
+ * @param fp FILE
+ * @return no. of columns
+ */
+static size_t maxColumnWidth(FILE *fp)
+{
+ size_t maxcols = _POPTHELP_MAXLINE;
+#if defined(TIOCGWINSZ)
+ struct winsize ws;
+ int fdno = fileno(fp ? fp : stdout);
+
+ memset(&ws, 0, sizeof(ws));
+ if (fdno >= 0 && !ioctl(fdno, (unsigned long)TIOCGWINSZ, &ws)) {
+ size_t ws_col = (size_t)ws.ws_col;
+ if (ws_col > maxcols && ws_col < (size_t)256)
+ maxcols = ws_col - 1;
+ }
+#endif
+ return maxcols;
+}
/**
- * @param table option(s)
+ * Determine number of display characters in a string.
+ * @param s string
+ * @return no. of display characters.
*/
-/*@observer@*/ /*@null@*/ static const char *
-getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
- /*@*/
+static inline size_t stringDisplayWidth(const char *s)
{
- const struct poptOption *opt;
+ size_t n = strlen(s);
+#ifdef HAVE_MBSRTOWCS
+ mbstate_t t;
- if (table != NULL)
- for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+ memset ((void *)&t, 0, sizeof (t)); /* In initial state. */
+ /* Determine number of display characters. */
+ n = mbsrtowcs (NULL, &s, n, &t);
+#else
+ n = 0;
+ for (; *s; s = POPT_next_char(s))
+ n++;
+#endif
+
+ return n;
+}
+
+/**
+ * @param opt option(s)
+ */
+static const char *
+getTableTranslationDomain(const struct poptOption *opt)
+{
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
return opt->arg;
}
@@ -106,32 +151,46 @@ getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
* @param opt option(s)
* @param translation_domain translation domain
*/
-/*@observer@*/ /*@null@*/ static const char *
+static const char *
getArgDescrip(const struct poptOption * opt,
- /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@=paramuse@*/
- /*@*/
+ /* FIX: i18n macros disabled with lclint */
+ const char * translation_domain)
{
- if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
-
- if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
- if (opt->argDescrip) return POPT_(opt->argDescrip);
-
- if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+ if (!poptArgType(opt)) return NULL;
+
+ if (poptArgType(opt) == POPT_ARG_MAINCALL)
+ return opt->argDescrip;
+ if (poptArgType(opt) == POPT_ARG_ARGV)
+ return opt->argDescrip;
+
+ if (opt->argDescrip) {
+ /* Some strings need popt library, not application, i18n domain. */
+ if (opt == (poptHelpOptions + 1)
+ || opt == (poptHelpOptions + 2)
+ || !strcmp(opt->argDescrip, N_("Help options:"))
+ || !strcmp(opt->argDescrip, N_("Options implemented via popt alias/exec:")))
+ return POPT_(opt->argDescrip);
+
+ /* Use the application i18n domain. */
+ return D_(translation_domain, opt->argDescrip);
+ }
- switch (opt->argInfo & POPT_ARG_MASK) {
- /*case POPT_ARG_NONE: return POPT_("NONE");*/ /* impossible */
+ switch (poptArgType(opt)) {
+ case POPT_ARG_NONE: return POPT_("NONE");
#ifdef DYING
case POPT_ARG_VAL: return POPT_("VAL");
#else
case POPT_ARG_VAL: return NULL;
#endif
case POPT_ARG_INT: return POPT_("INT");
+ case POPT_ARG_SHORT: return POPT_("SHORT");
case POPT_ARG_LONG: return POPT_("LONG");
+ case POPT_ARG_LONGLONG: return POPT_("LONGLONG");
case POPT_ARG_STRING: return POPT_("STRING");
case POPT_ARG_FLOAT: return POPT_("FLOAT");
case POPT_ARG_DOUBLE: return POPT_("DOUBLE");
+ case POPT_ARG_MAINCALL: return NULL;
+ case POPT_ARG_ARGV: return NULL;
default: return POPT_("ARG");
}
}
@@ -143,59 +202,62 @@ getArgDescrip(const struct poptOption * opt,
* @param translation_domain translation domain
* @return
*/
-static /*@only@*/ /*@null@*/ char *
+static char *
singleOptionDefaultValue(size_t lineLength,
const struct poptOption * opt,
- /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@=paramuse@*/
- /*@*/
+ /* FIX: i18n macros disabled with lclint */
+ const char * translation_domain)
{
const char * defstr = D_(translation_domain, "default");
- size_t limit, bufsize = 4*lineLength + 1;
- char * le = malloc(bufsize);
+ char * le = malloc(4*lineLength + 1);
char * l = le;
if (le == NULL) return NULL; /* XXX can't happen */
-/*@-boundswrite@*/
+ *le = '\0';
*le++ = '(';
- le += strlcpy(le, defstr, bufsize - 3);
+ le = stpcpy(le, defstr);
*le++ = ':';
*le++ = ' ';
- limit = bufsize - (le - l) - 1; /* -1 for closing paren */
- if (opt->arg) /* XXX programmer error */
- switch (opt->argInfo & POPT_ARG_MASK) {
+ if (opt->arg) { /* XXX programmer error */
+ poptArg arg = { .ptr = opt->arg };
+ switch (poptArgType(opt)) {
case POPT_ARG_VAL:
case POPT_ARG_INT:
- { long aLong = *((int *)opt->arg);
- le += snprintf(le, limit, "%ld", aLong);
- } break;
+ le += sprintf(le, "%d", arg.intp[0]);
+ break;
+ case POPT_ARG_SHORT:
+ le += sprintf(le, "%hd", arg.shortp[0]);
+ break;
case POPT_ARG_LONG:
- { long aLong = *((long *)opt->arg);
- le += snprintf(le, limit, "%ld", aLong);
- } break;
+ le += sprintf(le, "%ld", arg.longp[0]);
+ break;
+ case POPT_ARG_LONGLONG:
+ le += sprintf(le, "%lld", arg.longlongp[0]);
+ break;
case POPT_ARG_FLOAT:
- { double aDouble = *((float *)opt->arg);
- le += snprintf(le, limit, "%g", aDouble);
+ { double aDouble = (double) arg.floatp[0];
+ le += sprintf(le, "%g", aDouble);
} break;
case POPT_ARG_DOUBLE:
- { double aDouble = *((double *)opt->arg);
- le += snprintf(le, limit, "%g", aDouble);
- } break;
+ le += sprintf(le, "%g", arg.doublep[0]);
+ break;
+ case POPT_ARG_MAINCALL:
+ le += sprintf(le, "%p", opt->arg);
+ break;
+ case POPT_ARG_ARGV:
+ le += sprintf(le, "%p", opt->arg);
+ break;
case POPT_ARG_STRING:
- { const char * s = *(const char **)opt->arg;
- if (s == NULL) {
- le += strlcpy(le, "null", limit);
- } else {
- size_t len;
- limit -= 2; /* make room for quotes */
+ { const char * s = arg.argv[0];
+ if (s == NULL)
+ le = stpcpy(le, "null");
+ else {
+ size_t limit = 4*lineLength - (le - l) - sizeof("\"\")");
+ size_t slen;
*le++ = '"';
- len = strlcpy(le, s, limit);
- if (len >= limit) {
- le += limit - 3 - 1;
- *le++ = '.'; *le++ = '.'; *le++ = '.';
- } else
- le += len;
+ strncpy(le, s, limit); le[limit] = '\0'; le += (slen = strlen(le));
+ if (slen == limit && s[limit])
+ le[-1] = le[-2] = le[-3] = '.';
*le++ = '"';
}
} break;
@@ -203,11 +265,11 @@ singleOptionDefaultValue(size_t lineLength,
default:
l = _free(l);
return NULL;
- /*@notreached@*/ break;
+ break;
}
+ }
*le++ = ')';
*le = '\0';
-/*@=boundswrite@*/
return l;
}
@@ -215,80 +277,101 @@ singleOptionDefaultValue(size_t lineLength,
/**
* Display help text for an option.
* @param fp output file handle
- * @param maxLeftCol largest argument display width
+ * @param columns output display width control
* @param opt option(s)
* @param translation_domain translation domain
*/
-static void singleOptionHelp(FILE * fp, size_t maxLeftCol,
+static void singleOptionHelp(FILE * fp, columns_t columns,
const struct poptOption * opt,
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+ const char * translation_domain)
{
+ size_t maxLeftCol = columns->cur;
size_t indentLength = maxLeftCol + 5;
- size_t lineLength = 79 - indentLength;
+ size_t lineLength = columns->max - indentLength;
const char * help = D_(translation_domain, opt->descrip);
const char * argDescrip = getArgDescrip(opt, translation_domain);
+ /* Display shortName iff printable non-space. */
+ int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
size_t helpLength;
char * defs = NULL;
char * left;
- size_t lelen, limit;
size_t nb = maxLeftCol + 1;
int displaypad = 0;
/* Make sure there's more than enough room in target buffer. */
if (opt->longName) nb += strlen(opt->longName);
+ if (F_ISSET(opt, TOGGLE)) nb += sizeof("[no]") - 1;
if (argDescrip) nb += strlen(argDescrip);
-/*@-boundswrite@*/
left = malloc(nb);
if (left == NULL) return; /* XXX can't happen */
left[0] = '\0';
left[maxLeftCol] = '\0';
- if (opt->longName && opt->shortName)
- snprintf(left, nb, "-%c, %s%s", opt->shortName,
- ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
- opt->longName);
- else if (opt->shortName != '\0')
- snprintf(left, nb, "-%c", opt->shortName);
- else if (opt->longName)
- snprintf(left, nb, "%s%s",
- ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
- opt->longName);
- if (!*left) goto out;
+#define prtlong (opt->longName != NULL) /* XXX splint needs a clue */
+ if (!(prtshort || prtlong))
+ goto out;
+ if (prtshort && prtlong) {
+ const char *dash = F_ISSET(opt, ONEDASH) ? "-" : "--";
+ left[0] = '-';
+ left[1] = opt->shortName;
+ (void) stpcpy(stpcpy(stpcpy(left+2, ", "), dash), opt->longName);
+ } else if (prtshort) {
+ left[0] = '-';
+ left[1] = opt->shortName;
+ left[2] = '\0';
+ } else if (prtlong) {
+ /* XXX --long always padded for alignment with/without "-X, ". */
+ const char *dash = poptArgType(opt) == POPT_ARG_MAINCALL ? ""
+ : (F_ISSET(opt, ONEDASH) ? "-" : "--");
+ const char *longName = opt->longName;
+ const char *toggle;
+ if (F_ISSET(opt, TOGGLE)) {
+ toggle = "[no]";
+ if (longName[0] == 'n' && longName[1] == 'o') {
+ longName += sizeof("no") - 1;
+ if (longName[0] == '-')
+ longName++;
+ }
+ } else
+ toggle = "";
+ (void) stpcpy(stpcpy(stpcpy(stpcpy(left, " "), dash), toggle), longName);
+ }
+#undef prtlong
if (argDescrip) {
char * le = left + strlen(left);
- if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ if (F_ISSET(opt, OPTIONAL))
*le++ = '[';
/* Choose type of output */
- /*@-branchstate@*/
- if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
+ if (F_ISSET(opt, SHOW_DEFAULT)) {
defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
if (defs) {
- size_t bufsize = (help ? strlen(help) : 0) + sizeof " " + strlen(defs);
- char * t = malloc(bufsize);
+ char * t = malloc((help ? strlen(help) : 0) +
+ strlen(defs) + sizeof(" "));
if (t) {
- snprintf(t, bufsize, "%s %s", help ? help : "", defs);
+ char * te = t;
+ if (help)
+ te = stpcpy(te, help);
+ *te++ = ' ';
+ strcpy(te, defs);
defs = _free(defs);
+ defs = t;
}
- defs = t;
}
}
- /*@=branchstate@*/
if (opt->argDescrip == NULL) {
- switch (opt->argInfo & POPT_ARG_MASK) {
+ switch (poptArgType(opt)) {
case POPT_ARG_NONE:
break;
case POPT_ARG_VAL:
#ifdef NOTNOW /* XXX pug ugly nerdy output */
{ long aLong = opt->val;
- int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
- int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
+ int ops = F_ISSET(opt, LOGICALOPS);
+ int negate = F_ISSET(opt, NOT);
/* Don't bother displaying typical values */
if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
@@ -297,111 +380,102 @@ static void singleOptionHelp(FILE * fp, size_t maxLeftCol,
switch (ops) {
case POPT_ARGFLAG_OR:
*le++ = '|';
- /*@innerbreak@*/ break;
+ break;
case POPT_ARGFLAG_AND:
*le++ = '&';
- /*@innerbreak@*/ break;
+ break;
case POPT_ARGFLAG_XOR:
*le++ = '^';
- /*@innerbreak@*/ break;
+ break;
default:
- /*@innerbreak@*/ break;
+ break;
}
*le++ = (opt->longName != NULL ? '=' : ' ');
if (negate) *le++ = '~';
- /*@-formatconst@*/
- limit = nb - (le - left);
- lelen = snprintf(le, limit, (ops ? "0x%lx" : "%ld"), aLong);
- le += lelen >= limit ? limit - 1 : lelen;
- /*@=formatconst@*/
+ le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
*le++ = ']';
}
#endif
break;
case POPT_ARG_INT:
+ case POPT_ARG_SHORT:
case POPT_ARG_LONG:
+ case POPT_ARG_LONGLONG:
case POPT_ARG_FLOAT:
case POPT_ARG_DOUBLE:
case POPT_ARG_STRING:
*le++ = (opt->longName != NULL ? '=' : ' ');
- limit = nb - (le - left);
- lelen = strlcpy(le, argDescrip, limit);
- le += lelen >= limit ? limit - 1 : lelen;
+ le = stpcpy(le, argDescrip);
break;
default:
break;
}
} else {
+ char *leo;
- *le++ = '=';
- limit = nb - (le - left);
- lelen = strlcpy(le, argDescrip, limit);
- if (lelen >= limit)
- lelen = limit - 1;
- le += lelen;
-
-#ifdef POPT_WCHAR_HACK
- { const char * scopy = argDescrip;
- mbstate_t t;
- size_t n;
-
- memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
- /* Determine number of characters. */
- n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+ /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
+ if (!strchr(" =(", argDescrip[0]))
+ *le++ = ((poptArgType(opt) == POPT_ARG_MAINCALL) ? ' ' :
+ (poptArgType(opt) == POPT_ARG_ARGV) ? ' ' :
+ opt->longName == NULL ? ' ' : '=');
+ le = stpcpy(leo = le, argDescrip);
- displaypad = (int) (lelen-n);
- }
-#endif
+ /* Adjust for (possible) wide characters. */
+ displaypad = (int)((le - leo) - stringDisplayWidth(argDescrip));
}
- if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ if (F_ISSET(opt, OPTIONAL))
*le++ = ']';
*le = '\0';
}
-/*@=boundswrite@*/
if (help)
- fprintf(fp," %-*s ", (int)maxLeftCol+displaypad, left);
+ POPT_fprintf(fp," %-*s ", (int)(maxLeftCol+displaypad), left);
else {
- fprintf(fp," %s\n", left);
+ POPT_fprintf(fp," %s\n", left);
goto out;
}
left = _free(left);
-/*@-branchstate@*/
- if (defs) {
+ if (defs)
help = defs;
- defs = NULL;
- }
-/*@=branchstate@*/
helpLength = strlen(help);
-/*@-boundsread@*/
while (helpLength > lineLength) {
const char * ch;
char format[16];
ch = help + lineLength - 1;
- while (ch > help && !isSpace(ch)) ch--;
+ while (ch > help && !_isspaceptr(ch))
+ ch = POPT_prev_char(ch);
if (ch == help) break; /* give up */
- while (ch > (help + 1) && isSpace(ch)) ch--;
- ch++;
+ while (ch > (help + 1) && _isspaceptr(ch))
+ ch = POPT_prev_char (ch);
+ ch = POPT_next_char(ch);
+
+ /*
+ * XXX strdup is necessary to add NUL terminator so that an unknown
+ * no. of (possible) multi-byte characters can be displayed.
+ */
+ { char * fmthelp = xstrdup(help);
+ if (fmthelp) {
+ fmthelp[ch - help] = '\0';
+ sprintf(format, "%%s\n%%%ds", (int) indentLength);
+ POPT_fprintf(fp, format, fmthelp, " ");
+ free(fmthelp);
+ }
+ }
- snprintf(format, sizeof format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength);
- /*@-formatconst@*/
- fprintf(fp, format, help, " ");
- /*@=formatconst@*/
help = ch;
- while (isSpace(help) && *help) help++;
+ while (_isspaceptr(help) && *help)
+ help = POPT_next_char(help);
helpLength = strlen(help);
}
-/*@=boundsread@*/
if (helpLength) fprintf(fp, "%s\n", help);
+ help = NULL;
out:
- /*@-dependenttrans@*/
defs = _free(defs);
- /*@=dependenttrans@*/
left = _free(left);
}
@@ -412,54 +486,45 @@ static void singleOptionHelp(FILE * fp, size_t maxLeftCol,
* @return display width
*/
static size_t maxArgWidth(const struct poptOption * opt,
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@*/
+ const char * translation_domain)
{
size_t max = 0;
size_t len = 0;
- const char * s;
+ const char * argDescrip;
if (opt != NULL)
while (opt->longName || opt->shortName || opt->arg) {
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
- if (opt->arg) /* XXX program error */
- len = maxArgWidth(opt->arg, translation_domain);
+ if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions)
+ arg = poptHelpOptionsI18N;
+ if (arg) /* XXX program error */
+ len = maxArgWidth(arg, translation_domain);
if (len > max) max = len;
- } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ } else if (!F_ISSET(opt, DOC_HIDDEN)) {
len = sizeof(" ")-1;
- if (opt->shortName != '\0') len += sizeof("-X")-1;
- if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
+ /* XXX --long always padded for alignment with/without "-X, ". */
+ len += sizeof("-X, ")-1;
if (opt->longName) {
- len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
- ? sizeof("-")-1 : sizeof("--")-1);
+ len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
len += strlen(opt->longName);
}
- s = getArgDescrip(opt, translation_domain);
-
-#ifdef POPT_WCHAR_HACK
- /* XXX Calculate no. of display characters. */
- if (s) {
- const char * scopy = s;
- mbstate_t t;
- size_t n;
-
-/*@-boundswrite@*/
- memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
-/*@=boundswrite@*/
- /* Determine number of characters. */
- n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
- len += sizeof("=")-1 + n;
+ argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (argDescrip) {
+
+ /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
+ if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
+
+ /* Adjust for (possible) wide characters. */
+ len += stringDisplayWidth(argDescrip);
}
-#else
- if (s)
- len += sizeof("=")-1 + strlen(s);
-#endif
- if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
+ if (F_ISSET(opt, OPTIONAL)) len += sizeof("[]")-1;
if (len > max) max = len;
}
-
opt++;
}
@@ -471,14 +536,13 @@ static size_t maxArgWidth(const struct poptOption * opt,
* @param fp output file handle
* @param items alias/exec array
* @param nitems no. of alias/exec entries
- * @param left largest argument display width
+ * @param columns output display width control
* @param translation_domain translation domain
*/
static void itemHelp(FILE * fp,
- /*@null@*/ poptItem items, int nitems, size_t left,
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+ poptItem items, int nitems,
+ columns_t columns,
+ const char * translation_domain)
{
poptItem item;
int i;
@@ -487,9 +551,8 @@ static void itemHelp(FILE * fp,
for (i = 0, item = items; i < nitems; i++, item++) {
const struct poptOption * opt;
opt = &item->option;
- if ((opt->longName || opt->shortName) &&
- !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
- singleOptionHelp(fp, left, opt, translation_domain);
+ if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
+ singleOptionHelp(fp, columns, opt, translation_domain);
}
}
@@ -498,43 +561,48 @@ static void itemHelp(FILE * fp,
* @param con context
* @param fp output file handle
* @param table option(s)
- * @param left largest argument display width
+ * @param columns output display width control
* @param translation_domain translation domain
*/
static void singleTableHelp(poptContext con, FILE * fp,
- /*@null@*/ const struct poptOption * table, size_t left,
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+ const struct poptOption * table,
+ columns_t columns,
+ const char * translation_domain)
{
const struct poptOption * opt;
const char *sub_transdom;
if (table == poptAliasOptions) {
- itemHelp(fp, con->aliases, con->numAliases, left, NULL);
- itemHelp(fp, con->execs, con->numExecs, left, NULL);
+ itemHelp(fp, con->aliases, con->numAliases, columns, NULL);
+ itemHelp(fp, con->execs, con->numExecs, columns, NULL);
return;
}
if (table != NULL)
- for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
- if ((opt->longName || opt->shortName) &&
- !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
- singleOptionHelp(fp, left, opt, translation_domain);
+ for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+ if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
+ singleOptionHelp(fp, columns, opt, translation_domain);
}
if (table != NULL)
- for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
- if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
+ for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+ void * arg = opt->arg;
+ if (poptArgType(opt) != POPT_ARG_INCLUDE_TABLE)
continue;
- sub_transdom = getTableTranslationDomain(opt->arg);
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions)
+ arg = poptHelpOptionsI18N;
+ sub_transdom = getTableTranslationDomain(arg);
if (sub_transdom == NULL)
sub_transdom = translation_domain;
+ /* If no popt aliases/execs, skip poptAliasOption processing. */
+ if (arg == poptAliasOptions && !(con->numAliases || con->numExecs))
+ continue;
if (opt->descrip)
- fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
+ POPT_fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
- singleTableHelp(con, fp, opt->arg, left, sub_transdom);
+ singleTableHelp(con, fp, arg, columns, sub_transdom);
}
}
@@ -542,22 +610,18 @@ static void singleTableHelp(poptContext con, FILE * fp,
* @param con context
* @param fp output file handle
*/
-static int showHelpIntro(poptContext con, FILE * fp)
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+static size_t showHelpIntro(poptContext con, FILE * fp)
{
- int len = 6;
- const char * fn;
+ const char *usage_str = POPT_("Usage:");
+ size_t len = strlen(usage_str);
+ POPT_fprintf(fp, "%s", usage_str);
- fprintf(fp, POPT_("Usage:"));
if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
-/*@-boundsread@*/
- /*@-nullderef -type@*/ /* LCL: wazzup? */
- fn = con->optionStack->argv[0];
- /*@=nullderef =type@*/
-/*@=boundsread@*/
+ struct optionStackEntry * os = con->optionStack;
+ const char * fn = (os->argv ? os->argv[0] : NULL);
if (fn == NULL) return len;
if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
+ /* XXX POPT_fprintf not needed for argv[0] display. */
fprintf(fp, " %s", fn);
len += strlen(fn) + 1;
}
@@ -565,126 +629,114 @@ static int showHelpIntro(poptContext con, FILE * fp)
return len;
}
-void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+void poptPrintHelp(poptContext con, FILE * fp, UNUSED(int flags))
{
- size_t leftColWidth;
+ columns_t columns = calloc((size_t)1, sizeof(*columns));
(void) showHelpIntro(con, fp);
if (con->otherHelp)
- fprintf(fp, " %s\n", con->otherHelp);
+ POPT_fprintf(fp, " %s\n", con->otherHelp);
else
- fprintf(fp, " %s\n", POPT_("[OPTION...]"));
+ POPT_fprintf(fp, " %s\n", POPT_("[OPTION...]"));
- leftColWidth = maxArgWidth(con->options, NULL);
- singleTableHelp(con, fp, con->options, leftColWidth, NULL);
+ if (columns) {
+ columns->cur = maxArgWidth(con->options, NULL);
+ columns->max = maxColumnWidth(fp);
+ singleTableHelp(con, fp, con->options, columns, NULL);
+ free(columns);
+ }
}
/**
* Display usage text for an option.
* @param fp output file handle
- * @param cursor current display position
+ * @param columns output display width control
* @param opt option(s)
* @param translation_domain translation domain
*/
-static size_t singleOptionUsage(FILE * fp, size_t cursor,
+static size_t singleOptionUsage(FILE * fp, columns_t columns,
const struct poptOption * opt,
- /*@null@*/ const char *translation_domain)
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+ const char *translation_domain)
{
- size_t len = 4;
- char shortStr[2] = { '\0', '\0' };
- const char * item = shortStr;
+ size_t len = sizeof(" []")-1;
const char * argDescrip = getArgDescrip(opt, translation_domain);
-
- if (opt->shortName != '\0' && opt->longName != NULL) {
- len += 2;
- if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+ /* Display shortName iff printable non-space. */
+ int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
+
+#define prtlong (opt->longName != NULL) /* XXX splint needs a clue */
+ if (!(prtshort || prtlong))
+ return columns->cur;
+
+ len = sizeof(" []")-1;
+ if (prtshort)
+ len += sizeof("-c")-1;
+ if (prtlong) {
+ if (prtshort) len += sizeof("|")-1;
+ len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
len += strlen(opt->longName);
- } else if (opt->shortName != '\0') {
- len++;
- shortStr[0] = opt->shortName;
- shortStr[1] = '\0';
- } else if (opt->longName) {
- len += strlen(opt->longName);
- if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
- item = opt->longName;
}
- if (len == 4) return cursor;
-
-#ifdef POPT_WCHAR_HACK
- /* XXX Calculate no. of display characters. */
if (argDescrip) {
- const char * scopy = argDescrip;
- mbstate_t t;
- size_t n;
-
-/*@-boundswrite@*/
- memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
-/*@=boundswrite@*/
- /* Determine number of characters. */
- n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
- len += sizeof("=")-1 + n;
+
+ /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
+ if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
+
+ /* Adjust for (possible) wide characters. */
+ len += stringDisplayWidth(argDescrip);
}
-#else
- if (argDescrip)
- len += sizeof("=")-1 + strlen(argDescrip);
-#endif
- if ((cursor + len) > 79) {
+ if ((columns->cur + len) > columns->max) {
fprintf(fp, "\n ");
- cursor = 7;
+ columns->cur = (size_t)7;
}
- if (opt->longName && opt->shortName) {
- fprintf(fp, " [-%c|-%s%s%s%s]",
- opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
- opt->longName,
- (argDescrip ? " " : ""),
- (argDescrip ? argDescrip : ""));
- } else {
- fprintf(fp, " [-%s%s%s%s]",
- ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
- item,
- (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
- (argDescrip ? argDescrip : ""));
+ fprintf(fp, " [");
+ if (prtshort)
+ fprintf(fp, "-%c", opt->shortName);
+ if (prtlong)
+ fprintf(fp, "%s%s%s",
+ (prtshort ? "|" : ""),
+ (F_ISSET(opt, ONEDASH) ? "-" : "--"),
+ opt->longName);
+#undef prtlong
+
+ if (argDescrip) {
+ /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
+ if (!strchr(" =(", argDescrip[0])) fputc(opt->longName == NULL ? ' ' : '=', fp);
+ fprintf(fp, "%s", argDescrip);
}
+ fprintf(fp, "]");
- return cursor + len + 1;
+ return columns->cur + len + 1;
}
/**
* Display popt alias and exec usage.
* @param fp output file handle
- * @param cursor current display position
+ * @param columns output display width control
* @param item alias/exec array
* @param nitems no. of ara/exec entries
* @param translation_domain translation domain
*/
-static size_t itemUsage(FILE * fp, size_t cursor,
- /*@null@*/ poptItem item, int nitems,
- /*@null@*/ UNUSED(const char * translation_domain))
- /*@globals fileSystem @*/
- /*@modifies *fp, fileSystem @*/
+static size_t itemUsage(FILE * fp, columns_t columns,
+ poptItem item, int nitems,
+ const char * translation_domain)
{
int i;
- /*@-branchstate@*/ /* FIX: W2DO? */
if (item != NULL)
for (i = 0; i < nitems; i++, item++) {
const struct poptOption * opt;
opt = &item->option;
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
translation_domain = (const char *)opt->arg;
- } else if ((opt->longName || opt->shortName) &&
- !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
- cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ } else
+ if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
+ columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
}
}
- /*@=branchstate@*/
- return cursor;
+ return columns->cur;
}
/**
@@ -700,53 +752,51 @@ typedef struct poptDone_s {
* Display usage text for a table of options.
* @param con context
* @param fp output file handle
- * @param cursor current display position
+ * @param columns output display width control
* @param opt option(s)
* @param translation_domain translation domain
* @param done tables already processed
* @return
*/
-static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
- /*@null@*/ const struct poptOption * opt,
- /*@null@*/ UNUSED(const char * translation_domain),
- /*@null@*/ poptDone done)
- /*@globals fileSystem @*/
- /*@modifies *fp, done, fileSystem @*/
+static size_t singleTableUsage(poptContext con, FILE * fp, columns_t columns,
+ const struct poptOption * opt,
+ const char * translation_domain,
+ poptDone done)
{
- /*@-branchstate@*/ /* FIX: W2DO? */
if (opt != NULL)
for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
- if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
translation_domain = (const char *)opt->arg;
- } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ } else
+ if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions)
+ arg = poptHelpOptionsI18N;
if (done) {
int i = 0;
+ if (done->opts != NULL)
for (i = 0; i < done->nopts; i++) {
-/*@-boundsread@*/
const void * that = done->opts[i];
-/*@=boundsread@*/
- if (that == NULL || that != opt->arg)
- /*@innercontinue@*/ continue;
- /*@innerbreak@*/ break;
+ if (that == NULL || that != arg)
+ continue;
+ break;
}
/* Skip if this table has already been processed. */
- if (opt->arg == NULL || i < done->nopts)
+ if (arg == NULL || i < done->nopts)
continue;
-/*@-boundswrite@*/
- if (done->nopts < done->maxopts)
- done->opts[done->nopts++] = (const void *) opt->arg;
-/*@=boundswrite@*/
+ if (done->opts != NULL && done->nopts < done->maxopts)
+ done->opts[done->nopts++] = (const void *) arg;
}
- cursor = singleTableUsage(con, fp, cursor, opt->arg,
+ columns->cur = singleTableUsage(con, fp, columns, opt->arg,
translation_domain, done);
- } else if ((opt->longName || opt->shortName) &&
- !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
- cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ } else
+ if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
+ columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
}
}
- /*@=branchstate@*/
- return cursor;
+ return columns->cur;
}
/**
@@ -757,66 +807,78 @@ static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
* @retval str concatenation of short options
* @return length of display string
*/
-static int showShortOptions(const struct poptOption * opt, FILE * fp,
- /*@null@*/ char * str)
- /*@globals fileSystem @*/
- /*@modifies *str, *fp, fileSystem @*/
- /*@requires maxRead(str) >= 0 @*/
+static size_t showShortOptions(const struct poptOption * opt, FILE * fp,
+ char * str)
{
- /* bufsize larger then the ascii set, lazy alloca on top level call. */
- char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
- int len = 0;
+ /* bufsize larger then the ascii set, lazy allocation on top level call. */
+ size_t nb = (size_t)300;
+ char * s = (str != NULL ? str : calloc((size_t)1, nb));
+ size_t len = (size_t)0;
if (s == NULL)
return 0;
-/*@-boundswrite@*/
if (opt != NULL)
for (; (opt->longName || opt->shortName || opt->arg); opt++) {
- if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
- s[strlen(s)] = opt->shortName;
- else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
- if (opt->arg) /* XXX program error */
- len = showShortOptions(opt->arg, fp, s);
+ if (!F_ISSET(opt, DOC_HIDDEN) && opt->shortName && !poptArgType(opt))
+ {
+ /* Display shortName iff unique printable non-space. */
+ if (!strchr(s, opt->shortName) && isprint((int)opt->shortName)
+ && opt->shortName != ' ')
+ s[strlen(s)] = opt->shortName;
+ } else if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions)
+ arg = poptHelpOptionsI18N;
+ if (arg) /* XXX program error */
+ len = showShortOptions(arg, fp, s);
+ }
}
-/*@=boundswrite@*/
/* On return to top level, print the short options, return print length. */
- if (s == str && *s != '\0') {
+ if (s != str && *s != '\0') {
fprintf(fp, " [-%s]", s);
len = strlen(s) + sizeof(" [-]")-1;
}
+ if (s != str)
+ free(s);
return len;
}
-void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+void poptPrintUsage(poptContext con, FILE * fp, UNUSED(int flags))
{
- poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
- size_t cursor;
+ columns_t columns = calloc((size_t)1, sizeof(*columns));
+ struct poptDone_s done_buf;
+ poptDone done = &done_buf;
+ memset(done, 0, sizeof(*done));
done->nopts = 0;
done->maxopts = 64;
- cursor = done->maxopts * sizeof(*done->opts);
-/*@-boundswrite@*/
- done->opts = memset(alloca(cursor), 0, cursor);
- /*@-keeptrans@*/
- done->opts[done->nopts++] = (const void *) con->options;
- /*@=keeptrans@*/
-/*@=boundswrite@*/
-
- cursor = showHelpIntro(con, fp);
- cursor += showShortOptions(con->options, fp, NULL);
- cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
- cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
- cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
+ if (columns) {
+ columns->cur = done->maxopts * sizeof(*done->opts);
+ columns->max = maxColumnWidth(fp);
+ done->opts = calloc((size_t)1, columns->cur);
+ if (done->opts != NULL)
+ done->opts[done->nopts++] = (const void *) con->options;
+
+ columns->cur = showHelpIntro(con, fp);
+ columns->cur += showShortOptions(con->options, fp, NULL);
+ columns->cur = singleTableUsage(con, fp, columns, con->options, NULL, done);
+ columns->cur = itemUsage(fp, columns, con->aliases, con->numAliases, NULL);
+ columns->cur = itemUsage(fp, columns, con->execs, con->numExecs, NULL);
if (con->otherHelp) {
- cursor += strlen(con->otherHelp) + 1;
- if (cursor > 79) fprintf(fp, "\n ");
+ columns->cur += strlen(con->otherHelp) + 1;
+ if (columns->cur > columns->max) fprintf(fp, "\n ");
fprintf(fp, " %s", con->otherHelp);
}
fprintf(fp, "\n");
+ if (done->opts != NULL)
+ free(done->opts);
+ free(columns);
+ }
}
void poptSetOtherOptionHelp(poptContext con, const char * text)
diff --git a/popt/poptint.c b/popt/poptint.c
new file mode 100644
index 000000000..b8dc90f42
--- /dev/null
+++ b/popt/poptint.c
@@ -0,0 +1,194 @@
+#include "system.h"
+#include
+#include
+#ifdef HAVE_LANGINFO_H
+#include
+#endif
+#include "poptint.h"
+
+/* Any pair of 32 bit hashes can be used. lookup3.c generates pairs, will do. */
+#define _JLU3_jlu32lpair 1
+#define jlu32lpair poptJlu32lpair
+#include "lookup3.c"
+
+const char *
+POPT_prev_char (const char *str)
+{
+ const char *p = str;
+
+ while (1) {
+ p--;
+ if (((unsigned)*p & 0xc0) != (unsigned)0x80)
+ return p;
+ }
+}
+
+const char *
+POPT_next_char (const char *str)
+{
+ const char *p = str;
+
+ while (*p != '\0') {
+ p++;
+ if (((unsigned)*p & 0xc0) != (unsigned)0x80)
+ break;
+ }
+ return p;
+}
+
+#if !defined(POPT_fprintf) /* XXX lose all the goop ... */
+
+#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
+/*
+ * Rebind a "UTF-8" codeset for popt's internal use.
+ */
+char *
+POPT_dgettext(const char * dom, const char * str)
+{
+ char * codeset = NULL;
+ char * retval = NULL;
+
+ if (!dom)
+ dom = textdomain(NULL);
+ codeset = bind_textdomain_codeset(dom, NULL);
+ bind_textdomain_codeset(dom, "UTF-8");
+ retval = dgettext(dom, str);
+ bind_textdomain_codeset(dom, codeset);
+
+ return retval;
+}
+#endif
+
+#ifdef HAVE_ICONV
+/**
+ * Return malloc'd string converted from UTF-8 to current locale.
+ * @param istr input string (UTF-8 encoding assumed)
+ * @return localized string
+ */
+static char *
+strdup_locale_from_utf8 (char * istr)
+{
+ char * codeset = NULL;
+ char * ostr = NULL;
+ iconv_t cd;
+
+ if (istr == NULL)
+ return NULL;
+
+#ifdef HAVE_LANGINFO_H
+ codeset = nl_langinfo ((nl_item)CODESET);
+#endif
+
+ if (codeset != NULL && strcmp(codeset, "UTF-8") != 0
+ && (cd = iconv_open(codeset, "UTF-8")) != (iconv_t)-1)
+ {
+ char * shift_pin = NULL;
+ size_t db = strlen(istr);
+ char * dstr = malloc((db + 1) * sizeof(*dstr));
+ char * dstr_tmp;
+ char * pin = istr;
+ char * pout = dstr;
+ size_t ib = db;
+ size_t ob = db;
+ size_t err;
+
+ if (dstr == NULL) {
+ (void) iconv_close(cd);
+ return NULL;
+ }
+ err = iconv(cd, NULL, NULL, NULL, NULL);
+ while (1) {
+ *pout = '\0';
+ err = iconv(cd, &pin, &ib, &pout, &ob);
+ if (err != (size_t)-1) {
+ if (shift_pin == NULL) {
+ shift_pin = pin;
+ pin = NULL;
+ ib = 0;
+ continue;
+ }
+ } else
+ switch (errno) {
+ case E2BIG:
+ { size_t used = (size_t)(pout - dstr);
+ db *= 2;
+ dstr_tmp = realloc(dstr, (db + 1) * sizeof(*dstr));
+ if (dstr_tmp == NULL) {
+ free(dstr);
+ (void) iconv_close(cd);
+ return NULL;
+ }
+ dstr = dstr_tmp;
+ pout = dstr + used;
+ ob = db - used;
+ continue;
+ } break;
+ case EINVAL:
+ case EILSEQ:
+ default:
+ break;
+ }
+ break;
+ }
+ (void) iconv_close(cd);
+ *pout = '\0';
+ ostr = xstrdup(dstr);
+ free(dstr);
+ } else
+ ostr = xstrdup(istr);
+
+ return ostr;
+}
+#endif
+
+int
+POPT_fprintf (FILE * stream, const char * format, ...)
+{
+ char * b = NULL, * ob = NULL;
+ int rc;
+ va_list ap;
+
+#if defined(HAVE_VASPRINTF)
+ va_start(ap, format);
+ if ((rc = vasprintf(&b, format, ap)) < 0)
+ b = NULL;
+ va_end(ap);
+#else
+ size_t nb = (size_t)1;
+
+ /* HACK: add +1 to the realloc no. of bytes "just in case". */
+ /* XXX Likely unneeded, the issues wrto vsnprintf(3) return b0rkage have
+ * to do with whether the final '\0' is counted (or not). The code
+ * below already adds +1 for the (possibly already counted) trailing NUL.
+ */
+ while ((b = realloc(b, nb+1)) != NULL) {
+ va_start(ap, format);
+ rc = vsnprintf(b, nb, format, ap);
+ va_end(ap);
+ if (rc > -1) { /* glibc 2.1 */
+ if ((size_t)rc < nb)
+ break;
+ nb = (size_t)(rc + 1); /* precise buffer length known */
+ } else /* glibc 2.0 */
+ nb += (nb < (size_t)100 ? (size_t)100 : nb);
+ ob = b;
+ }
+#endif
+
+ rc = 0;
+ if (b != NULL) {
+#ifdef HAVE_ICONV
+ ob = strdup_locale_from_utf8(b);
+ if (ob != NULL) {
+ rc = fprintf(stream, "%s", ob);
+ free(ob);
+ } else
+#endif
+ rc = fprintf(stream, "%s", b);
+ free (b);
+ }
+
+ return rc;
+}
+
+#endif /* !defined(POPT_fprintf) */
diff --git a/popt/poptint.h b/popt/poptint.h
index bec7c9769..001c5c35d 100644
--- a/popt/poptint.h
+++ b/popt/poptint.h
@@ -1,5 +1,5 @@
/** \ingroup popt
- * \file popt/poptint.h
+ * @file
*/
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -9,108 +9,145 @@
#ifndef H_POPTINT
#define H_POPTINT
+#include
+
/**
* Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
* @param p memory to free
* @retval NULL always
*/
-/*@unused@*/ static inline /*@null@*/ void *
-_free(/*@only@*/ /*@null@*/ const void * p)
- /*@modifies p @*/
+static inline void *
+_free(const void * p)
{
if (p != NULL) free((void *)p);
return NULL;
}
-static inline int
-isSpace(const char *ptr)
-{
- return isspace(*(unsigned char *)ptr);
-}
-
/* Bit mask macros. */
-/*@-exporttype -redef @*/
typedef unsigned int __pbm_bits;
-/*@=exporttype =redef @*/
#define __PBM_NBITS (8 * sizeof (__pbm_bits))
#define __PBM_IX(d) ((d) / __PBM_NBITS)
#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
-/*@-exporttype -redef @*/
typedef struct {
__pbm_bits bits[1];
} pbm_set;
-/*@=exporttype =redef @*/
#define __PBM_BITS(set) ((set)->bits)
-#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(pbm_set))
#define PBM_FREE(s) _free(s);
#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+extern void poptJlu32lpair(const void *key, size_t size,
+ uint32_t *pc, uint32_t *pb);
+
+/** \ingroup popt
+ * Typedef's for string and array of strings.
+ */
+typedef const char * poptString;
+typedef poptString * poptArgv;
+
+/** \ingroup popt
+ * A union to simplify opt->arg access without casting.
+ */
+typedef union poptArg_u {
+ void * ptr;
+ int * intp;
+ short * shortp;
+ long * longp;
+ long long * longlongp;
+ float * floatp;
+ double * doublep;
+ const char ** argv;
+ poptCallbackType cb;
+ poptOption opt;
+} poptArg;
+
+extern unsigned int _poptArgMask;
+extern unsigned int _poptGroupMask;
+
+#define poptArgType(_opt) ((_opt)->argInfo & _poptArgMask)
+#define poptGroup(_opt) ((_opt)->argInfo & _poptGroupMask)
+
+#define F_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_ARGFLAG_##_FLAG)
+#define LF_ISSET(_FLAG) (argInfo & POPT_ARGFLAG_##_FLAG)
+#define CBF_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_CBFLAG_##_FLAG)
+
+/* XXX sick hack to preserve pretense of a popt-1.x ABI. */
+#define poptSubstituteHelpI18N(opt) \
+ { if ((opt) == poptHelpOptions) (opt) = poptHelpOptionsI18N; }
+
struct optionStackEntry {
int argc;
-/*@only@*/ /*@null@*/
- const char ** argv;
-/*@only@*/ /*@null@*/
+ poptArgv argv;
pbm_set * argb;
int next;
-/*@only@*/ /*@null@*/
- const char * nextArg;
-/*@observer@*/ /*@null@*/
+ char * nextArg;
const char * nextCharArg;
-/*@dependent@*/ /*@null@*/
poptItem currAlias;
int stuffed;
};
struct poptContext_s {
struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
-/*@dependent@*/
struct optionStackEntry * os;
-/*@owned@*/ /*@null@*/
- const char ** leftovers;
+ poptArgv leftovers;
int numLeftovers;
+ int allocLeftovers;
int nextLeftover;
-/*@keep@*/
const struct poptOption * options;
int restLeftover;
-/*@only@*/ /*@null@*/
const char * appName;
-/*@only@*/ /*@null@*/
poptItem aliases;
int numAliases;
- int flags;
-/*@owned@*/ /*@null@*/
+ unsigned int flags;
poptItem execs;
int numExecs;
-/*@only@*/ /*@null@*/
- const char ** finalArgv;
+ char * execFail;
+ poptArgv finalArgv;
int finalArgvCount;
int finalArgvAlloced;
-/*@dependent@*/ /*@null@*/
+ int (*maincall) (int argc, const char **argv);
poptItem doExec;
-/*@only@*/
const char * execPath;
int execAbsolute;
-/*@only@*/ /*@relnull@*/
const char * otherHelp;
-/*@null@*/
pbm_set * arg_strip;
};
-#ifdef HAVE_LIBINTL_H
+#if defined(POPT_fprintf)
+#define POPT_dgettext dgettext
+#else
+#ifdef HAVE_ICONV
+#include
+#endif
+
+#if defined(HAVE_DCGETTEXT)
+char *POPT_dgettext(const char * dom, const char * str);
+#endif
+
+FORMAT(printf, 2, 3)
+int POPT_fprintf (FILE* stream, const char *format, ...);
+#endif /* !defined(POPT_fprintf) */
+
+const char *POPT_prev_char (const char *str);
+const char *POPT_next_char (const char *str);
+
+#endif
+
+#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H)
#include
#endif
-#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
#define _(foo) gettext(foo)
#else
#define _(foo) foo
#endif
-#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
-#define D_(dom, str) dgettext(dom, str)
+#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
+#define D_(dom, str) POPT_dgettext(dom, str)
#define POPT_(foo) D_("popt", foo)
#else
#define D_(dom, str) str
@@ -119,4 +156,3 @@ struct poptContext_s {
#define N_(foo) foo
-#endif
diff --git a/popt/poptparse.c b/popt/poptparse.c
index e003a04a9..5afc6c551 100644
--- a/popt/poptparse.c
+++ b/popt/poptparse.c
@@ -1,5 +1,5 @@
/** \ingroup popt
- * \file popt/poptparse.c
+ * @file
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -8,11 +8,8 @@
#include "system.h"
-#include "poptint.h"
-
#define POPT_ARGV_ARRAY_GROW_DELTA 5
-/*@-boundswrite@*/
int poptDupArgv(int argc, const char **argv,
int * argcPtr, const char *** argvPtr)
{
@@ -34,13 +31,13 @@ int poptDupArgv(int argc, const char **argv,
return POPT_ERROR_MALLOC;
argv2 = (void *) dst;
dst += (argc + 1) * sizeof(*argv);
+ *dst = '\0';
- /*@-branchstate@*/
for (i = 0; i < argc; i++) {
argv2[i] = dst;
- dst += strlcpy(dst, argv[i], nb) + 1;
+ dst = stpcpy(dst, argv[i]);
+ dst++; /* trailing NUL */
}
- /*@=branchstate@*/
argv2[argc] = NULL;
if (argvPtr) {
@@ -53,21 +50,25 @@ int poptDupArgv(int argc, const char **argv,
*argcPtr = argc;
return 0;
}
-/*@=boundswrite@*/
-/*@-bounds@*/
int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
{
const char * src;
char quote = '\0';
int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ const char ** argv_tmp;
int argc = 0;
- int buflen = strlen(s) + 1;
- char * buf = memset(alloca(buflen), 0, buflen);
+ size_t buflen = strlen(s) + 1;
+ char * buf, * bufOrig = NULL;
int rc = POPT_ERROR_MALLOC;
if (argv == NULL) return rc;
+ buf = bufOrig = calloc((size_t)1, buflen);
+ if (buf == NULL) {
+ free(argv);
+ return rc;
+ }
argv[argc] = buf;
for (src = s; *src != '\0'; src++) {
@@ -83,13 +84,14 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
if (*src != quote) *buf++ = '\\';
}
*buf++ = *src;
- } else if (isSpace(src)) {
+ } else if (_isspaceptr(src)) {
if (*argv[argc] != '\0') {
buf++, argc++;
if (argc == argvAlloced) {
argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
- argv = realloc(argv, sizeof(*argv) * argvAlloced);
- if (argv == NULL) goto exit;
+ argv_tmp = realloc(argv, sizeof(*argv) * argvAlloced);
+ if (argv_tmp == NULL) goto exit;
+ argv = argv_tmp;
}
argv[argc] = buf;
}
@@ -97,17 +99,17 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
case '"':
case '\'':
quote = *src;
- /*@switchbreak@*/ break;
+ break;
case '\\':
src++;
if (!*src) {
rc = POPT_ERROR_BADQUOTE;
goto exit;
}
- /*@fallthrough@*/
+ /* fallthrough */
default:
*buf++ = *src;
- /*@switchbreak@*/ break;
+ break;
}
}
@@ -118,29 +120,30 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
exit:
+ if (bufOrig) free(bufOrig);
if (argv) free(argv);
return rc;
}
-/*@=bounds@*/
/* still in the dev stage.
- * return values, perhaps 1== file erro
+ * return values, perhaps 1== file error
* 2== line to long
* 3== umm.... more?
*/
-int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
+int poptConfigFileToString(FILE *fp, char ** argstrp,
+ UNUSED(int flags))
{
char line[999];
char * argstr;
+ char * argstr_tmp;
char * p;
char * q;
char * x;
- int t;
- int argvlen = 0;
+ size_t t;
+ size_t argvlen = 0;
size_t maxlinelen = sizeof(line);
size_t linelen;
- int maxargvlen = 480;
- int linenum = 0;
+ size_t maxargvlen = (size_t)480;
*argstrp = NULL;
@@ -155,11 +158,10 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
if (argstr == NULL) return POPT_ERROR_MALLOC;
while (fgets(line, (int)maxlinelen, fp) != NULL) {
- linenum++;
p = line;
/* loop until first non-space char or EOL */
- while( *p != '\0' && isSpace(p) )
+ while( *p != '\0' && _isspaceptr(p) )
p++;
linelen = strlen(p);
@@ -173,25 +175,29 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
q = p;
- while (*q != '\0' && (!isSpace(q)) && *q != '=')
+ while (*q != '\0' && (!_isspaceptr(q)) && *q != '=')
q++;
- if (isSpace(q)) {
+ if (_isspaceptr(q)) {
/* a space after the name, find next non space */
*q++='\0';
- while( *q != '\0' && isSpace(q) ) q++;
+ while( *q != '\0' && _isspaceptr(q) ) q++;
}
if (*q == '\0') {
/* single command line option (ie, no name=val, just name) */
q[-1] = '\0'; /* kill off newline from fgets() call */
- argvlen += (t = q - p) + (sizeof(" --")-1);
+ argvlen += (t = (size_t)(q - p)) + (sizeof(" --")-1);
if (argvlen >= maxargvlen) {
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
- argstr = realloc(argstr, maxargvlen);
- if (argstr == NULL) return POPT_ERROR_MALLOC;
+ argstr_tmp = realloc(argstr, maxargvlen);
+ if (argstr_tmp == NULL) {
+ free(argstr);
+ return POPT_ERROR_MALLOC;
+ }
+ argstr = argstr_tmp;
}
- strlcat(argstr, " --", maxargvlen);
- strlcat(argstr, p, maxargvlen);
+ strcat(argstr, " --");
+ strcat(argstr, p);
continue;
}
if (*q != '=')
@@ -201,29 +207,33 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
*q++ = '\0';
/* find next non-space letter of value */
- while (*q != '\0' && isSpace(q))
+ while (*q != '\0' && _isspaceptr(q))
q++;
if (*q == '\0')
continue; /* XXX silently ignore missing value */
/* now, loop and strip all ending whitespace */
x = p + linelen;
- while (isSpace(--x))
- *x = 0; /* null out last char if space (including fgets() NL) */
+ while (_isspaceptr(--x))
+ *x = '\0'; /* null out last char if space (including fgets() NL) */
/* rest of line accept */
- t = x - p;
+ t = (size_t)(x - p);
argvlen += t + (sizeof("' --='")-1);
if (argvlen >= maxargvlen) {
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
- argstr = realloc(argstr, maxargvlen);
- if (argstr == NULL) return POPT_ERROR_MALLOC;
+ argstr_tmp = realloc(argstr, maxargvlen);
+ if (argstr_tmp == NULL) {
+ free(argstr);
+ return POPT_ERROR_MALLOC;
+ }
+ argstr = argstr_tmp;
}
- strlcat(argstr, " --", maxargvlen);
- strlcat(argstr, p, maxargvlen);
- strlcat(argstr, "=\"", maxargvlen);
- strlcat(argstr, q, maxargvlen);
- strlcat(argstr, "\"", maxargvlen);
+ strcat(argstr, " --");
+ strcat(argstr, p);
+ strcat(argstr, "=\"");
+ strcat(argstr, q);
+ strcat(argstr, "\"");
}
*argstrp = argstr;
diff --git a/popt/system.h b/popt/system.h
index 25c22daee..f731d206d 100644
--- a/popt/system.h
+++ b/popt/system.h
@@ -1,134 +1,70 @@
+/**
+ * @file
+ */
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#if defined (__GLIBC__) && defined(__LCLINT__)
-/*@-declundef@*/
-/*@unchecked@*/
-extern __const __int32_t *__ctype_tolower;
-/*@unchecked@*/
-extern __const __int32_t *__ctype_toupper;
-/*@=declundef@*/
-#endif
-
-#ifdef __TANDEM
-# include
-#endif
-
#include
-#include
-#include
-#include
+/* XXX isspace(3) has i18n encoding signedness issues on Solaris. */
+#define _isspaceptr(_chp) isspace((int)(*(unsigned const char *)(_chp)))
-#if HAVE_MCHECK_H
+#ifdef HAVE_MCHECK_H
#include
#endif
-#include
-#ifdef HAVE_SYS_TYPES_H
-# include
-#endif
-#ifdef STDC_HEADERS
-# include
-# include
-#else
-# ifdef HAVE_STDLIB_H
-# include
-# endif
-#endif
-#ifdef HAVE_STRING_H
-# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
-# include
-# endif
-# include
-#endif
-#ifdef HAVE_STRINGS_H
-# include
-#endif
-#ifdef HAVE_UNISTD_H
-# include
-#endif
+#include
+#include
+#include
-#ifndef __GNUC__
-#define __attribute__(x)
-#endif
+void * xmalloc (size_t size);
-#ifdef __NeXT
-/* access macros are not declared in non posix mode in unistd.h -
- don't try to use posix on NeXTstep 3.3 ! */
-#include
-#endif
+void * xcalloc (size_t nmemb, size_t size);
-#if defined(__LCLINT__)
-/*@-declundef -incondefs @*/ /* LCL: missing annotation */
-/*@only@*/ /*@out@*/
-void * alloca (size_t __size)
- /*@ensures MaxSet(result) == (__size - 1) @*/
- /*@*/;
-/*@=declundef =incondefs @*/
-#endif
+void * xrealloc (void * ptr, size_t size);
-/* AIX requires this to be the first thing in the file. */
-#ifndef __GNUC__
-# if HAVE_ALLOCA_H
-# include
-# else
-# ifdef _AIX
-#pragma alloca
-# else
-# ifdef HAVE_ALLOCA
-# ifndef alloca /* predefined by HP cc +Olibcalls */
-char *alloca(size_t size);
-# endif
-# else
-# ifdef alloca
-# undef alloca
-# endif
-# define alloca(sz) malloc(sz) /* Kludge this for now */
-# endif
-# endif
-# endif
-#elif !defined(alloca)
-#define alloca __builtin_alloca
-#endif
+char * xstrdup (const char *str);
-#ifndef HAVE_STRLCPY
-size_t strlcpy(char *d, const char *s, size_t bufsize);
-#endif
+#if !defined(HAVE_STPCPY)
+/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
+static inline char * stpcpy (char *dest, const char * src) {
+ register char *d = dest;
+ register const char *s = src;
-#ifndef HAVE_STRLCAT
-size_t strlcat(char *d, const char *s, size_t bufsize);
+ do
+ *d++ = *s;
+ while (*s++ != '\0');
+ return d - 1;
+}
#endif
-#if HAVE_MCHECK_H && defined(__GNUC__)
-static inline char *
-xstrdup(const char *s)
-{
- size_t memsize = strlen(s) + 1;
- char *ptr = malloc(memsize);
- if (!ptr) {
- fprintf(stderr, "virtual memory exhausted.\n");
- exit(EXIT_FAILURE);
- }
- strlcpy(ptr, s, memsize);
- return ptr;
-}
+/* Memory allocation via macro defs to get meaningful locations from mtrace() */
+#if defined(HAVE_MCHECK_H) && defined(__GNUC__)
+#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xmalloc(_size) (malloc(_size) ? : vmefail())
+#define xcalloc(_nmemb, _size) (calloc((_nmemb), (_size)) ? : vmefail())
+#define xrealloc(_ptr, _size) (realloc((_ptr), (_size)) ? : vmefail())
+#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
#else
+#define xmalloc(_size) malloc(_size)
+#define xcalloc(_nmemb, _size) calloc((_nmemb), (_size))
+#define xrealloc(_ptr, _size) realloc((_ptr), (_size))
#define xstrdup(_str) strdup(_str)
-#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
+#endif /* defined(HAVE_MCHECK_H) && defined(__GNUC__) */
-#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
+#if defined(HAVE_SECURE_GETENV)
+#define getenv(_s) secure_getenv(_s)
+#elif defined(HAVE___SECURE_GETENV)
#define getenv(_s) __secure_getenv(_s)
#endif
-#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
-#define snprintf rsync_snprintf
-int snprintf(char *str,size_t count,const char *fmt,...);
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
#endif
-
#define UNUSED(x) x __attribute__((__unused__))
-
-#define PACKAGE "rsync"
+#define FORMAT(a, b, c) __attribute__((__format__ (a, b, c)))
+#define NORETURN __attribute__((__noreturn__))
#include "popt.h"
diff --git a/prepare-source b/prepare-source
index f5b7b46ca..a4e78e61e 100755
--- a/prepare-source
+++ b/prepare-source
@@ -6,7 +6,7 @@
#
# build build the config files [the default w/no arg]
# fetch fetch the latest dev autoconfig files
-# fetchgen fetch all the latest dev generated files (including man pages)
+# fetchgen fetch all the latest dev generated files (including manpages)
# fetchSRC fetch the latest dev source files [NON-GENERATED FILES]
#
# The script stops after the first successful action.
@@ -32,7 +32,7 @@ if test "$dir" != '.'; then
fi
done
for fn in configure.sh config.h.in aclocal.m4; do
- test ! -f $fn -a -f "$dir/$fn" && cp -p "$dir/$fn" $fn
+ test ! -f $fn && test -f "$dir/$fn" && cp -p "$dir/$fn" $fn
done
fi
diff --git a/progress.c b/progress.c
index 8da528622..87207fbfa 100644
--- a/progress.c
+++ b/progress.c
@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -115,13 +115,13 @@ static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_l
units = "kB/s";
}
- if (remain < 0)
+ if (remain < 0 || remain > 9999.0 * 3600.0)
strlcpy(rembuf, " ??:??:??", sizeof rembuf);
else {
- snprintf(rembuf, sizeof rembuf, "%4d:%02d:%02d",
- (int) (remain / 3600.0),
- (int) (remain / 60.0) % 60,
- (int) remain % 60);
+ snprintf(rembuf, sizeof rembuf, "%4u:%02u:%02u",
+ (unsigned int) (remain / 3600.0),
+ (unsigned int) (remain / 60.0) % 60,
+ (unsigned int) remain % 60);
}
output_needs_newline = 0;
diff --git a/receiver.c b/receiver.c
index 2cd843517..edfbb2106 100644
--- a/receiver.c
+++ b/receiver.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,7 +56,6 @@ extern int inplace;
extern int inplace_partial;
extern int allowed_lull;
extern int delay_updates;
-extern int xfersum_type;
extern BOOL want_progress_now;
extern mode_t orig_umask;
extern struct stats stats;
@@ -67,6 +66,10 @@ extern char sender_file_sum[MAX_DIGEST_LEN];
extern struct file_list *cur_flist, *first_flist, *dir_flist;
extern filter_rule_list daemon_filter_list;
extern OFF_T preallocated_len;
+extern int fuzzy_basis;
+
+extern struct name_num_item *xfer_sum_nni;
+extern int xfer_sum_len;
static struct bitbag *delayed_bits = NULL;
static int phase = 0, redoing = 0;
@@ -240,7 +243,6 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
static char file_sum1[MAX_DIGEST_LEN];
struct map_struct *mapbuf;
struct sum_struct sum;
- int sum_len;
int32 len;
OFF_T total_size = F_LENGTH(file);
OFF_T offset = 0;
@@ -280,7 +282,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
} else
mapbuf = NULL;
- sum_init(xfersum_type, checksum_seed);
+ sum_init(xfer_sum_nni, checksum_seed);
if (append_mode > 0) {
OFF_T j;
@@ -371,7 +373,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (fd != -1 && offset > 0) {
if (sparse_files > 0) {
- if (sparse_end(fd, offset) != 0)
+ if (sparse_end(fd, offset, updating_basis_or_equiv) != 0)
goto report_write_error;
} else if (flush_write_file(fd) < 0) {
report_write_error:
@@ -393,7 +395,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (INFO_GTE(PROGRESS, 1))
end_progress(total_size);
- sum_len = sum_end(file_sum1);
+ sum_end(file_sum1);
if (do_fsync && fd != -1 && fsync(fd) != 0) {
rsyserr(FERROR, errno, "fsync failed on %s", full_fname(fname));
@@ -403,10 +405,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (mapbuf)
unmap_file(mapbuf);
- read_buf(f_in, sender_file_sum, sum_len);
+ read_buf(f_in, sender_file_sum, xfer_sum_len);
if (DEBUG_GTE(DELTASUM, 2))
rprintf(FINFO,"got file_sum\n");
- if (fd != -1 && memcmp(file_sum1, sender_file_sum, sum_len) != 0)
+ if (fd != -1 && memcmp(file_sum1, sender_file_sum, xfer_sum_len) != 0)
return 0;
return 1;
}
@@ -439,9 +441,8 @@ static void handle_delayed_updates(char *local_name)
"rename failed for %s (from %s)",
full_fname(fname), partialptr);
} else {
- if (remove_source_files
- || (preserve_hard_links && F_IS_HLINKED(file)))
- send_msg_int(MSG_SUCCESS, ndx);
+ if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
handle_partial_dir(partialptr, PDIR_DELETE);
}
}
@@ -551,6 +552,8 @@ int recv_files(int f_in, int f_out, char *local_name)
progress_init();
while (1) {
+ const char *basedir = NULL;
+
cleanup_disable();
/* This call also sets cur_flist. */
@@ -593,10 +596,13 @@ int recv_files(int f_in, int f_out, char *local_name)
if (DEBUG_GTE(RECV, 1))
rprintf(FINFO, "recv_files(%s)\n", fname);
- if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')
- && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
- rprintf(FERROR, "attempt to hack rsync failed.\n");
- exit_cleanup(RERR_PROTOCOL);
+ if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) {
+ int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE;
+ if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) {
+ rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n",
+ fname);
+ exit_cleanup(RERR_PROTOCOL);
+ }
}
#ifdef SUPPORT_XATTRS
@@ -695,7 +701,7 @@ int recv_files(int f_in, int f_out, char *local_name)
if (!am_server)
discard_receive_data(f_in, file);
if (inc_recurse)
- send_msg_int(MSG_SUCCESS, ndx);
+ send_msg_success(fname, ndx);
continue;
}
@@ -713,28 +719,34 @@ int recv_files(int f_in, int f_out, char *local_name)
fnamecmp = get_backup_name(fname);
break;
case FNAMECMP_FUZZY:
+ if (fuzzy_basis == 0) {
+ rprintf(FERROR_XFER, "rsync: refusing malicious fuzzy operation for %s\n", xname);
+ exit_cleanup(RERR_PROTOCOL);
+ }
if (file->dirname) {
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
- fnamecmp = fnamecmpbuf;
- } else
- fnamecmp = xname;
+ basedir = file->dirname;
+ }
+ fnamecmp = xname;
break;
default:
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
fnamecmp_type -= FNAMECMP_FUZZY + 1;
if (file->dirname) {
- stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
- basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
- } else
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
+ basedir = fnamecmpbuf;
+ } else {
+ basedir = basis_dir[fnamecmp_type];
+ }
+ fnamecmp = xname;
} else if (fnamecmp_type >= basis_dir_cnt) {
rprintf(FERROR,
"invalid basis_dir index: %d.\n",
fnamecmp_type);
exit_cleanup(RERR_PROTOCOL);
- } else
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
- fnamecmp = fnamecmpbuf;
+ } else {
+ basedir = basis_dir[fnamecmp_type];
+ fnamecmp = fname;
+ }
break;
}
if (!fnamecmp || (daemon_filter_list.head
@@ -757,25 +769,31 @@ int recv_files(int f_in, int f_out, char *local_name)
}
/* open the file */
- fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
if (fd1 == -1 && protocol_version < 29) {
if (fnamecmp != fname) {
fnamecmp = fname;
fnamecmp_type = FNAMECMP_FNAME;
- fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
}
if (fd1 == -1 && basis_dir[0]) {
/* pre-29 allowed only one alternate basis */
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
- basis_dir[0], fname);
- fnamecmp = fnamecmpbuf;
+ basedir = basis_dir[0];
+ fnamecmp = fname;
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
- fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
}
}
+ if (basedir) {
+ // for the following code we need the full
+ // path name as a single string
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basedir, fnamecmp);
+ fnamecmp = fnamecmpbuf;
+ }
+
one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
updating_basis_or_equiv = one_inplace
|| (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
@@ -808,14 +826,16 @@ int recv_files(int f_in, int f_out, char *local_name)
continue;
}
- if (fd1 != -1 && !(S_ISREG(st.st_mode) || (write_devices && IS_DEVICE(st.st_mode)))) {
+ if (write_devices && IS_DEVICE(st.st_mode)) {
+ if (fd1 != -1 && st.st_size == 0)
+ st.st_size = get_device_size(fd1, fname);
+ /* Mark the file entry as a device so that we don't try to truncate it later on. */
+ file->mode = S_IFBLK | (file->mode & ACCESSPERMS);
+ } else if (fd1 != -1 && !(S_ISREG(st.st_mode))) {
close(fd1);
fd1 = -1;
}
- if (fd1 != -1 && IS_DEVICE(st.st_mode) && st.st_size == 0)
- st.st_size = get_device_size(fd1, fname);
-
/* If we're not preserving permissions, change the file-list's
* mode based on the local permissions and some heuristics. */
if (!preserve_perms) {
@@ -921,9 +941,8 @@ int recv_files(int f_in, int f_out, char *local_name)
case 2:
break;
case 1:
- if (remove_source_files || inc_recurse
- || (preserve_hard_links && F_IS_HLINKED(file)))
- send_msg_int(MSG_SUCCESS, ndx);
+ if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
break;
case 0: {
enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
diff --git a/rsync-ssl.1.md b/rsync-ssl.1.md
index 32ab31e2c..a6f1e3d39 100644
--- a/rsync-ssl.1.md
+++ b/rsync-ssl.1.md
@@ -1,14 +1,17 @@
-# NAME
+## NAME
rsync-ssl - a helper script for connecting to an ssl rsync daemon
-# SYNOPSIS
+## SYNOPSIS
```
rsync-ssl [--type=SSL_TYPE] RSYNC_ARGS
```
-# DESCRIPTION
+The online version of this manpage (that includes cross-linking of topics)
+is available at .
+
+## DESCRIPTION
The rsync-ssl script helps you to run an rsync copy to/from an rsync daemon
that requires ssl connections.
@@ -20,7 +23,7 @@ environment. You can specify an overriding port via `--port` or by including
it in the normal spot in the URL format, though both of those require your
rsync version to be at least 3.2.0.
-# OPTIONS
+## OPTIONS
If the **first** arg is a `--type=SSL_TYPE` option, the script will only use
that particular program to open an ssl connection instead of trying to find an
@@ -32,35 +35,56 @@ required for this particular option.
All the other options are passed through to the rsync command, so consult the
**rsync**(1) manpage for more information on how it works.
-# ENVIRONMENT VARIABLES
+## ENVIRONMENT VARIABLES
The ssl helper scripts are affected by the following environment variables:
-0. `RSYNC_SSL_TYPE` Specifies the program type that should be used to open the
- ssl connection. It must be one of `openssl` or `stunnel`. The
- `--type=SSL_TYPE` option overrides this, when specified.
-0. `RSYNC_SSL_PORT` If specified, the value is the port number that is used as
- the default when the user does not specify a port in their rsync command.
- When not specified, the default port number is 874. (Note that older rsync
- versions (prior to 3.2.0) did not communicate an overriding port number
- value to the helper script.)
-0. `RSYNC_SSL_CERT` If specified, the value is a filename that contains a
+0. `RSYNC_SSL_TYPE`
+
+ Specifies the program type that should be used to open the ssl connection.
+ It must be one of `openssl` or `stunnel`. The `--type=SSL_TYPE` option
+ overrides this, when specified.
+
+0. `RSYNC_SSL_PORT`
+
+ If specified, the value is the port number that is used as the default when
+ the user does not specify a port in their rsync command. When not
+ specified, the default port number is 874. (Note that older rsync versions
+ (prior to 3.2.0) did not communicate an overriding port number value to the
+ helper script.)
+
+0. `RSYNC_SSL_CERT`
+
+ If specified, the value is a filename that contains a certificate to use
+ for the connection.
+
+0. `RSYNC_SSL_KEY`
+
+ If specified, the value is a filename that contains a key for the provided
certificate to use for the connection.
-0. `RSYNC_SSL_KEY` If specified, the value is a filename that contains a
- key for the provided certificate to use for the connection.
-0. `RSYNC_SSL_CA_CERT` If specified, the value is a filename that contains a
- certificate authority certificate that is used to validate the connection.
-0. `RSYNC_SSL_OPENSSL` Specifies the openssl executable to run when the
- connection type is set to openssl. If unspecified, the $PATH is searched
- for "openssl".
-0. `RSYNC_SSL_GNUTLS` Specifies the gnutls-cli executable to run when the
- connection type is set to gnutls. If unspecified, the $PATH is searched
- for "gnutls-cli".
-0. `RSYNC_SSL_STUNNEL` Specifies the stunnel executable to run when the
- connection type is set to stunnel. If unspecified, the $PATH is searched
- first for "stunnel4" and then for "stunnel".
-
-# EXAMPLES
+
+0. `RSYNC_SSL_CA_CERT`
+
+ If specified, the value is a filename that contains a certificate authority
+ certificate that is used to validate the connection.
+
+0. `RSYNC_SSL_OPENSSL`
+
+ Specifies the openssl executable to run when the connection type is set to
+ openssl. If unspecified, the $PATH is searched for "openssl".
+
+0. `RSYNC_SSL_GNUTLS`
+
+ Specifies the gnutls-cli executable to run when the connection type is set
+ to gnutls. If unspecified, the $PATH is searched for "gnutls-cli".
+
+0. `RSYNC_SSL_STUNNEL`
+
+ Specifies the stunnel executable to run when the connection type is set to
+ stunnel. If unspecified, the $PATH is searched first for "stunnel4" and
+ then for "stunnel".
+
+## EXAMPLES
> rsync-ssl -aiv example.com::mod/ dest
@@ -70,11 +94,16 @@ The ssl helper scripts are affected by the following environment variables:
> rsync-ssl -aiv rsync://example.com:9874/mod/ dest
-# SEE ALSO
+## THE SERVER SIDE
+
+For help setting up an SSL/TLS supporting rsync, see the [instructions in
+rsyncd.conf](rsyncd.conf.5#SSL_TLS_Daemon_Setup).
+
+## SEE ALSO
-**rsync**(1), **rsyncd.conf**(5)
+[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
-# CAVEATS
+## CAVEATS
Note that using an stunnel connection requires at least version 4 of stunnel,
which should be the case on modern systems. Also, it does not verify a
@@ -87,23 +116,23 @@ release the gnutls-cli command was dropping output, making it unusable. If
that bug has been fixed in your version, feel free to put gnutls into an
exported RSYNC_SSL_TYPE environment variable to make its use the default.
-# BUGS
+## BUGS
Please report bugs! See the web site at .
-# VERSION
+## VERSION
-This man page is current for version @VERSION@ of rsync.
+This manpage is current for version @VERSION@ of rsync.
-# CREDITS
+## CREDITS
-rsync is distributed under the GNU General Public License. See the file
-COPYING for details.
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
A web site is available at . The site includes an
FAQ-O-Matic which may cover questions unanswered by this manual page.
-# AUTHOR
+## AUTHOR
This manpage was written by Wayne Davison.
diff --git a/rsync.1.md b/rsync.1.md
index d154a98ca..7e40e3617 100644
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -1,8 +1,8 @@
-# NAME
+## NAME
rsync - a fast, versatile, remote (and local) file-copying tool
-# SYNOPSIS
+## SYNOPSIS
```
Local:
@@ -26,7 +26,10 @@ Access via rsync daemon:
Usages with just one SRC arg and no DEST arg will list the source files instead
of copying.
-# DESCRIPTION
+The online version of this manpage (that includes cross-linking of topics)
+is available at .
+
+## DESCRIPTION
Rsync is a fast and extraordinarily versatile file copying tool. It can copy
locally, to/from another host over any remote shell, or to/from a remote rsync
@@ -54,7 +57,7 @@ Some of the additional features of rsync are:
- pipelining of file transfers to minimize latency costs
- support for anonymous or authenticated rsync daemons (ideal for mirroring)
-# GENERAL
+## GENERAL
Rsync copies files either to or from a remote host, or locally on the current
host (it does not support copying files between two remote hosts).
@@ -65,21 +68,21 @@ rsync daemon directly via TCP. The remote-shell transport is used whenever the
source or destination path contains a single colon (:) separator after a host
specification. Contacting an rsync daemon directly happens when the source or
destination path contains a double colon (::) separator after a host
-specification, OR when an rsync:// URL is specified (see also the "USING
-RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION" section for an exception
-to this latter rule).
+specification, OR when an rsync:// URL is specified (see also the [USING
+RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION](#) section for an
+exception to this latter rule).
As a special case, if a single source arg is specified without a destination,
the files are listed in an output format similar to "`ls -l`".
As expected, if neither the source or destination path specify a remote host,
-the copy occurs locally (see also the `--list-only` option).
+the copy occurs locally (see also the [`--list-only`](#opt) option).
Rsync refers to the local side as the client and the remote side as the server.
Don't confuse server with an rsync daemon. A daemon is always a server, but a
server can be either a daemon or a remote-shell spawned process.
-# SETUP
+## SETUP
See the file README.md for installation instructions.
@@ -89,12 +92,12 @@ protocol). For remote transfers, a modern rsync uses ssh for its
communications, but it may have been configured to use a different remote shell
by default, such as rsh or remsh.
-You can also specify any remote shell you like, either by using the `-e`
-command line option, or by setting the RSYNC_RSH environment variable.
+You can also specify any remote shell you like, either by using the [`-e`](#opt)
+command line option, or by setting the [`RSYNC_RSH`](#) environment variable.
Note that rsync must be installed on both the source and destination machines.
-# USAGE
+## USAGE
You use rsync in the same way you use rcp. You must specify a source and a
destination, one of which may be remote.
@@ -149,55 +152,161 @@ rsync daemon by leaving off the module name:
> rsync somehost.mydomain.com::
-See the following section for more details.
+## COPYING TO A DIFFERENT NAME
+
+When you want to copy a directory to a different name, use a trailing slash on
+the source directory to put the contents of the directory into any destination
+directory you like:
+
+> rsync -ai foo/ bar/
+
+Rsync also has the ability to customize a destination file's name when copying
+a single item. The rules for this are:
+
+- The transfer list must consist of a single item (either a file or an empty
+ directory)
+- The final element of the destination path must not exist as a directory
+- The destination path must not have been specified with a trailing slash
+
+Under those circumstances, rsync will set the name of the destination's single
+item to the last element of the destination path. Keep in mind that it is best
+to only use this idiom when copying a file and use the above trailing-slash
+idiom when copying a directory.
+
+The following example copies the `foo.c` file as `bar.c` in the `save` dir
+(assuming that `bar.c` isn't a directory):
+
+> rsync -ai src/foo.c save/bar.c
+
+The single-item copy rule might accidentally bite you if you unknowingly copy a
+single item and specify a destination dir that doesn't exist (without using a
+trailing slash). For example, if `src/*.c` matches one file and `save/dir`
+doesn't exist, this will confuse you by naming the destination file `save/dir`:
+
+> rsync -ai src/*.c save/dir
+
+To prevent such an accident, either make sure the destination dir exists or
+specify the destination path with a trailing slash:
+
+> rsync -ai src/*.c save/dir/
+
+## SORTED TRANSFER ORDER
+
+Rsync always sorts the specified filenames into its internal transfer list.
+This handles the merging together of the contents of identically named
+directories, makes it easy to remove duplicate filenames. It can, however,
+confuse someone when the files are transferred in a different order than what
+was given on the command-line.
+
+If you need a particular file to be transferred prior to another, either
+separate the files into different rsync calls, or consider using
+[`--delay-updates`](#opt) (which doesn't affect the sorted transfer order, but
+does make the final file-updating phase happen much more rapidly).
+
+## MULTI-HOST SECURITY
-# ADVANCED USAGE
+Rsync takes steps to ensure that the file requests that are shared in a
+transfer are protected against various security issues. Most of the potential
+problems arise on the receiving side where rsync takes steps to ensure that the
+list of files being transferred remains within the bounds of what was
+requested.
+
+Toward this end, rsync 3.1.2 and later have aborted when a file list contains
+an absolute or relative path that tries to escape out of the top of the
+transfer. Also, beginning with version 3.2.5, rsync does two more safety
+checks of the file list to (1) ensure that no extra source arguments were added
+into the transfer other than those that the client requested and (2) ensure
+that the file list obeys the exclude rules that were sent to the sender.
+
+For those that don't yet have a 3.2.5 client rsync (or those that want to be
+extra careful), it is safest to do a copy into a dedicated destination
+directory for the remote files when you don't trust the remote host. For
+example, instead of doing an rsync copy into your home directory:
+
+> rsync -aiv host1:dir1 ~
+
+Dedicate a "host1-files" dir to the remote content:
+
+> rsync -aiv host1:dir1 ~/host1-files
+
+See the [`--trust-sender`](#opt) option for additional details.
+
+CAUTION: it is not particularly safe to use rsync to copy files from a
+case-preserving filesystem to a case-ignoring filesystem. If you must perform
+such a copy, you should either disable symlinks via `--no-links` or enable the
+munging of symlinks via [`--munge-links`](#opt) (and make sure you use the
+right local or remote option). This will prevent rsync from doing potentially
+dangerous things if a symlink name overlaps with a file or directory. It does
+not, however, ensure that you get a full copy of all the files (since that may
+not be possible when the names overlap). A potentially better solution is to
+list all the source files and create a safe list of filenames that you pass to
+the [`--files-from`](#opt) option. Any files that conflict in name would need
+to be copied to different destination directories using more than one copy.
+
+While a copy of a case-ignoring filesystem to a case-ignoring filesystem can
+work out fairly well, if no `--delete-during` or `--delete-before` option is
+active, rsync can potentially update an existing file on the receiving side
+without noticing that the upper-/lower-case of the filename should be changed
+to match the sender.
+
+## ADVANCED USAGE
The syntax for requesting multiple files from a remote host is done by
specifying additional remote-host args in the same style as the first, or with
the hostname omitted. For instance, all these work:
-> rsync -av host:file1 :file2 host:file{3,4} /dest/
-> rsync -av host::modname/file{1,2} host::modname/file3 /dest/
-> rsync -av host::modname/file1 ::modname/file{3,4}
+> rsync -aiv host:file1 :file2 host:file{3,4} /dest/
+> rsync -aiv host::modname/file{1,2} host::modname/extra /dest/
+> rsync -aiv host::modname/first ::extra-file{1,2} /dest/
-Older versions of rsync required using quoted spaces in the SRC, like these
-examples:
+Note that a daemon connection only supports accessing one module per copy
+command, so if the start of a follow-up path doesn't begin with the
+modname of the first path, it is assumed to be a path in the module (such as
+the extra-file1 & extra-file2 that are grabbed above).
-> rsync -av host:'dir1/file1 dir2/file2' /dest
-> rsync host::'modname/dir1/file1 modname/dir2/file2' /dest
+Really old versions of rsync (2.6.9 and before) only allowed specifying one
+remote-source arg, so some people have instead relied on the remote-shell
+performing space splitting to break up an arg into multiple paths. Such
+unintuitive behavior is no longer supported by default (though you can request
+it, as described below).
-This word-splitting still works (by default) in the latest rsync, but is not as
-easy to use as the first method.
+Starting in 3.2.4, filenames are passed to a remote shell in such a way as to
+preserve the characters you give it. Thus, if you ask for a file with spaces
+in the name, that's what the remote rsync looks for:
-If you need to transfer a filename that contains whitespace, you can either
-specify the `--protect-args` (`-s`) option, or you'll need to escape the
-whitespace in a way that the remote shell will understand. For instance:
+> rsync -aiv host:'a simple file.pdf' /dest/
-> rsync -av host:'file\ name\ with\ spaces' /dest
+If you use scripts that have been written to manually apply extra quoting to
+the remote rsync args (or to require remote arg splitting), you can ask rsync
+to let your script handle the extra escaping. This is done by either adding
+the [`--old-args`](#opt) option to the rsync runs in the script (which requires
+a new rsync) or exporting [RSYNC_OLD_ARGS](#)=1 and [RSYNC_PROTECT_ARGS](#)=0
+(which works with old or new rsync versions).
-# CONNECTING TO AN RSYNC DAEMON
+## CONNECTING TO AN RSYNC DAEMON
It is also possible to use rsync without a remote shell as the transport. In
this case you will directly connect to a remote rsync daemon, typically using
TCP port 873. (This obviously requires the daemon to be running on the remote
-system, so refer to the STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS section
-below for information on that.)
+system, so refer to the [STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS](#)
+section below for information on that.)
Using rsync in this way is the same as using it with a remote shell except
that:
-- you either use a double colon :: instead of a single colon to separate the
- hostname from the path, or you use an rsync:// URL.
-- the first word of the "path" is actually a module name.
-- the remote daemon may print a message of the day when you connect.
-- if you specify no path name on the remote daemon then the list of accessible
- paths on the daemon will be shown.
-- if you specify no local destination then a listing of the specified files on
- the remote daemon is provided.
-- you must not specify the `--rsh` (`-e`) option (since that overrides the
- daemon connection to use ssh -- see USING RSYNC-DAEMON FEATURES VIA A
- REMOTE-SHELL CONNECTION below).
+- Use either double-colon syntax or rsync:// URL syntax instead of the
+ single-colon (remote shell) syntax.
+- The first element of the "path" is actually a module name.
+- Additional remote source args can use an abbreviated syntax that omits the
+ hostname and/or the module name, as discussed in [ADVANCED USAGE](#).
+- The remote daemon may print a "message of the day" when you connect.
+- If you specify only the host (with no module or path) then a list of
+ accessible modules on the daemon is output.
+- If you specify a remote source path but no destination, a listing of the
+ matching files on the remote daemon is output.
+- The [`--rsh`](#opt) (`-e`) option must be omitted to avoid changing the
+ connection style from using a socket connection to [USING RSYNC-DAEMON
+ FEATURES VIA A REMOTE-SHELL CONNECTION](#).
An example that copies all the files in a remote module named "src":
@@ -205,22 +314,23 @@ An example that copies all the files in a remote module named "src":
Some modules on the remote daemon may require authentication. If so, you will
receive a password prompt when you connect. You can avoid the password prompt
-by setting the environment variable RSYNC_PASSWORD to the password you want to
-use or using the `--password-file` option. This may be useful when scripting
-rsync.
+by setting the environment variable [`RSYNC_PASSWORD`](#) to the password you
+want to use or using the [`--password-file`](#opt) option. This may be useful
+when scripting rsync.
WARNING: On some systems environment variables are visible to all users. On
-those systems using `--password-file` is recommended.
+those systems using [`--password-file`](#opt) is recommended.
You may establish the connection via a web proxy by setting the environment
-variable RSYNC_PROXY to a hostname:port pair pointing to your web proxy. Note
-that your web proxy's configuration must support proxy connections to port 873.
+variable [`RSYNC_PROXY`](#) to a hostname:port pair pointing to your web proxy.
+Note that your web proxy's configuration must support proxy connections to port
+873.
You may also establish a daemon connection using a program as a proxy by
-setting the environment variable RSYNC_CONNECT_PROG to the commands you wish to
-run in place of making a direct socket connection. The string may contain the
-escape "%H" to represent the hostname specified in the rsync command (so use
-"%%" if you need a single "%" in your string). For example:
+setting the environment variable [`RSYNC_CONNECT_PROG`](#) to the commands you
+wish to run in place of making a direct socket connection. The string may
+contain the escape "%H" to represent the hostname specified in the rsync
+command (so use "%%" if you need a single "%" in your string). For example:
> export RSYNC_CONNECT_PROG='ssh proxyhost nc %H 873'
> rsync -av targethost1::module/src/ /dest/
@@ -229,11 +339,11 @@ escape "%H" to represent the hostname specified in the rsync command (so use
The command specified above uses ssh to run nc (netcat) on a proxyhost, which
forwards all data to port 873 (the rsync daemon) on the targethost (%H).
-Note also that if the RSYNC_SHELL environment variable is set, that program
-will be used to run the RSYNC_CONNECT_PROG command instead of using the default
-shell of the **system()** call.
+Note also that if the [`RSYNC_SHELL`](#) environment variable is set, that
+program will be used to run the `RSYNC_CONNECT_PROG` command instead of using
+the default shell of the **system()** call.
-# USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION
+## USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION
It is sometimes useful to use various features of an rsync daemon (such as
named modules) without actually allowing any new socket connections into a
@@ -250,7 +360,7 @@ on that remote host to only allow connections from "localhost".)
From the user's perspective, a daemon transfer via a remote-shell connection
uses nearly the same command-line syntax as a normal rsync-daemon transfer,
with the only exception being that you must explicitly set the remote shell
-program on the command-line with the `--rsh=COMMAND` option. (Setting the
+program on the command-line with the [`--rsh=COMMAND`](#opt) option. (Setting the
RSYNC_RSH in the environment will not turn on this functionality.) For example:
> rsync -av --rsh=ssh host::module /dest
@@ -259,73 +369,47 @@ If you need to specify a different remote-shell user, keep in mind that the
user@ prefix in front of the host is specifying the rsync-user value (for a
module that requires user-based authentication). This means that you must give
the '-l user' option to ssh when specifying the remote-shell, as in this
-example that uses the short version of the `--rsh` option:
+example that uses the short version of the [`--rsh`](#opt) option:
> rsync -av -e "ssh -l ssh-user" rsync-user@host::module /dest
The "ssh-user" will be used at the ssh level; the "rsync-user" will be used to
log-in to the "module".
-# STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS
+In this setup, the daemon is started by the ssh command that is accessing the
+system (which can be forced via the `~/.ssh/authorized_keys` file, if desired).
+However, when accessing a daemon directly, it needs to be started beforehand.
+
+## STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS
In order to connect to an rsync daemon, the remote system needs to have a
daemon already running (or it needs to have configured something like inetd to
spawn an rsync daemon for incoming connections on a particular port). For full
information on how to start a daemon that will handling incoming socket
-connections, see the **rsyncd.conf**(5) man page -- that is the config file for
-the daemon, and it contains the full details for how to run the daemon
-(including stand-alone and inetd configurations).
+connections, see the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage -- that is
+the config file for the daemon, and it contains the full details for how to run
+the daemon (including stand-alone and inetd configurations).
If you're using one of the remote-shell transports for the transfer, there is
no need to manually start an rsync daemon.
-# SORTED TRANSFER ORDER
-
-Rsync always sorts the specified filenames into its internal transfer list.
-This handles the merging together of the contents of identically named
-directories, makes it easy to remove duplicate filenames, and may confuse
-someone when the files are transferred in a different order than what was given
-on the command-line.
-
-If you need a particular file to be transferred prior to another, either
-separate the files into different rsync calls, or consider using
-`--delay-updates` (which doesn't affect the sorted transfer order, but does
-make the final file-updating phase happen much more rapidly).
-
-# EXAMPLES
-
-Here are some examples of how I use rsync.
-
-To backup my wife's home directory, which consists of large MS Word files and
-mail folders, I use a cron job that runs
-
-> rsync -Cavz . arvidsjaur:backup
+## EXAMPLES
-each night over a PPP connection to a duplicate directory on my machine
-"arvidsjaur".
+Here are some examples of how rsync can be used.
-To synchronize my samba source trees I use the following Makefile targets:
+To backup a home directory, which consists of large MS Word files and mail
+folders, a per-user cron job can be used that runs this each day:
-> get:
-> rsync -avuzb --exclude '*~' samba:samba/ .
-> put:
-> rsync -Cavuzb . samba:samba/
-> sync: get put
+> rsync -aiz . bkhost:backup/joe/
-This allows me to sync with a CVS directory at the other end of the connection.
-I then do CVS operations on the remote machine, which saves a lot of time as
-the remote CVS protocol isn't very efficient.
+To move some files from a remote host to the local host, you could run:
-I mirror a directory between my "old" and "new" ftp sites with the command:
+> rsync -aiv --remove-source-files rhost:/tmp/{file1,file2}.c ~/src/
-> rsync -az -e ssh --delete ~ftp/pub/samba nimbus:"~ftp/pub/tridge"
+## OPTION SUMMARY
-This is launched from cron every few hours.
-
-# OPTION SUMMARY
-
-Here is a short summary of the options available in rsync. Please refer to the
-detailed description below for a complete description.
+Here is a short summary of the options available in rsync. Each option also
+has its own detailed description later in this manpage.
[comment]: # (help-rsync.h)
[comment]: # (Keep these short enough that they'll be under 80 chars when indented by 7 chars.)
@@ -351,7 +435,8 @@ detailed description below for a complete description.
--append append data onto shorter files
--append-verify --append w/old data in file checksum
--dirs, -d transfer directories without recursing
---mkpath create the destination's path component
+--old-dirs, --old-d works like --dirs when talking to old rsync
+--mkpath create destination's missing path components
--links, -l copy symlinks as symlinks
--copy-links, -L transform symlink into referent file/dir
--copy-unsafe-links only "unsafe" symlinks are transformed
@@ -368,6 +453,8 @@ detailed description below for a complete description.
--owner, -o preserve owner (super-user only)
--group, -g preserve group
--devices preserve device files (super-user only)
+--copy-devices copy device contents as a regular file
+--write-devices write to devices as files (implies --inplace)
--specials preserve special files
-D same as --devices --specials
--times, -t preserve modification times
@@ -380,7 +467,6 @@ detailed description below for a complete description.
--fake-super store/recover privileged attrs using xattrs
--sparse, -S turn sequences of nulls into sparse blocks
--preallocate allocate dest files before writing them
---write-devices write to devices as files (implies --inplace)
--dry-run, -n perform a trial run with no changes made
--whole-file, -W copy files whole (w/o delta-xfer algorithm)
--checksum-choice=STR choose the checksum algorithm (aka --cc)
@@ -438,7 +524,9 @@ detailed description below for a complete description.
--include-from=FILE read include patterns from FILE
--files-from=FILE read list of source-file names from FILE
--from0, -0 all *-from/filter files are delimited by 0s
---protect-args, -s no space-splitting; wildcard chars only
+--old-args disable the modern arg-protection idiom
+--secluded-args, -s use the protocol to safely send the args
+--trust-sender trust the remote sender's file list
--copy-as=USER[:GROUP] specify user & optional group for the copy
--address=ADDRESS bind address for outgoing socket to daemon
--port=PORT specify double-colon alternate port number
@@ -496,35 +584,46 @@ accepted:
--help, -h show this help (when used with --daemon)
```
-# OPTIONS
+## OPTIONS
Rsync accepts both long (double-dash + word) and short (single-dash + letter)
options. The full list of the available options are described below. If an
option can be specified in more than one way, the choices are comma-separated.
-Some options only have a long variant, not a short. If the option takes a
-parameter, the parameter is only listed after the long variant, even though it
-must also be specified for the short. When specifying a parameter, you can
-either use the form `--option=param` or replace the '=' with whitespace. The
-parameter may need to be quoted in some manner for it to survive the shell's
-command-line parsing. Keep in mind that a leading tilde (`~`) in a filename is
-substituted by your shell, so `--option=~/foo` will not change the tilde into
-your home directory (remove the '=' for that).
+Some options only have a long variant, not a short.
+
+If the option takes a parameter, the parameter is only listed after the long
+variant, even though it must also be specified for the short. When specifying
+a parameter, you can either use the form `--option=param`, `--option param`,
+`-o=param`, `-o param`, or `-oparam` (the latter choices assume that your
+option has a short variant).
+
+The parameter may need to be quoted in some manner for it to survive the
+shell's command-line parsing. Also keep in mind that a leading tilde (`~`) in
+a pathname is substituted by your shell, so make sure that you separate the
+option name from the pathname using a space if you want the local shell to
+expand it.
+
+[comment]: # (Some markup below uses a literal non-breakable space when a backtick string)
+[comment]: # (needs to contain a space since markdown strips spaces from the start/end)
[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
-0. `--help`, `-h` `(*)`
+0. `--help`
Print a short help page describing the options available in rsync and exit.
- (*) The `-h` short option will only invoke `--help` when used without other
- options since it normally means `--human-readable`.
+ You can also use `-h` for `--help` when it is used without any other
+ options (since it normally means [`--human-readable`](#opt)).
0. `--version`, `-V`
- Print the rsync version plus other info and exit.
+ Print the rsync version plus other info and exit. When repeated, the
+ information is output is a JSON format that is still fairly readable
+ (client side only).
- The output includes the default list of checksum algorithms, the default
- list of compression algorithms, a list of compiled-in capabilities, a link
- to the rsync web site, and some license/copyright info.
+ The output includes a list of compiled-in capabilities, a list of
+ optimizations, the default list of checksum algorithms, the default list of
+ compression algorithms, the default list of daemon auth digests, a link to
+ the rsync web site, and a few other items.
0. `--verbose`, `-v`
@@ -535,12 +634,24 @@ your home directory (remove the '=' for that).
being skipped and slightly more information at the end. More than two `-v`
options should only be used if you are debugging rsync.
+ The end-of-run summary tells you the number of bytes sent to the remote
+ rsync (which is the receiving side on a local copy), the number of bytes
+ received from the remote host, and the average bytes per second of the
+ transferred data computed over the entire length of the rsync run. The
+ second line shows the total size (in bytes), which is the sum of all the
+ file sizes that rsync considered transferring. It also shows a "speedup"
+ value, which is a ratio of the total file size divided by the sum of the
+ sent and received bytes (which is really just a feel-good bigger-is-better
+ number). Note that these byte values can be made more (or less)
+ human-readable by using the [`--human-readable`](#opt) (or
+ `--no-human-readable`) options.
+
In a modern rsync, the `-v` option is equivalent to the setting of groups
- of `--info` and `--debug` options. You can choose to use these newer
- options in addition to, or in place of using `--verbose`, as any
- fine-grained settings override the implied settings of `-v`. Both `--info`
- and `--debug` have a way to ask for help that tells you exactly what flags
- are set for each increase in verbosity.
+ of [`--info`](#opt) and [`--debug`](#opt) options. You can choose to use
+ these newer options in addition to, or in place of using `--verbose`, as
+ any fine-grained settings override the implied settings of `-v`. Both
+ [`--info`](#opt) and [`--debug`](#opt) have a way to ask for help that
+ tells you exactly what flags are set for each increase in verbosity.
However, do keep in mind that a daemon's "`max verbosity`" setting will limit
how high of a level the various individual flags can be set on the daemon
@@ -561,9 +672,9 @@ your home directory (remove the '=' for that).
> rsync -a --info=progress2 src/ dest/
> rsync -avv --info=stats2,misc1,flist0 src/ dest/
- Note that `--info=name`'s output is affected by the `--out-format` and
- `--itemize-changes` (`-i`) options. See those options for more information
- on what is output and when.
+ Note that `--info=name`'s output is affected by the [`--out-format`](#opt)
+ and [`--itemize-changes`](#opt) (`-i`) options. See those options for more
+ information on what is output and when.
This option was added to 3.1.0, so an older rsync on the server side might
reject your attempts at fine-grained control (if one or more flags needed
@@ -583,8 +694,8 @@ your home directory (remove the '=' for that).
> rsync -avvv --debug=none src/ dest/
> rsync -avA --del --debug=del2,acl src/ dest/
- Note that some debug messages will only be output when `--stderr=all` is
- specified, especially those pertaining to I/O and buffer debugging.
+ Note that some debug messages will only be output when the [`--stderr=all`](#opt)
+ option is specified, especially those pertaining to I/O and buffer debugging.
Beginning in 3.2.0, this option is no longer auto-forwarded to the server
side in order to allow you to specify different debug values for each side
@@ -614,8 +725,8 @@ your home directory (remove the '=' for that).
divide up the info and error messages by file handle. For those doing
debugging or using several levels of verbosity, this option can help to
avoid clogging up the transfer stream (which should prevent any chance of
- a deadlock bug hanging things up). It also allows `--debug` to enable
- some extra I/O related messages.
+ a deadlock bug hanging things up). It also allows [`--debug`](#opt) to
+ enable some extra I/O related messages.
- `client` - causes all rsync messages to be sent to the client side
via the protocol stream. One client process outputs all messages, with
@@ -652,9 +763,9 @@ your home directory (remove the '=' for that).
the same modification timestamp. This option turns off this "quick check"
behavior, causing all files to be updated.
- This option can be a little confusing compared to `--ignore-existing` and
- `--ignore-non-existing` in that that they cause rsync to transfer fewer
- files, while this option causes rsync to transfer more files.
+ This option can be confusing compared to [`--ignore-existing`](#opt) and
+ [`--ignore-non-existing`](#opt) in that that they cause rsync to transfer
+ fewer files, while this option causes rsync to transfer more files.
0. `--size-only`
@@ -710,8 +821,9 @@ your home directory (remove the '=' for that).
before-the-transfer "Does this file need to be updated?" check.
The checksum used is auto-negotiated between the client and the server, but
- can be overridden using either the `--checksum-choice` (`--cc`) option or an
- environment variable that is discussed in that option's section.
+ can be overridden using either the [`--checksum-choice`](#opt) (`--cc`)
+ option or an environment variable that is discussed in that option's
+ section.
0. `--archive`, `-a`
@@ -720,52 +832,81 @@ your home directory (remove the '=' for that).
**not** include preserving ACLs (`-A`), xattrs (`-X`), atimes (`-U`),
crtimes (`-N`), nor the finding and preserving of hardlinks (`-H`).
- The only exception to the above equivalence is when
- `--files-from` is specified, in which case `-r` is not implied.
+ The only exception to the above equivalence is when [`--files-from`](#opt)
+ is specified, in which case [`-r`](#opt) is not implied.
0. `--no-OPTION`
You may turn off one or more implied options by prefixing the option name
- with "no-". Not all options may be prefixed with a "no-": only options that
- are implied by other options (e.g. `--no-D`, `--no-perms`) or have
- different defaults in various circumstances (e.g. `--no-whole-file`,
- `--no-blocking-io`, `--no-dirs`). You may specify either the short or the
- long option name after the "no-" prefix (e.g. `--no-R` is the same as
- `--no-relative`).
-
- For example: if you want to use `-a` (`--archive`) but don't want `-o`
- (`--owner`), instead of converting `-a` into `-rlptgD`, you could specify
- `-a --no-o` (or `-a --no-owner`).
-
- The order of the options is important: if you specify `--no-r -a`, the
- `-r` option would end up being turned on, the opposite of `-a --no-r`.
- Note also that the side-effects of the `--files-from` option are NOT
+ with "no-". Not all positive options have a negated opposite, but a lot
+ do, including those that can be used to disable an implied option (e.g.
+ `--no-D`, `--no-perms`) or have different defaults in various circumstances
+ (e.g. [`--no-whole-file`](#opt), `--no-blocking-io`, `--no-dirs`). Every
+ valid negated option accepts both the short and the long option name after
+ the "no-" prefix (e.g. `--no-R` is the same as `--no-relative`).
+
+ As an example, if you want to use [`--archive`](#opt) (`-a`) but don't want
+ [`--owner`](#opt) (`-o`), instead of converting `-a` into `-rlptgD`, you
+ can specify `-a --no-o` (aka `--archive --no-owner`).
+
+ The order of the options is important: if you specify `--no-r -a`, the `-r`
+ option would end up being turned on, the opposite of `-a --no-r`. Note
+ also that the side-effects of the [`--files-from`](#opt) option are NOT
positional, as it affects the default state of several options and slightly
- changes the meaning of `-a` (see the `--files-from` option for more
- details).
+ changes the meaning of [`-a`](#opt) (see the [`--files-from`](#opt) option
+ for more details).
0. `--recursive`, `-r`
- This tells rsync to copy directories recursively. See also `--dirs` (`-d`).
+ This tells rsync to copy directories recursively. See also
+ [`--dirs`](#opt) (`-d`) for an option that allows the scanning of a single
+ directory.
- Beginning with rsync 3.0.0, the recursive algorithm used is now an
- incremental scan that uses much less memory than before and begins the
- transfer after the scanning of the first few directories have been
- completed. This incremental scan only affects our recursion algorithm, and
- does not change a non-recursive transfer. It is also only possible when
- both ends of the transfer are at least version 3.0.0.
+ See the [`--inc-recursive`](#opt) option for a discussion of the
+ incremental recursion for creating the list of files to transfer.
- Some options require rsync to know the full file list, so these options
- disable the incremental recursion mode. These include: `--delete-before`,
- `--delete-after`, `--prune-empty-dirs`, and `--delay-updates`. Because of
- this, the default delete mode when you specify `--delete` is now
- `--delete-during` when both ends of the connection are at least 3.0.0 (use
- `--del` or `--delete-during` to request this improved deletion mode
- explicitly). See also the `--delete-delay` option that is a better choice
- than using `--delete-after`.
+0. `--inc-recursive`, `--i-r`
- Incremental recursion can be disabled using the `--no-inc-recursive` option
- or its shorter `--no-i-r` alias.
+ This option explicitly enables on incremental recursion when scanning for
+ files, which is enabled by default when using the [`--recursive`](#opt)
+ option and both sides of the transfer are running rsync 3.0.0 or newer.
+
+ Incremental recursion uses much less memory than non-incremental, while
+ also beginning the transfer more quickly (since it doesn't need to scan the
+ entire transfer hierarchy before it starts transferring files). If no
+ recursion is enabled in the source files, this option has no effect.
+
+ Some options require rsync to know the full file list, so these options
+ disable the incremental recursion mode. These include:
+ - [`--delete-before`](#opt) (the old default of [`--delete`](#opt))
+ - [`--delete-after`](#opt)
+ - [`--prune-empty-dirs`](#opt)
+ - [`--delay-updates`](#opt)
+
+ In order to make [`--delete`](#opt) compatible with incremental recursion,
+ rsync 3.0.0 made [`--delete-during`](#opt) the default delete mode (which
+ was first added in 2.6.4).
+
+ One side-effect of incremental recursion is that any missing
+ sub-directories inside a recursively-scanned directory are (by default)
+ created prior to recursing into the sub-dirs. This earlier creation point
+ (compared to a non-incremental recursion) allows rsync to then set the
+ modify time of the finished directory right away (without having to delay
+ that until a bunch of recursive copying has finished). However, these
+ early directories don't yet have their completed mode, mtime, or ownership
+ set -- they have more restrictive rights until the subdirectory's copying
+ actually begins. This early-creation idiom can be avoided by using the
+ [`--omit-dir-times`](#opt) option.
+
+ Incremental recursion can be disabled using the
+ [`--no-inc-recursive`](#opt) (`--no-i-r`) option.
+
+0. `--no-inc-recursive`, `--no-i-r`
+
+ Disables the new incremental recursion algorithm of the
+ [`--recursive`](#opt) option. This makes rsync scan the full file list
+ before it begins to transfer files. See [`--inc-recursive`](#opt) for more
+ info.
0. `--relative`, `-R`
@@ -794,7 +935,7 @@ your home directory (remove the '=' for that).
in its path. If you want to duplicate a server-side symlink, include both
the symlink via its path, and referent directory via its real path. If
you're dealing with an older rsync on the sending side, you may need to use
- the `--no-implied-dirs` option.
+ the [`--no-implied-dirs`](#opt) option.
It is also possible to limit the amount of path information that is sent as
implied directories for each path you specify. With a modern rsync on the
@@ -820,7 +961,7 @@ your home directory (remove the '=' for that).
0. `--no-implied-dirs`
- This option affects the default behavior of the `--relative` option. When
+ This option affects the default behavior of the [`--relative`](#opt) option. When
it is specified, the attributes of the implied directories from the source
names are not included in the transfer. This means that the corresponding
path elements on the destination system are left unchanged if they exist,
@@ -830,13 +971,13 @@ your home directory (remove the '=' for that).
For instance, if a command-line arg or a files-from entry told rsync to
transfer the file "path/foo/file", the directories "path" and "path/foo"
- are implied when `--relative` is used. If "path/foo" is a symlink to "bar"
+ are implied when [`--relative`](#opt) is used. If "path/foo" is a symlink to "bar"
on the destination system, the receiving rsync would ordinarily delete
"path/foo", recreate it as a directory, and receive the file into the new
directory. With `--no-implied-dirs`, the receiving rsync updates
"path/foo/file" using the existing path elements, which means that the file
ends up being created in "path/bar". Another way to accomplish this link
- preservation is to use the `--keep-dirlinks` option (which will also affect
+ preservation is to use the [`--keep-dirlinks`](#opt) option (which will also affect
symlinks to directories in the rest of the transfer).
When pulling files from an rsync older than 3.0.0, you may need to use this
@@ -847,27 +988,31 @@ your home directory (remove the '=' for that).
With this option, preexisting destination files are renamed as each file is
transferred or deleted. You can control where the backup file goes and
- what (if any) suffix gets appended using the `--backup-dir` and `--suffix`
- options.
-
- Note that if you don't specify `--backup-dir`, (1) the `--omit-dir-times`
- option will be forced on, and (2) if `--delete` is also in effect (without
- `--delete-excluded`), rsync will add a "protect" filter-rule for the backup
- suffix to the end of all your existing excludes (e.g. `-f "P *~"`). This
- will prevent previously backed-up files from being deleted. Note that if
- you are supplying your own filter rules, you may need to manually insert
- your own exclude/protect rule somewhere higher up in the list so that it
- has a high enough priority to be effective (e.g., if your rules specify a
- trailing inclusion/exclusion of `*`, the auto-added rule would never be
- reached).
+ what (if any) suffix gets appended using the [`--backup-dir`](#opt) and
+ [`--suffix`](#opt) options.
+
+ If you don't specify [`--backup-dir`](#opt):
+
+ 1. the [`--omit-dir-times`](#opt) option will be forced on
+ 2. the use of [`--delete`](#opt) (without [`--delete-excluded`](#opt)),
+ causes rsync to add a "protect" [filter-rule](#FILTER_RULES) for the
+ backup suffix to the end of all your existing filters that looks like
+ this: `-f "P *~"`. This rule prevents previously backed-up files from
+ being deleted.
+
+ Note that if you are supplying your own filter rules, you may need to
+ manually insert your own exclude/protect rule somewhere higher up in the
+ list so that it has a high enough priority to be effective (e.g. if your
+ rules specify a trailing inclusion/exclusion of `*`, the auto-added rule
+ would never be reached).
0. `--backup-dir=DIR`
- This implies the `--backup` option, and tells rsync to store all
+ This implies the [`--backup`](#opt) option, and tells rsync to store all
backups in the specified directory on the receiving side. This can be used
for incremental backups. You can additionally specify a backup suffix
- using the `--suffix` option (otherwise the files backed up in the specified
- directory will keep their original filenames).
+ using the [`--suffix`](#opt) option (otherwise the files backed up in the
+ specified directory will keep their original filenames).
Note that if you specify a relative path, the backup directory will be
relative to the destination directory, so you probably want to specify
@@ -878,8 +1023,8 @@ your home directory (remove the '=' for that).
0. `--suffix=SUFFIX`
This option allows you to override the default backup suffix used with the
- `--backup` (`-b`) option. The default suffix is a `~` if no `--backup-dir`
- was specified, otherwise it is an empty string.
+ [`--backup`](#opt) (`-b`) option. The default suffix is a `~` if no
+ [`--backup-dir`](#opt) was specified, otherwise it is an empty string.
0. `--update`, `-u`
@@ -895,9 +1040,15 @@ your home directory (remove the '=' for that).
directory where the destination has a file, the transfer would occur
regardless of the timestamps.
- This option is a transfer rule, not an exclude, so it doesn't affect the
- data that goes into the file-lists, and thus it doesn't affect deletions.
- It just limits the files that the receiver requests to be transferred.
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+ A caution for those that choose to combine [`--inplace`](#opt) with
+ `--update`: an interrupted transfer will leave behind a partial file on the
+ receiving side that has a very recent modified time, so re-running the
+ transfer will probably **not** continue the interrupted file. As such, it
+ is usually best to avoid combining this with[ `--inplace`](#opt) unless you
+ have implemented manual steps to handle any interrupted in-progress files.
0. `--inplace`
@@ -924,7 +1075,7 @@ your home directory (remove the '=' for that).
for the open of the file for writing to be successful.
- The efficiency of rsync's delta-transfer algorithm may be reduced if some
data in the destination file is overwritten before it can be copied to a
- position later in the file. This does not apply if you use `--backup`,
+ position later in the file. This does not apply if you use [`--backup`](#opt),
since rsync is smart enough to use the backup file as the basis file for
the transfer.
@@ -936,10 +1087,10 @@ your home directory (remove the '=' for that).
bound. It can also help keep a copy-on-write filesystem snapshot from
diverging the entire contents of a file that only has minor changes.
- The option implies `--partial` (since an interrupted transfer does not
- delete the file), but conflicts with `--partial-dir` and `--delay-updates`.
- Prior to rsync 2.6.4 `--inplace` was also incompatible with
- `--compare-dest` and `--link-dest`.
+ The option implies [`--partial`](#opt) (since an interrupted transfer does
+ not delete the file), but conflicts with [`--partial-dir`](#opt) and
+ [`--delay-updates`](#opt). Prior to rsync 2.6.4 `--inplace` was also
+ incompatible with [`--compare-dest`](#opt) and [`--link-dest`](#opt).
0. `--append`
@@ -965,13 +1116,13 @@ your home directory (remove the '=' for that).
0. `--append-verify`
- This special copy mode works like `--append` except that all the data in
- the file is included in the checksum verification (making it much less
+ This special copy mode works like [`--append`](#opt) except that all the
+ data in the file is included in the checksum verification (making it less
efficient but also potentially safer). This option **can be dangerous** if
you aren't 100% sure that all the files in the transfer are shared, growing
- files. See the `--append` option for more details.
+ files. See the [`--append`](#opt) option for more details.
- Note: prior to rsync 3.0.0, the `--append` option worked like
+ Note: prior to rsync 3.0.0, the [`--append`](#opt) option worked like
`--append-verify`, so if you are interacting with an older rsync (or the
transfer is using a protocol prior to 30), specifying either append option
will initiate an `--append-verify` transfer.
@@ -979,64 +1130,88 @@ your home directory (remove the '=' for that).
0. `--dirs`, `-d`
Tell the sending side to include any directories that are encountered.
- Unlike `--recursive`, a directory's contents are not copied unless the
- directory name specified is "." or ends with a trailing slash (e.g. ".",
- "dir/.", "dir/", etc.). Without this option or the `--recursive` option,
- rsync will skip all directories it encounters (and output a message to that
- effect for each one). If you specify both `--dirs` and `--recursive`,
- `--recursive` takes precedence.
-
- The `--dirs` option is implied by the `--files-from` option or the
- `--list-only` option (including an implied `--list-only` usage) if
- `--recursive` wasn't specified (so that directories are seen in the
- listing). Specify `--no-dirs` (or `--no-d`) if you want to turn this off.
-
- There is also a backward-compatibility helper option, `--old-dirs` (or
- `--old-d`) that tells rsync to use a hack of `-r --exclude='/*/*'` to get
+ Unlike [`--recursive`](#opt), a directory's contents are not copied unless
+ the directory name specified is "." or ends with a trailing slash (e.g.
+ ".", "dir/.", "dir/", etc.). Without this option or the
+ [`--recursive`](#opt) option, rsync will skip all directories it encounters
+ (and output a message to that effect for each one). If you specify both
+ `--dirs` and [`--recursive`](#opt), `--recursive` takes precedence.
+
+ The `--dirs` option is implied by the [`--files-from`](#opt) option or the
+ [`--list-only`](#opt) option (including an implied [`--list-only`](#opt)
+ usage) if [`--recursive`](#opt) wasn't specified (so that directories are
+ seen in the listing). Specify `--no-dirs` (or `--no-d`) if you want to
+ turn this off.
+
+ There is also a backward-compatibility helper option, `--old-dirs`
+ (`--old-d`) that tells rsync to use a hack of `-r --exclude='/*/*'` to get
an older rsync to list a single directory without recursing.
0. `--mkpath`
- Create a missing path component of the destination arg. This allows rsync
- to create multiple levels of missing destination dirs and to create a path
- in which to put a single renamed file. Keep in mind that you'll need to
- supply a trailing slash if you want the entire destination path to be
- treated as a directory when copying a single arg (making rsync behave the
- same way that it would if the path component of the destination had already
- existed).
+ Create all missing path components of the destination path.
- For example, the following creates a copy of file foo as bar in the sub/dir
- directory, creating dirs "sub" and "sub/dir" if either do not yet exist:
+ By default, rsync allows only the final component of the destination path
+ to not exist, which is an attempt to help you to validate your destination
+ path. With this option, rsync creates all the missing destination-path
+ components, just as if `mkdir -p $DEST_PATH` had been run on the receiving
+ side.
- > rsync -ai --mkpath foo sub/dir/bar
+ When specifying a destination path, including a trailing slash ensures that
+ the whole path is treated as directory names to be created, even when the
+ file list has a single item. See the [COPYING TO A DIFFERENT NAME](#)
+ section for full details on how rsync decides if a final destination-path
+ component should be created as a directory or not.
- If you instead ran the following, it would have created file foo in the
- sub/dir/bar directory:
+ If you would like the newly-created destination dirs to match the dirs on
+ the sending side, you should be using [`--relative`](#opt) (`-R`) instead
+ of `--mkpath`. For instance, the following two commands result in the same
+ destination tree, but only the second command ensures that the
+ "some/extra/path" components match the dirs on the sending side:
- > rsync -ai --mkpath foo sub/dir/bar/
+ > rsync -ai --mkpath host:some/extra/path/*.c some/extra/path/
+ > rsync -aiR host:some/extra/path/*.c ./
0. `--links`, `-l`
- When symlinks are encountered, recreate the symlink on the destination.
+ Add symlinks to the transferred files instead of noisily ignoring them with
+ a "non-regular file" warning for each symlink encountered. You can
+ alternately silence the warning by specifying [`--info=nonreg0`](#opt).
+
+ The default handling of symlinks is to recreate each symlink's unchanged
+ value on the receiving side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
0. `--copy-links`, `-L`
- When symlinks are encountered, the item that they point to (the referent)
- is copied, rather than the symlink. In older versions of rsync, this
- option also had the side-effect of telling the receiving side to follow
- symlinks, such as symlinks to directories. In a modern rsync such as this
- one, you'll need to specify `--keep-dirlinks` (`-K`) to get this extra
- behavior. The only exception is when sending files to an rsync that is too
- old to understand `-K` -- in that case, the `-L` option will still have the
- side-effect of `-K` on that older receiving rsync.
+ The sender transforms each symlink encountered in the transfer into the
+ referent item, following the symlink chain to the file or directory that it
+ references. If a symlink chain is broken, an error is output and the file
+ is dropped from the transfer.
+
+ This option supersedes any other options that affect symlinks in the
+ transfer, since there are no symlinks left in the transfer.
+
+ This option does not change the handling of existing symlinks on the
+ receiving side, unlike versions of rsync prior to 2.6.3 which had the
+ side-effect of telling the receiving side to also follow symlinks. A
+ modern rsync won't forward this option to a remote receiver (since only the
+ sender needs to know about it), so this caveat should only affect someone
+ using an rsync client older than 2.6.7 (which is when `-L` stopped being
+ forwarded to the receiver).
+
+ See the [`--keep-dirlinks`](#opt) (`-K`) if you need a symlink to a
+ directory to be treated as a real directory on the receiving side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
0. `--copy-unsafe-links`
This tells rsync to copy the referent of symbolic links that point outside
the copied tree. Absolute symlinks are also treated like ordinary files,
- and so are any symlinks in the source path itself when `--relative` is
- used. This option has no additional effect if `--copy-links` was also
- specified.
+ and so are any symlinks in the source path itself when [`--relative`](#opt)
+ is used.
Note that the cut-off point is the top of the transfer, which is the part
of the path that rsync isn't mentioning in the verbose output. If you copy
@@ -1047,50 +1222,88 @@ your home directory (remove the '=' for that).
slash) to "/dest/subdir" that would not allow symlinks to any files outside
of "subdir".
+ Note that safe symlinks are only copied if [`--links`](#opt) was also
+ specified or implied. The `--copy-unsafe-links` option has no extra effect
+ when combined with [`--copy-links`](#opt).
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
0. `--safe-links`
- This tells rsync to ignore any symbolic links which point outside the
- copied tree. All absolute symlinks are also ignored. Using this option in
- conjunction with `--relative` may give unexpected results.
+ This tells the receiving rsync to ignore any symbolic links in the transfer
+ which point outside the copied tree. All absolute symlinks are also
+ ignored.
-0. `--munge-links`
+ Since this ignoring is happening on the receiving side, it will still be
+ effective even when the sending side has munged symlinks (when it is using
+ [`--munge-links`](#opt)). It also affects deletions, since the file being
+ present in the transfer prevents any matching file on the receiver from
+ being deleted when the symlink is deemed to be unsafe and is skipped.
- This option tells rsync to (1) modify all symlinks on the receiving side in
- a way that makes them unusable but recoverable (see below), or (2) to
- unmunge symlinks on the sending side that had been stored in a munged
- state. This is useful if you don't quite trust the source of the data to
- not try to slip in a symlink to a unexpected place.
+ This option must be combined with [`--links`](#opt) (or
+ [`--archive`](#opt)) to have any symlinks in the transfer to conditionally
+ ignore. Its effect is superseded by [`--copy-unsafe-links`](#opt).
- The way rsync disables the use of symlinks is to prefix each one with the
- string "/rsyncd-munged/". This prevents the links from being used as long
- as that directory does not exist. When this option is enabled, rsync will
- refuse to run if that path is a directory or a symlink to a directory.
+ Using this option in conjunction with [`--relative`](#opt) may give
+ unexpected results.
- The option only affects the client side of the transfer, so if you need it
- to affect the server, specify it via `--remote-option`. (Note that in a
- local transfer, the client side is the sender.)
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
- This option has no affect on a daemon, since the daemon configures whether
- it wants munged symlinks via its "`munge symlinks`" parameter. See also the
- "munge-symlinks" perl script in the support directory of the source code.
+0. `--munge-links`
+
+ This option affects just one side of the transfer and tells rsync to munge
+ symlink values when it is receiving files or unmunge symlink values when it
+ is sending files. The munged values make the symlinks unusable on disk but
+ allows the original contents of the symlinks to be recovered.
+
+ The server-side rsync often enables this option without the client's
+ knowledge, such as in an rsync daemon's configuration file or by an option
+ given to the rrsync (restricted rsync) script. When specified on the
+ client side, specify the option normally if it is the client side that
+ has/needs the munged symlinks, or use `-M--munge-links` to give the option
+ to the server when it has/needs the munged symlinks. Note that on a local
+ transfer, the client is the sender, so specifying the option directly
+ unmunges symlinks while specifying it as a remote option munges symlinks.
+
+ This option has no effect when sent to a daemon via [`--remote-option`](#opt)
+ because the daemon configures whether it wants munged symlinks via its
+ "`munge symlinks`" parameter.
+
+ The symlink value is munged/unmunged once it is in the transfer, so any
+ option that transforms symlinks into non-symlinks occurs prior to the
+ munging/unmunging **except** for [`--safe-links`](#opt), which is a choice
+ that the receiver makes, so it bases its decision on the munged/unmunged
+ value. This does mean that if a receiver has munging enabled, that using
+ [`--safe-links`](#opt) will cause all symlinks to be ignored (since they
+ are all absolute).
+
+ The method that rsync uses to munge the symlinks is to prefix each one's
+ value with the string "/rsyncd-munged/". This prevents the links from
+ being used as long as the directory does not exist. When this option is
+ enabled, rsync will refuse to run if that path is a directory or a symlink
+ to a directory (though it only checks at startup). See also the
+ "munge-symlinks" python script in the support directory of the source code
+ for a way to munge/unmunge one or more symlinks in-place.
0. `--copy-dirlinks`, `-k`
This option causes the sending side to treat a symlink to a directory as
though it were a real directory. This is useful if you don't want symlinks
- to non-directories to be affected, as they would be using `--copy-links`.
+ to non-directories to be affected, as they would be using
+ [`--copy-links`](#opt).
Without this option, if the sending side has replaced a directory with a
symlink to a directory, the receiving side will delete anything that is in
the way of the new symlink, including a directory hierarchy (as long as
- `--force` or `--delete` is in effect).
+ [`--force`](#opt) or [`--delete`](#opt) is in effect).
- See also `--keep-dirlinks` for an analogous option for the receiving side.
+ See also [`--keep-dirlinks`](#opt) for an analogous option for the
+ receiving side.
`--copy-dirlinks` applies to all symlinks to directories in the source. If
you want to follow only a few specified symlinks, a trick you can use is to
pass them as additional source args with a trailing slash, using
- `--relative` to make the paths match up right. For example:
+ [`--relative`](#opt) to make the paths match up right. For example:
> rsync -r --relative src/./ src/./follow-me/ dest/
@@ -1099,6 +1312,8 @@ your home directory (remove the '=' for that).
directory in the file-list which overrides the symlink found during the
scan of "src/./".
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
0. `--keep-dirlinks`, `-K`
This option causes the receiving side to treat a symlink to a directory as
@@ -1114,14 +1329,18 @@ your home directory (remove the '=' for that).
"bar".
One note of caution: if you use `--keep-dirlinks`, you must trust all the
- symlinks in the copy! If it is possible for an untrusted user to create
- their own symlink to any directory, the user could then (on a subsequent
+ symlinks in the copy or enable the [`--munge-links`](#opt) option on the
+ receiving side! If it is possible for an untrusted user to create their
+ own symlink to any real directory, the user could then (on a subsequent
copy) replace the symlink with a real directory and affect the content of
whatever directory the symlink references. For backup copies, you are
better off using something like a bind mount instead of a symlink to modify
your receiving hierarchy.
- See also `--copy-dirlinks` for an analogous option for the sending side.
+ See also [`--copy-dirlinks`](#opt) for an analogous option for the sending
+ side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
0. `--hard-links`, `-H`
@@ -1138,41 +1357,42 @@ your home directory (remove the '=' for that).
is present in the source file list), the copying algorithm will not break
them explicitly. However, if one or more of the paths have content
differences, the normal file-update process will break those extra links
- (unless you are using the `--inplace` option).
- - If you specify a `--link-dest` directory that contains hard links, the
- linking of the destination files against the `--link-dest` files can
- cause some paths in the destination to become linked together due to the
- `--link-dest` associations.
+ (unless you are using the [`--inplace`](#opt) option).
+ - If you specify a [`--link-dest`](#opt) directory that contains hard
+ links, the linking of the destination files against the
+ [`--link-dest`](#opt) files can cause some paths in the destination to
+ become linked together due to the [`--link-dest`](#opt) associations.
Note that rsync can only detect hard links between files that are inside
the transfer set. If rsync updates a file that has extra hard-link
connections to files outside the transfer, that linkage will be broken. If
- you are tempted to use the `--inplace` option to avoid this breakage, be
+ you are tempted to use the [`--inplace`](#opt) option to avoid this breakage, be
very careful that you know how your files are being updated so that you are
certain that no unintended changes happen due to lingering hard links (and
- see the `--inplace` option for more caveats).
+ see the [`--inplace`](#opt) option for more caveats).
- If incremental recursion is active (see `--recursive`), rsync may transfer
- a missing hard-linked file before it finds that another link for that
- contents exists elsewhere in the hierarchy. This does not affect the
- accuracy of the transfer (i.e. which files are hard-linked together), just
- its efficiency (i.e. copying the data for a new, early copy of a
+ If incremental recursion is active (see [`--inc-recursive`](#opt)), rsync
+ may transfer a missing hard-linked file before it finds that another link
+ for that contents exists elsewhere in the hierarchy. This does not affect
+ the accuracy of the transfer (i.e. which files are hard-linked together),
+ just its efficiency (i.e. copying the data for a new, early copy of a
hard-linked file that could have been found later in the transfer in
another member of the hard-linked set of files). One way to avoid this
inefficiency is to disable incremental recursion using the
- `--no-inc-recursive` option.
+ [`--no-inc-recursive`](#opt) option.
0. `--perms`, `-p`
This option causes the receiving rsync to set the destination permissions
- to be the same as the source permissions. (See also the `--chmod` option
- for a way to modify what rsync considers to be the source permissions.)
+ to be the same as the source permissions. (See also the [`--chmod`](#opt)
+ option for a way to modify what rsync considers to be the source
+ permissions.)
When this option is _off_, permissions are set as follows:
- Existing files (including updated files) retain their existing
- permissions, though the `--executability` option might change just the
- execute permission for the file.
+ permissions, though the [`--executability`](#opt) option might change
+ just the execute permission for the file.
- New files get their "normal" permission bits set to the source file's
permissions masked with the receiving directory's default permissions
(either the receiving process's umask, or the permissions specified via
@@ -1180,18 +1400,19 @@ your home directory (remove the '=' for that).
bits disabled except in the case where a new directory inherits a setgid
bit from its parent directory.
- Thus, when `--perms` and `--executability` are both disabled, rsync's
+ Thus, when `--perms` and [`--executability`](#opt) are both disabled, rsync's
behavior is the same as that of other file-copy utilities, such as **cp**(1)
and **tar**(1).
In summary: to give destination files (both old and new) the source
permissions, use `--perms`. To give new files the destination-default
permissions (while leaving existing files unchanged), make sure that the
- `--perms` option is off and use `--chmod=ugo=rwX` (which ensures that all
- non-masked bits get enabled). If you'd care to make this latter behavior
- easier to type, you could define a popt alias for it, such as putting this
- line in the file `~/.popt` (the following defines the `-Z` option, and
- includes `--no-g` to use the default group of the destination dir):
+ `--perms` option is off and use [`--chmod=ugo=rwX`](#opt) (which ensures
+ that all non-masked bits get enabled). If you'd care to make this latter
+ behavior easier to type, you could define a popt alias for it, such as
+ putting this line in the file `~/.popt` (the following defines the `-Z`
+ option, and includes `--no-g` to use the default group of the destination
+ dir):
> rsync alias -Z --no-p --no-g --chmod=ugo=rwX
@@ -1215,8 +1436,8 @@ your home directory (remove the '=' for that).
0. `--executability`, `-E`
This option causes rsync to preserve the executability (or
- non-executability) of regular files when `--perms` is not enabled. A
- regular file is considered to be executable if at least one 'x' is turned
+ non-executability) of regular files when [`--perms`](#opt) is not enabled.
+ A regular file is considered to be executable if at least one 'x' is turned
on in its permissions. When an existing destination file's executability
differs from that of the corresponding source file, rsync modifies the
destination file's permissions as follows:
@@ -1225,16 +1446,16 @@ your home directory (remove the '=' for that).
- To make a file executable, rsync turns on each 'x' permission that has a
corresponding 'r' permission enabled.
- If `--perms` is enabled, this option is ignored.
+ If [`--perms`](#opt) is enabled, this option is ignored.
0. `--acls`, `-A`
This option causes rsync to update the destination ACLs to be the same as
- the source ACLs. The option also implies `--perms`.
+ the source ACLs. The option also implies [`--perms`](#opt).
The source and destination systems must have compatible ACL entries for
- this option to work properly. See the `--fake-super` option for a way to
- backup and restore ACLs that are not compatible.
+ this option to work properly. See the [`--fake-super`](#opt) option for a
+ way to backup and restore ACLs that are not compatible.
0. `--xattrs`, `-X`
@@ -1244,7 +1465,7 @@ your home directory (remove the '=' for that).
For systems that support extended-attribute namespaces, a copy being done
by a super-user copies all namespaces except system.\*. A normal user only
copies the user.\* namespace. To be able to backup and restore non-user
- namespaces as a normal user, see the `--fake-super` option.
+ namespaces as a normal user, see the [`--fake-super`](#opt) option.
The above name filtering can be overridden by using one or more filter
options with the **x** modifier. When you specify an xattr-affecting
@@ -1266,8 +1487,8 @@ your home directory (remove the '=' for that).
> --filter='-xr *'
Note that the `-X` option does not copy rsync's special xattr values (e.g.
- those used by `--fake-super`) unless you repeat the option (e.g. `-XX`).
- This "copy all xattrs" mode cannot be used with `--fake-super`.
+ those used by [`--fake-super`](#opt)) unless you repeat the option (e.g. `-XX`).
+ This "copy all xattrs" mode cannot be used with [`--fake-super`](#opt).
0. `--chmod=CHMOD`
@@ -1275,7 +1496,7 @@ your home directory (remove the '=' for that).
to the permission of the files in the transfer. The resulting value is
treated as though it were the permissions that the sending side supplied
for the file, which means that this option can seem to have no effect on
- existing files if `--perms` is not enabled.
+ existing files if [`--perms`](#opt) is not enabled.
In addition to the normal parsing rules specified in the **chmod**(1)
manpage, you can specify an item that should only apply to a directory by
@@ -1294,20 +1515,20 @@ your home directory (remove the '=' for that).
It is also legal to specify multiple `--chmod` options, as each additional
option is just appended to the list of changes to make.
- See the `--perms` and `--executability` options for how the resulting
- permission value can be applied to the files in the transfer.
+ See the [`--perms`](#opt) and [`--executability`](#opt) options for how the
+ resulting permission value can be applied to the files in the transfer.
0. `--owner`, `-o`
This option causes rsync to set the owner of the destination file to be the
same as the source file, but only if the receiving rsync is being run as
- the super-user (see also the `--super` and `--fake-super` options). Without
- this option, the owner of new and/or transferred files are set to the
- invoking user on the receiving side.
+ the super-user (see also the [`--super`](#opt) and [`--fake-super`](#opt)
+ options). Without this option, the owner of new and/or transferred files
+ are set to the invoking user on the receiving side.
The preservation of ownership will associate matching names by default, but
may fall back to using the ID number in some circumstances (see also the
- `--numeric-ids` option for a full discussion).
+ [`--numeric-ids`](#opt) option for a full discussion).
0. `--group`, `-g`
@@ -1320,60 +1541,88 @@ your home directory (remove the '=' for that).
The preservation of group information will associate matching names by
default, but may fall back to using the ID number in some circumstances
- (see also the `--numeric-ids` option for a full discussion).
+ (see also the [`--numeric-ids`](#opt) option for a full discussion).
0. `--devices`
This option causes rsync to transfer character and block device files to
- the remote system to recreate these devices. This option has no effect if
- the receiving rsync is not run as the super-user (see also the `--super`
- and `--fake-super` options).
+ the remote system to recreate these devices. If the receiving rsync is not
+ being run as the super-user, rsync silently skips creating the device files
+ (see also the [`--super`](#opt) and [`--fake-super`](#opt) options).
+
+ By default, rsync generates a "non-regular file" warning for each device
+ file encountered when this option is not set. You can silence the warning
+ by specifying [`--info=nonreg0`](#opt).
0. `--specials`
- This option causes rsync to transfer special files such as named sockets
- and fifos.
+ This option causes rsync to transfer special files, such as named sockets
+ and fifos. If the receiving rsync is not being run as the super-user,
+ rsync silently skips creating the special files (see also the
+ [`--super`](#opt) and [`--fake-super`](#opt) options).
+
+ By default, rsync generates a "non-regular file" warning for each special
+ file encountered when this option is not set. You can silence the warning
+ by specifying [`--info=nonreg0`](#opt).
0. `-D`
- The `-D` option is equivalent to `--devices --specials`.
+ The `-D` option is equivalent to "[`--devices`](#opt)
+ [`--specials`](#opt)".
+
+0. `--copy-devices`
+
+ This tells rsync to treat a device on the sending side as a regular file,
+ allowing it to be copied to a normal destination file (or another device
+ if `--write-devices` was also specified).
+
+ This option is refused by default by an rsync daemon.
0. `--write-devices`
This tells rsync to treat a device on the receiving side as a regular file,
allowing the writing of file data into a device.
- This option implies the `--inplace` option.
+ This option implies the [`--inplace`](#opt) option.
Be careful using this, as you should know what devices are present on the
- receiving side of the transfer, especially if running rsync as root.
+ receiving side of the transfer, especially when running rsync as root.
- This option is refused by an rsync daemon.
+ This option is refused by default by an rsync daemon.
0. `--times`, `-t`
This tells rsync to transfer modification times along with the files and
update them on the remote system. Note that if this option is not used,
the optimization that excludes files that have not been modified cannot be
- effective; in other words, a missing `-t` or `-a` will cause the next
- transfer to behave as if it used `-I`, causing all files to be updated
- (though rsync's delta-transfer algorithm will make the update fairly
- efficient if the files haven't actually changed, you're much better off
- using `-t`).
+ effective; in other words, a missing `-t` (or [`-a`](#opt)) will cause the
+ next transfer to behave as if it used [`--ignore-times`](#opt) (`-I`),
+ causing all files to be updated (though rsync's delta-transfer algorithm
+ will make the update fairly efficient if the files haven't actually
+ changed, you're much better off using `-t`).
+
+ A modern rsync that is using transfer protocol 30 or 31 conveys a modify
+ time using up to 8-bytes. If rsync is forced to speak an older protocol
+ (perhaps due to the remote rsync being older than 3.0.0) a modify time is
+ conveyed using 4-bytes. Prior to 3.2.7, these shorter values could convey
+ a date range of 13-Dec-1901 to 19-Jan-2038. Beginning with 3.2.7, these
+ 4-byte values now convey a date range of 1-Jan-1970 to 7-Feb-2106. If you
+ have files dated older than 1970, make sure your rsync executables are
+ upgraded so that the full range of dates can be conveyed.
0. `--atimes`, `-U`
This tells rsync to set the access (use) times of the destination files to
the same value as the source files.
- If repeated, it also sets the `--open-noatime` option, which can help you
+ If repeated, it also sets the [`--open-noatime`](#opt) option, which can help you
to make the sending and receiving systems have the same access times on the
transferred files without needing to run rsync an extra time after a file
is transferred.
Note that some older rsync versions (prior to 3.2.0) may have been built
- with a pre-release `--atimes` patch that does not imply `--open-noatime`
- when this option is repeated.
+ with a pre-release `--atimes` patch that does not imply
+ [`--open-noatime`](#opt) when this option is repeated.
0. `--open-noatime`
@@ -1387,25 +1636,20 @@ your home directory (remove the '=' for that).
0. `--crtimes`, `-N,`
This tells rsync to set the create times (newness) of the destination
- files to the same value as the source files.
+ files to the same value as the source files. Your OS & filesystem must
+ support the setting of arbitrary creation (birth) times for this option
+ to be supported.
0. `--omit-dir-times`, `-O`
This tells rsync to omit directories when it is preserving modification,
access, and create times. If NFS is sharing the directories on the receiving
side, it is a good idea to use `-O`. This option is inferred if you use
- `--backup` without `--backup-dir`.
-
- This option also has the side-effect of avoiding early creation of
- directories in incremental recursion copies. The default `--inc-recursive`
- copying normally does an early-create pass of all the sub-directories in a
- parent directory in order for it to be able to then set the modify time of
- the parent directory right away (without having to delay that until a bunch
- of recursive copying has finished). This early-create idiom is not
- necessary if directory modify times are not being preserved, so it is
- skipped. Since early-create directories don't have accurate mode, mtime,
- or ownership, the use of this option can help when someone wants to avoid
- these partially-finished directories.
+ [`--backup`](#opt) without [`--backup-dir`](#opt).
+
+ This option also has the side-effect of avoiding early creation of missing
+ sub-directories when incremental recursion is enabled, as discussed in the
+ [`--inc-recursive`](#opt) section.
0. `--omit-link-times`, `-J`
@@ -1416,12 +1660,13 @@ your home directory (remove the '=' for that).
This tells the receiving side to attempt super-user activities even if the
receiving rsync wasn't run by the super-user. These activities include:
- preserving users via the `--owner` option, preserving all groups (not just
- the current user's groups) via the `--group` option, and copying devices
- via the `--devices` option. This is useful for systems that allow such
- activities without being the super-user, and also for ensuring that you
- will get errors if the receiving side isn't being run as the super-user.
- To turn off super-user activities, the super-user can use `--no-super`.
+ preserving users via the [`--owner`](#opt) option, preserving all groups
+ (not just the current user's groups) via the [`--group`](#opt) option, and
+ copying devices via the [`--devices`](#opt) option. This is useful for
+ systems that allow such activities without being the super-user, and also
+ for ensuring that you will get errors if the receiving side isn't being run
+ as the super-user. To turn off super-user activities, the super-user can
+ use `--no-super`.
0. `--fake-super`
@@ -1434,15 +1679,15 @@ your home directory (remove the '=' for that).
u-s,g-s,o-t for safety) or that would limit the owner's access (since the
real super-user can always access/change a file, the files we create can
always be accessed/changed by the creating user). This option also handles
- ACLs (if `--acls` was specified) and non-user extended attributes (if
- `--xattrs` was specified).
+ ACLs (if [`--acls`](#opt) was specified) and non-user extended attributes
+ (if [`--xattrs`](#opt) was specified).
This is a good way to backup data without using a super-user, and to store
ACLs from incompatible systems.
The `--fake-super` option only affects the side where the option is used.
To affect the remote side of a remote-shell connection, use the
- `--remote-option` (`-M`) option:
+ [`--remote-option`](#opt) (`-M`) option:
> rsync -av -M--fake-super /src/ host:/dest/
@@ -1451,21 +1696,22 @@ your home directory (remove the '=' for that).
files, specify `-M--fake-super`. If you wish a local copy to enable this
option just for the source files, combine `--fake-super` with `-M--super`.
- This option is overridden by both `--super` and `--no-super`.
+ This option is overridden by both [`--super`](#opt) and `--no-super`.
- See also the "`fake super`" setting in the daemon's rsyncd.conf file.
+ See also the [`fake super`](rsyncd.conf.5#fake_super) setting in the
+ daemon's rsyncd.conf file.
0. `--sparse`, `-S`
Try to handle sparse files efficiently so they take up less space on the
- destination. If combined with `--inplace` the file created might not end
- up with sparse blocks with some combinations of kernel version and/or
- filesystem type. If `--whole-file` is in effect (e.g. for a local copy)
- then it will always work because rsync truncates the file prior to writing
- out the updated version.
+ destination. If combined with [`--inplace`](#opt) the file created might
+ not end up with sparse blocks with some combinations of kernel version
+ and/or filesystem type. If [`--whole-file`](#opt) is in effect (e.g. for a
+ local copy) then it will always work because rsync truncates the file prior
+ to writing out the updated version.
Note that versions of rsync older than 3.1.3 will reject the combination of
- `--sparse` and `--inplace`.
+ `--sparse` and [`--inplace`](#opt).
0. `--preallocate`
@@ -1480,26 +1726,26 @@ your home directory (remove the '=' for that).
the destination is not an extent-supporting filesystem (such as ext4, xfs,
NTFS, etc.), this option may have no positive effect at all.
- If combined with `--sparse`, the file will only have sparse blocks (as
- opposed to allocated sequences of null bytes) if the kernel version and
+ If combined with [`--sparse`](#opt), the file will only have sparse blocks
+ (as opposed to allocated sequences of null bytes) if the kernel version and
filesystem type support creating holes in the allocated data.
0. `--dry-run`, `-n`
This makes rsync perform a trial run that doesn't make any changes (and
produces mostly the same output as a real run). It is most commonly used
- in combination with the `--verbose`, `-v` and/or `--itemize-changes`, `-i`
- options to see what an rsync command is going to do before one actually
- runs it.
-
- The output of `--itemize-changes` is supposed to be exactly the same on a
- dry run and a subsequent real run (barring intentional trickery and system
- call failures); if it isn't, that's a bug. Other output should be mostly
- unchanged, but may differ in some areas. Notably, a dry run does not send
- the actual data for file transfers, so `--progress` has no effect, the
- "bytes sent", "bytes received", "literal data", and "matched data"
- statistics are too small, and the "speedup" value is equivalent to a run
- where no file transfers were needed.
+ in combination with the [`--verbose`](#opt) (`-v`) and/or
+ [`--itemize-changes`](#opt) (`-i`) options to see what an rsync command is
+ going to do before one actually runs it.
+
+ The output of [`--itemize-changes`](#opt) is supposed to be exactly the
+ same on a dry run and a subsequent real run (barring intentional trickery
+ and system call failures); if it isn't, that's a bug. Other output should
+ be mostly unchanged, but may differ in some areas. Notably, a dry run does
+ not send the actual data for file transfers, so [`--progress`](#opt) has no
+ effect, the "bytes sent", "bytes received", "literal data", and "matched
+ data" statistics are too small, and the "speedup" value is equivalent to a
+ run where no file transfers were needed.
0. `--whole-file`, `-W`
@@ -1511,11 +1757,20 @@ your home directory (remove the '=' for that).
source and destination are specified as local paths, but only if no
batch-writing option is in effect.
+0. `--no-whole-file`, `--no-W`
+
+ Disable whole-file updating when it is enabled by default for a local
+ transfer. This usually slows rsync down, but it can be useful if you are
+ trying to minimize the writes to the destination file (if combined with
+ [`--inplace`](#opt)) or for testing the checksum-based update algorithm.
+
+ See also the [`--whole-file`](#opt) option.
+
0. `--checksum-choice=STR`, `--cc=STR`
This option overrides the checksum algorithms. If one algorithm name is
specified, it is used for both the transfer checksums and (assuming
- `--checksum` is specified) the pre-transfer checksums. If two
+ [`--checksum`](#opt) is specified) the pre-transfer checksums. If two
comma-separated names are supplied, the first name affects the transfer
checksums, and the second name affects the pre-transfer checksums (`-c`).
@@ -1527,15 +1782,16 @@ your home directory (remove the '=' for that).
- `xxh64` (aka `xxhash`)
- `md5`
- `md4`
+ - `sha1`
- `none`
Run `rsync --version` to see the default checksum list compiled into your
version (which may differ from the list above).
- If "none" is specified for the first (or only) name, the `--whole-file`
+ If "none" is specified for the first (or only) name, the [`--whole-file`](#opt)
option is forced on and no checksum verification is performed on the
transferred data. If "none" is specified for the second (or only) name,
- the `--checksum` option cannot be used.
+ the [`--checksum`](#opt) option cannot be used.
The "auto" option is the default, where rsync bases its algorithm choice on
a negotiation between the client and the server as follows:
@@ -1548,14 +1804,14 @@ your home directory (remove the '=' for that).
and various flavors of MD4 based on protocol age).
The default order can be customized by setting the environment variable
- RSYNC_CHECKSUM_LIST to a space-separated list of acceptable checksum names.
- If the string contains a "`&`" character, it is separated into the "client
- string & server string", otherwise the same string
- applies to both. If the string (or string portion) contains no
- non-whitespace characters, the default checksum list is used. This method
- does not allow you to specify the transfer checksum separately from the
- pre-transfer checksum, and it discards "auto" and all unknown checksum
- names. A list with only invalid names results in a failed negotiation.
+ [`RSYNC_CHECKSUM_LIST`](#) to a space-separated list of acceptable checksum
+ names. If the string contains a "`&`" character, it is separated into the
+ "client string & server string", otherwise the same string applies to both.
+ If the string (or string portion) contains no non-whitespace characters,
+ the default checksum list is used. This method does not allow you to
+ specify the transfer checksum separately from the pre-transfer checksum,
+ and it discards "auto" and all unknown checksum names. A list with only
+ invalid names results in a failed negotiation.
The use of the `--checksum-choice` option overrides this environment list.
@@ -1573,45 +1829,45 @@ your home directory (remove the '=' for that).
encounters (using the attributes of the mounted directory because those of
the underlying mount-point directory are inaccessible).
- If rsync has been told to collapse symlinks (via `--copy-links` or
- `--copy-unsafe-links`), a symlink to a directory on another device is
- treated like a mount-point. Symlinks to non-directories are unaffected by
- this option.
+ If rsync has been told to collapse symlinks (via [`--copy-links`](#opt) or
+ [`--copy-unsafe-links`](#opt)), a symlink to a directory on another device
+ is treated like a mount-point. Symlinks to non-directories are unaffected
+ by this option.
-0. `--existing`, `--ignore-non-existing`
+0. `--ignore-non-existing`, `--existing`
This tells rsync to skip creating files (including directories) that do not
exist yet on the destination. If this option is combined with the
- `--ignore-existing` option, no files will be updated (which can be useful
- if all you want to do is delete extraneous files).
+ [`--ignore-existing`](#opt) option, no files will be updated (which can be
+ useful if all you want to do is delete extraneous files).
- This option is a transfer rule, not an exclude, so it doesn't affect the
- data that goes into the file-lists, and thus it doesn't affect deletions.
- It just limits the files that the receiver requests to be transferred.
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
0. `--ignore-existing`
This tells rsync to skip updating files that already exist on the
destination (this does _not_ ignore existing directories, or nothing would
- get done). See also `--existing`.
-
- This option is a transfer rule, not an exclude, so it doesn't affect the
- data that goes into the file-lists, and thus it doesn't affect deletions.
- It just limits the files that the receiver requests to be transferred.
-
- This option can be useful for those doing backups using the `--link-dest`
- option when they need to continue a backup run that got interrupted. Since
- a `--link-dest` run is copied into a new directory hierarchy (when it is
- used properly), using `--ignore-existing` will ensure that the
- already-handled files don't get tweaked (which avoids a change in
- permissions on the hard-linked files). This does mean that this option is
- only looking at the existing files in the destination hierarchy itself.
-
- When `--info=skip2` is used rsync will output "FILENAME exists (INFO)"
- messages where the INFO indicates one of "type change", "sum change"
- (requires `-c`), "file change" (based on the quick check), "attr change",
- or "uptodate". Using `--info=skip1` (which is also implied by `-vv`)
- outputs the exists message without the INFO suffix.
+ get done). See also [`--ignore-non-existing`](#opt).
+
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+ This option can be useful for those doing backups using the
+ [`--link-dest`](#opt) option when they need to continue a backup run that
+ got interrupted. Since a [`--link-dest`](#opt) run is copied into a new
+ directory hierarchy (when it is used properly), using [`--ignore-existing`
+ will ensure that the already-handled files don't get tweaked (which avoids
+ a change in permissions on the hard-linked files). This does mean that
+ this option is only looking at the existing files in the destination
+ hierarchy itself.
+
+ When [`--info=skip2`](#opt) is used rsync will output "FILENAME exists
+ (INFO)" messages where the INFO indicates one of "type change", "sum
+ change" (requires [`-c`](#opt)), "file change" (based on the quick check),
+ "attr change", or "uptodate". Using [`--info=skip1`](#opt) (which is also
+ implied by 2 [`-v`](#opt) options) outputs the exists message without the
+ INFO suffix.
0. `--remove-source-files`
@@ -1627,12 +1883,16 @@ your home directory (remove the '=' for that).
If you can't first write the files into a different directory, you should
use a naming idiom that lets rsync avoid transferring files that are not
yet finished (e.g. name the file "foo.new" when it is written, rename it to
- "foo" when it is done, and then use the option `--exclude='*.new'` for the
- rsync transfer).
+ "foo" when it is done, and then use the option [`--exclude='*.new'`](#opt)
+ for the rsync transfer).
Starting with 3.1.0, rsync will skip the sender-side removal (and output an
error) if the file's size or modify time has not stayed unchanged.
+ Starting with 3.2.6, a local rsync copy will ensure that the sender does
+ not remove a file the receiver just verified, such as when the user
+ accidentally makes the source and destination directory the same path.
+
0. `--delete`
This tells rsync to delete extraneous files from the receiving side (ones
@@ -1642,70 +1902,71 @@ your home directory (remove the '=' for that).
contents (e.g. "`dir/*`") since the wildcard is expanded by the shell and
rsync thus gets a request to transfer individual files, not the files'
parent directory. Files that are excluded from the transfer are also
- excluded from being deleted unless you use the `--delete-excluded` option
- or mark the rules as only matching on the sending side (see the
- include/exclude modifiers in the FILTER RULES section).
+ excluded from being deleted unless you use the [`--delete-excluded`](#opt)
+ option or mark the rules as only matching on the sending side (see the
+ include/exclude modifiers in the [FILTER RULES](#) section).
- Prior to rsync 2.6.7, this option would have no effect unless `--recursive`
- was enabled. Beginning with 2.6.7, deletions will also occur when `--dirs`
- (`-d`) is enabled, but only for directories whose contents are being
- copied.
+ Prior to rsync 2.6.7, this option would have no effect unless
+ [`--recursive`](#opt) was enabled. Beginning with 2.6.7, deletions will
+ also occur when [`--dirs`](#opt) (`-d`) is enabled, but only for
+ directories whose contents are being copied.
This option can be dangerous if used incorrectly! It is a very good idea to
- first try a run using the `--dry-run` option (`-n`) to see what files are
- going to be deleted.
+ first try a run using the [`--dry-run`](#opt) (`-n`) option to see what
+ files are going to be deleted.
If the sending side detects any I/O errors, then the deletion of any files
at the destination will be automatically disabled. This is to prevent
temporary filesystem failures (such as NFS errors) on the sending side from
causing a massive deletion of files on the destination. You can override
- this with the `--ignore-errors` option.
+ this with the [`--ignore-errors`](#opt) option.
The `--delete` option may be combined with one of the --delete-WHEN options
- without conflict, as well as `--delete-excluded`. However, if none of the
- `--delete-WHEN` options are specified, rsync will choose the
- `--delete-during` algorithm when talking to rsync 3.0.0 or newer, and the
- `--delete-before` algorithm when talking to an older rsync. See also
- `--delete-delay` and `--delete-after`.
+ without conflict, as well as [`--delete-excluded`](#opt). However, if none
+ of the `--delete-WHEN` options are specified, rsync will choose the
+ [`--delete-during`](#opt) algorithm when talking to rsync 3.0.0 or newer,
+ or the [`--delete-before`](#opt) algorithm when talking to an older rsync.
+ See also [`--delete-delay`](#opt) and [`--delete-after`](#opt).
0. `--delete-before`
Request that the file-deletions on the receiving side be done before the
- transfer starts. See `--delete` (which is implied) for more details on
- file-deletion.
+ transfer starts. See [`--delete`](#opt) (which is implied) for more
+ details on file-deletion.
Deleting before the transfer is helpful if the filesystem is tight for
space and removing extraneous files would help to make the transfer
possible. However, it does introduce a delay before the start of the
transfer, and this delay might cause the transfer to timeout (if
- `--timeout` was specified). It also forces rsync to use the old,
+ [`--timeout`](#opt) was specified). It also forces rsync to use the old,
non-incremental recursion algorithm that requires rsync to scan all the
- files in the transfer into memory at once (see `--recursive`).
+ files in the transfer into memory at once (see [`--recursive`](#opt)).
0. `--delete-during`, `--del`
Request that the file-deletions on the receiving side be done incrementally
as the transfer happens. The per-directory delete scan is done right
before each directory is checked for updates, so it behaves like a more
- efficient `--delete-before`, including doing the deletions prior to any
- per-directory filter files being updated. This option was first added in
- rsync version 2.6.4. See `--delete` (which is implied) for more details on
- file-deletion.
+ efficient [`--delete-before`](#opt), including doing the deletions prior to
+ any per-directory filter files being updated. This option was first added
+ in rsync version 2.6.4. See [`--delete`](#opt) (which is implied) for more
+ details on file-deletion.
0. `--delete-delay`
Request that the file-deletions on the receiving side be computed during
- the transfer (like `--delete-during`), and then removed after the transfer
- completes. This is useful when combined with `--delay-updates` and/or
- `--fuzzy`, and is more efficient than using `--delete-after` (but can
- behave differently, since `--delete-after` computes the deletions in a
- separate pass after all updates are done). If the number of removed files
- overflows an internal buffer, a temporary file will be created on the
- receiving side to hold the names (it is removed while open, so you
- shouldn't see it during the transfer). If the creation of the temporary
- file fails, rsync will try to fall back to using `--delete-after` (which it
- cannot do if `--recursive` is doing an incremental scan). See `--delete`
- (which is implied) for more details on file-deletion.
+ the transfer (like [`--delete-during`](#opt)), and then removed after the
+ transfer completes. This is useful when combined with
+ [`--delay-updates`](#opt) and/or [`--fuzzy`](#opt), and is more efficient
+ than using [`--delete-after`](#opt) (but can behave differently, since
+ [`--delete-after`](#opt) computes the deletions in a separate pass after
+ all updates are done). If the number of removed files overflows an
+ internal buffer, a temporary file will be created on the receiving side to
+ hold the names (it is removed while open, so you shouldn't see it during
+ the transfer). If the creation of the temporary file fails, rsync will try
+ to fall back to using [`--delete-after`](#opt) (which it cannot do if
+ [`--recursive`](#opt) is doing an incremental scan). See
+ [`--delete`](#opt) (which is implied) for more details on file-deletion.
0. `--delete-after`
@@ -1715,53 +1976,70 @@ your home directory (remove the '=' for that).
exclusions to take effect for the delete phase of the current transfer. It
also forces rsync to use the old, non-incremental recursion algorithm that
requires rsync to scan all the files in the transfer into memory at once
- (see `--recursive`). See `--delete` (which is implied) for more details on
- file-deletion.
+ (see [`--recursive`](#opt)). See [`--delete`](#opt) (which is implied) for
+ more details on file-deletion.
+
+ See also the [`--delete-delay`](#opt) option that might be a faster choice
+ for those that just want the deletions to occur at the end of the transfer.
0. `--delete-excluded`
- In addition to deleting the files on the receiving side that are not on the
- sending side, this tells rsync to also delete any files on the receiving
- side that are excluded (see `--exclude`). See the FILTER RULES section for
- a way to make individual exclusions behave this way on the receiver, and
- for a way to protect files from `--delete-excluded`. See `--delete` (which
- is implied) for more details on file-deletion.
+ This option turns any unqualified exclude/include rules into server-side
+ rules that do not affect the receiver's deletions.
+
+ By default, an exclude or include has both a server-side effect (to "hide"
+ and "show" files when building the server's file list) and a receiver-side
+ effect (to "protect" and "risk" files when deletions are occurring). Any
+ rule that has no modifier to specify what sides it is executed on will be
+ instead treated as if it were a server-side rule only, avoiding any
+ "protect" effects of the rules.
+
+ A rule can still apply to both sides even with this option specified if the
+ rule is given both the sender & receiver modifier letters (e.g., `-f'-sr
+ foo'`). Receiver-side protect/risk rules can also be explicitly specified
+ to limit the deletions. This saves you from having to edit a bunch of
+ `-f'- foo'` rules into `-f'-s foo'` (aka `-f'H foo'`) rules (not to mention
+ the corresponding includes).
+
+ See the [FILTER RULES](#) section for more information. See
+ [`--delete`](#opt) (which is implied) for more details on deletion.
0. `--ignore-missing-args`
When rsync is first processing the explicitly requested source files (e.g.
- command-line arguments or `--files-from` entries), it is normally an error
- if the file cannot be found. This option suppresses that error, and does
- not try to transfer the file. This does not affect subsequent
+ command-line arguments or [`--files-from`](#opt) entries), it is normally
+ an error if the file cannot be found. This option suppresses that error,
+ and does not try to transfer the file. This does not affect subsequent
vanished-file errors if a file was initially found to be present and later
is no longer there.
0. `--delete-missing-args`
- This option takes the behavior of (the implied) `--ignore-missing-args`
- option a step farther: each missing arg will become a deletion request of
- the corresponding destination file on the receiving side (should it exist).
- If the destination file is a non-empty directory, it will only be
- successfully deleted if `--force` or `--delete` are in effect. Other than
- that, this option is independent of any other type of delete processing.
+ This option takes the behavior of the (implied)
+ [`--ignore-missing-args`](#opt) option a step farther: each missing arg
+ will become a deletion request of the corresponding destination file on the
+ receiving side (should it exist). If the destination file is a non-empty
+ directory, it will only be successfully deleted if [`--force`](#opt) or
+ [`--delete`](#opt) are in effect. Other than that, this option is
+ independent of any other type of delete processing.
The missing source files are represented by special file-list entries which
- display as a "`*missing`" entry in the `--list-only` output.
+ display as a "`*missing`" entry in the [`--list-only`](#opt) output.
0. `--ignore-errors`
- Tells `--delete` to go ahead and delete files even when there are I/O
- errors.
+ Tells [`--delete`](#opt) to go ahead and delete files even when there are
+ I/O errors.
0. `--force`
This option tells rsync to delete a non-empty directory when it is to be
replaced by a non-directory. This is only relevant if deletions are not
- active (see `--delete` for details).
+ active (see [`--delete`](#opt) for details).
Note for older rsync versions: `--force` used to still be required when
- using `--delete-after`, and it used to be non-functional unless the
- `--recursive` option was also enabled.
+ using [`--delete-after`](#opt), and it used to be non-functional unless the
+ [`--recursive`](#opt) option was also enabled.
0. `--max-delete=NUM`
@@ -1785,9 +2063,8 @@ your home directory (remove the '=' for that).
the numeric units or left unqualified to specify bytes. Feel free to use a
fractional value along with the units, such as `--max-size=1.5m`.
- This option is a transfer rule, not an exclude, so it doesn't affect the
- data that goes into the file-lists, and thus it doesn't affect deletions.
- It just limits the files that the receiver requests to be transferred.
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
The first letter of a units string can be `B` (bytes), `K` (kilo), `M`
(mega), `G` (giga), `T` (tera), or `P` (peta). If the string is a single
@@ -1809,7 +2086,7 @@ your home directory (remove the '=' for that).
This tells rsync to avoid transferring any file that is smaller than the
specified SIZE, which can help in not transferring small, junk files. See
- the `--max-size` option for a description of SIZE and other information.
+ the [`--max-size`](#opt) option for a description of SIZE and other info.
Note that rsync versions prior to 3.1.0 did not allow `--min-size=0`.
@@ -1826,16 +2103,18 @@ your home directory (remove the '=' for that).
Keep in mind that this is not a limit on the total size of allocated
memory. It is a sanity-check value for each individual allocation.
- See the `--max-size` option for a description of how SIZE can be specified.
- The default suffix if none is given is bytes.
+ See the [`--max-size`](#opt) option for a description of how SIZE can be
+ specified. The default suffix if none is given is bytes.
- Beginning in 3.2.3, a value of 0 specifies no limit.
+ Beginning in 3.2.7, a value of 0 is an easy way to specify SIZE_MAX (the
+ largest limit possible).
- You can set a default value using the environment variable RSYNC_MAX_ALLOC
- using the same SIZE values as supported by this option. If the remote
- rsync doesn't understand the `--max-alloc` option, you can override an
- environmental value by specifying `--max-alloc=1g`, which will make rsync
- avoid sending the option to the remote side (because "1G" is the default).
+ You can set a default value using the environment variable
+ [`RSYNC_MAX_ALLOC`](#) using the same SIZE values as supported by this
+ option. If the remote rsync doesn't understand the `--max-alloc` option,
+ you can override an environmental value by specifying `--max-alloc=1g`,
+ which will make rsync avoid sending the option to the remote side (because
+ "1G" is the default).
0. `--block-size=SIZE`, `-B`
@@ -1844,7 +2123,7 @@ your home directory (remove the '=' for that).
updated. See the technical report for details.
Beginning in 3.2.3 the SIZE can be specified with a suffix as detailed in
- the `--max-size` option. Older versions only accepted a byte count.
+ the [`--max-size`](#opt) option. Older versions only accepted a byte count.
0. `--rsh=COMMAND`, `-e`
@@ -1857,17 +2136,17 @@ your home directory (remove the '=' for that).
shell _COMMAND_ will be used to run an rsync daemon on the remote host, and
all data will be transmitted through that remote shell connection, rather
than through a direct socket connection to a running rsync daemon on the
- remote host. See the section "USING RSYNC-DAEMON FEATURES VIA A
- REMOTE-SHELL CONNECTION" above.
-
- Beginning with rsync 3.2.0, the RSYNC_PORT environment variable will be set
- when a daemon connection is being made via a remote-shell connection. It
- is set to 0 if the default daemon port is being assumed, or it is set to
- the value of the rsync port that was specified via either the `--port`
- option or a non-empty port value in an rsync:// URL. This allows the
- script to discern if a non-default port is being requested, allowing for
- things such as an SSL or stunnel helper script to connect to a default or
- alternate port.
+ remote host. See the [USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL
+ CONNECTION](#) section above.
+
+ Beginning with rsync 3.2.0, the [`RSYNC_PORT`](#) environment variable will
+ be set when a daemon connection is being made via a remote-shell
+ connection. It is set to 0 if the default daemon port is being assumed, or
+ it is set to the value of the rsync port that was specified via either the
+ [`--port`](#opt) option or a non-empty port value in an `rsync://` URL.
+ This allows the script to discern if a non-default port is being requested,
+ allowing for things such as an SSL or stunnel helper script to connect to a
+ default or alternate port.
Command-line arguments are permitted in COMMAND provided that COMMAND is
presented to rsync as a single argument. You must use spaces (not tabs or
@@ -1884,10 +2163,11 @@ your home directory (remove the '=' for that).
(Note that ssh users can alternately customize site-specific connect
options in their .ssh/config file.)
- You can also choose the remote shell program using the RSYNC_RSH
+ You can also choose the remote shell program using the [`RSYNC_RSH`](#)
environment variable, which accepts the same range of values as `-e`.
- See also the `--blocking-io` option which is affected by this option.
+ See also the [`--blocking-io`](#opt) option which is affected by this
+ option.
0. `--rsync-path=PROGRAM`
@@ -1899,7 +2179,7 @@ your home directory (remove the '=' for that).
& standard-out that rsync is using to communicate.
One tricky example is to set a different default directory on the remote
- machine for use with the `--relative` option. For instance:
+ machine for use with the [`--relative`](#opt) option. For instance:
> rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/
@@ -1907,8 +2187,8 @@ your home directory (remove the '=' for that).
This option is used for more advanced situations where you want certain
effects to be limited to one side of the transfer only. For instance, if
- you want to pass `--log-file=FILE` and `--fake-super` to the remote system,
- specify it like this:
+ you want to pass [`--log-file=FILE`](#opt) and [`--fake-super`](#opt) to
+ the remote system, specify it like this:
> rsync -av -M --log-file=foo -M--fake-super src/ dest/
@@ -1922,11 +2202,10 @@ your home directory (remove the '=' for that).
cause rsync to have a different idea about what data to expect next over
the socket, and that will make it fail in a cryptic fashion.
- Note that it is best to use a separate `--remote-option` for each option
- you want to pass. This makes your usage compatible with the
- `--protect-args` option. If that option is off, any spaces in your remote
- options will be split by the remote shell unless you take steps to protect
- them.
+ Note that you should use a separate `-M` option for each remote option you
+ want to pass. On older rsync versions, the presence of any spaces in the
+ remote-option arg could cause it to be split into separate remote args, but
+ this requires the use of [`--old-args`](#opt) in a modern rsync.
When performing a local transfer, the "local" side is the sender and the
"remote" side is the receiver.
@@ -1944,7 +2223,8 @@ your home directory (remove the '=' for that).
to CVS to determine if a file should be ignored.
The exclude list is initialized to exclude the following items (these
- initial items are marked as perishable -- see the FILTER RULES section):
+ initial items are marked as perishable -- see the [FILTER RULES](#)
+ section):
[comment]: # (This list gets used for the default-cvsignore.h file.)
@@ -1994,17 +2274,17 @@ your home directory (remove the '=' for that).
filter/exclude files, these patterns are split on whitespace. See the
**cvs**(1) manual for more information.
- If you're combining `-C` with your own `--filter` rules, you should note
- that these CVS excludes are appended at the end of your own rules,
+ If you're combining `-C` with your own [`--filter`](#opt) rules, you should
+ note that these CVS excludes are appended at the end of your own rules,
regardless of where the `-C` was placed on the command-line. This makes
them a lower priority than any rules you specified explicitly. If you want
to control where these CVS excludes get inserted into your filter rules,
you should omit the `-C` as a command-line option and use a combination of
- `--filter=:C` and `--filter=-C` (either on your command-line or by putting
- the ":C" and "-C" rules into a filter file with your other rules). The
- first option turns on the per-directory scanning for the .cvsignore file.
- The second option does a one-time import of the CVS excludes mentioned
- above.
+ [`--filter=:C`](#opt) and [`--filter=-C`](#opt) (either on your
+ command-line or by putting the ":C" and "-C" rules into a filter file with
+ your other rules). The first option turns on the per-directory scanning
+ for the .cvsignore file. The second option does a one-time import of the
+ CVS excludes mentioned above.
0. `--filter=RULE`, `-f`
@@ -2018,12 +2298,12 @@ your home directory (remove the '=' for that).
argument. The text below also mentions that you can use an underscore to
replace the space that separates a rule from its arg.
- See the FILTER RULES section for detailed information on this option.
+ See the [FILTER RULES](#) section for detailed information on this option.
0. `-F`
- The `-F` option is a shorthand for adding two `--filter` rules to your
- command. The first time it is used is a shorthand for this rule:
+ The `-F` option is a shorthand for adding two [`--filter`](#opt) rules to
+ your command. The first time it is used is a shorthand for this rule:
> --filter='dir-merge /.rsync-filter'
@@ -2036,41 +2316,55 @@ your home directory (remove the '=' for that).
This filters out the .rsync-filter files themselves from the transfer.
- See the FILTER RULES section for detailed information on how these options
- work.
+ See the [FILTER RULES](#) section for detailed information on how these
+ options work.
0. `--exclude=PATTERN`
- This option is a simplified form of the `--filter` option that defaults to
- an exclude rule and does not allow the full rule-parsing syntax of normal
- filter rules.
+ This option is a simplified form of the [`--filter`](#opt) option that
+ specifies an exclude rule and does not allow the full rule-parsing syntax
+ of normal filter rules. This is equivalent to specifying `-f'- PATTERN'`.
- See the FILTER RULES section for detailed information on this option.
+ See the [FILTER RULES](#) section for detailed information on this option.
0. `--exclude-from=FILE`
- This option is related to the `--exclude` option, but it specifies a FILE
- that contains exclude patterns (one per line). Blank lines in the file are
- ignored, as are whole-line comments that start with '`;`' or '`#`'
+ This option is related to the [`--exclude`](#opt) option, but it specifies
+ a FILE that contains exclude patterns (one per line). Blank lines in the
+ file are ignored, as are whole-line comments that start with '`;`' or '`#`'
(filename rules that contain those characters are unaffected).
+ If a line begins with "`-Â `" (dash, space) or "`+Â `" (plus, space), then
+ the type of rule is being explicitly specified as an exclude or an include
+ (respectively). Any rules without such a prefix are taken to be an exclude.
+
+ If a line consists of just "`!`", then the current filter rules are cleared
+ before adding any further rules.
+
If _FILE_ is '`-`', the list will be read from standard input.
0. `--include=PATTERN`
- This option is a simplified form of the `--filter` option that defaults to
- an include rule and does not allow the full rule-parsing syntax of normal
- filter rules.
+ This option is a simplified form of the [`--filter`](#opt) option that
+ specifies an include rule and does not allow the full rule-parsing syntax
+ of normal filter rules. This is equivalent to specifying `-f'+ PATTERN'`.
- See the FILTER RULES section for detailed information on this option.
+ See the [FILTER RULES](#) section for detailed information on this option.
0. `--include-from=FILE`
- This option is related to the `--include` option, but it specifies a FILE
- that contains include patterns (one per line). Blank lines in the file are
- ignored, as are whole-line comments that start with '`;`' or '`#`'
+ This option is related to the [`--include`](#opt) option, but it specifies
+ a FILE that contains include patterns (one per line). Blank lines in the
+ file are ignored, as are whole-line comments that start with '`;`' or '`#`'
(filename rules that contain those characters are unaffected).
+ If a line begins with "`-Â `" (dash, space) or "`+Â `" (plus, space), then
+ the type of rule is being explicitly specified as an exclude or an include
+ (respectively). Any rules without such a prefix are taken to be an include.
+
+ If a line consists of just "`!`", then the current filter rules are cleared
+ before adding any further rules.
+
If _FILE_ is '`-`', the list will be read from standard input.
0. `--files-from=FILE`
@@ -2080,17 +2374,17 @@ your home directory (remove the '=' for that).
tweaks the default behavior of rsync to make transferring just the
specified files and directories easier:
- - The `--relative` (`-R`) option is implied, which preserves the path
- information that is specified for each item in the file (use
+ - The [`--relative`](#opt) (`-R`) option is implied, which preserves the
+ path information that is specified for each item in the file (use
`--no-relative` or `--no-R` if you want to turn that off).
- - The `--dirs` (`-d`) option is implied, which will create directories
- specified in the list on the destination rather than noisily skipping
- them (use `--no-dirs` or `--no-d` if you want to turn that off).
- - The `--archive` (`-a`) option's behavior does not imply `--recursive`
- (`-r`), so specify it explicitly, if you want it.
+ - The [`--dirs`](#opt) (`-d`) option is implied, which will create
+ directories specified in the list on the destination rather than noisily
+ skipping them (use `--no-dirs` or `--no-d` if you want to turn that off).
+ - The [`--archive`](#opt) (`-a`) option's behavior does not imply
+ [`--recursive`](#opt) (`-r`), so specify it explicitly, if you want it.
- These side-effects change the default state of rsync, so the position of
the `--files-from` option on the command-line has no bearing on how other
- options are parsed (e.g. `-a` works the same before or after
+ options are parsed (e.g. [`-a`](#opt) works the same before or after
`--files-from`, as does `--no-R` and all other options).
The filenames that are read from the FILE are all relative to the source
@@ -2103,13 +2397,13 @@ your home directory (remove the '=' for that).
directory will be created as /backup/bin on the remote host. If it
contains "bin/" (note the trailing slash), the immediate contents of the
directory would also be sent (without needing to be explicitly mentioned in
- the file -- this began in version 2.6.4). In both cases, if the `-r`
- option was enabled, that dir's entire hierarchy would also be transferred
- (keep in mind that `-r` needs to be specified explicitly with
- `--files-from`, since it is not implied by `-a`). Also note that the
- effect of the (enabled by default) `--relative` option is to duplicate only
- the path info that is read from the file -- it does not force the
- duplication of the source-spec path (/usr in this case).
+ the file -- this began in version 2.6.4). In both cases, if the
+ [`-r`](#opt) option was enabled, that dir's entire hierarchy would also be
+ transferred (keep in mind that [`-r`](#opt) needs to be specified
+ explicitly with `--files-from`, since it is not implied by [`-a`](#opt).
+ Also note that the effect of the (enabled by default) [`-r`](#opt) option
+ is to duplicate only the path info that is read from the file -- it does
+ not force the duplication of the source-spec path (/usr in this case).
In addition, the `--files-from` file can be read from the remote host
instead of the local host if you specify a "host:" in front of the file
@@ -2122,9 +2416,9 @@ your home directory (remove the '=' for that).
This would copy all the files specified in the /path/file-list file that
was located on the remote "src" host.
- If the `--iconv` and `--protect-args` options are specified and the
- `--files-from` filenames are being sent from one host to another, the
- filenames will be translated from the sending host's charset to the
+ If the [`--iconv`](#opt) and [`--secluded-args`](#opt) options are specified
+ and the `--files-from` filenames are being sent from one host to another,
+ the filenames will be translated from the sending host's charset to the
receiving host's charset.
NOTE: sorting the list of files in the `--files-from` input helps rsync to
@@ -2138,40 +2432,117 @@ your home directory (remove the '=' for that).
This tells rsync that the rules/filenames it reads from a file are
terminated by a null ('\\0') character, not a NL, CR, or CR+LF. This
- affects `--exclude-from`, `--include-from`, `--files-from`, and any merged
- files specified in a `--filter` rule. It does not affect `--cvs-exclude`
- (since all names read from a .cvsignore file are split on whitespace).
+ affects [`--exclude-from`](#opt), [`--include-from`](#opt),
+ [`--files-from`](#opt), and any merged files specified in a
+ [`--filter`](#opt) rule. It does not affect [`--cvs-exclude`](#opt) (since
+ all names read from a .cvsignore file are split on whitespace).
+
+0. `--old-args`
+
+ This option tells rsync to stop trying to protect the arg values on the
+ remote side from unintended word-splitting or other misinterpretation.
+ It also allows the client to treat an empty arg as a "." instead of
+ generating an error.
+
+ The default in a modern rsync is for "shell-active" characters (including
+ spaces) to be backslash-escaped in the args that are sent to the remote
+ shell. The wildcard characters `*`, `?`, `[`, & `]` are not escaped in
+ filename args (allowing them to expand into multiple filenames) while being
+ protected in option args, such as [`--usermap`](#opt).
+
+ If you have a script that wants to use old-style arg splitting in its
+ filenames, specify this option once. If the remote shell has a problem
+ with any backslash escapes at all, specify this option twice.
+
+ You may also control this setting via the [`RSYNC_OLD_ARGS`](#) environment
+ variable. If it has the value "1", rsync will default to a single-option
+ setting. If it has the value "2" (or more), rsync will default to a
+ repeated-option setting. If it is "0", you'll get the default escaping
+ behavior. The environment is always overridden by manually specified
+ positive or negative options (the negative is `--no-old-args`).
+
+ Note that this option also disables the extra safety check added in 3.2.5
+ that ensures that a remote sender isn't including extra top-level items in
+ the file-list that you didn't request. This side-effect is necessary
+ because we can't know for sure what names to expect when the remote shell
+ is interpreting the args.
+
+ This option conflicts with the [`--secluded-args`](#opt) option.
+
+0. `--secluded-args`, `-s`
+
+ This option sends all filenames and most options to the remote rsync via
+ the protocol (not the remote shell command line) which avoids letting the
+ remote shell modify them. Wildcards are expanded on the remote host by
+ rsync instead of a shell.
+
+ This is similar to the default backslash-escaping of args that was added
+ in 3.2.4 (see [`--old-args`](#opt)) in that it prevents things like space
+ splitting and unwanted special-character side-effects. However, it has the
+ drawbacks of being incompatible with older rsync versions (prior to 3.0.0)
+ and of being refused by restricted shells that want to be able to inspect
+ all the option values for safety.
+
+ This option is useful for those times that you need the argument's
+ character set to be converted for the remote host, if the remote shell is
+ incompatible with the default backslash-escpaing method, or there is some
+ other reason that you want the majority of the options and arguments to
+ bypass the command-line of the remote shell.
+
+ If you combine this option with [`--iconv`](#opt), the args related to the
+ remote side will be translated from the local to the remote character-set.
+ The translation happens before wild-cards are expanded. See also the
+ [`--files-from`](#opt) option.
+
+ You may also control this setting via the [`RSYNC_PROTECT_ARGS`](#)
+ environment variable. If it has a non-zero value, this setting will be
+ enabled by default, otherwise it will be disabled by default. Either state
+ is overridden by a manually specified positive or negative version of this
+ option (note that `--no-s` and `--no-secluded-args` are the negative
+ versions). This environment variable is also superseded by a non-zero
+ [`RSYNC_OLD_ARGS`](#) export.
-0. `--protect-args`, `-s`
+ This option conflicts with the [`--old-args`](#opt) option.
- This option sends all filenames and most options to the remote rsync
- without allowing the remote shell to interpret them. This means that
- spaces are not split in names, and any non-wildcard special characters are
- not translated (such as `~`, `$`, `;`, `&`, etc.). Wildcards are expanded
- on the remote host by rsync (instead of the shell doing it).
+ This option used to be called `--protect-args` (before 3.2.6) and that
+ older name can still be used (though specifying it as `-s` is always the
+ easiest and most compatible choice).
- If you use this option with `--iconv`, the args related to the remote side
- will also be translated from the local to the remote character-set. The
- translation happens before wild-cards are expanded. See also the
- `--files-from` option.
+0. `--trust-sender`
- You may also control this option via the RSYNC_PROTECT_ARGS environment
- variable. If this variable has a non-zero value, this option will be
- enabled by default, otherwise it will be disabled by default. Either state
- is overridden by a manually specified positive or negative version of this
- option (note that `--no-s` and `--no-protect-args` are the negative
- versions). Since this option was first introduced in 3.0.0, you'll need to
- make sure it's disabled if you ever need to interact with a remote rsync
- that is older than that.
+ This option disables two extra validation checks that a local client
+ performs on the file list generated by a remote sender. This option should
+ only be used if you trust the sender to not put something malicious in the
+ file list (something that could possibly be done via a modified rsync, a
+ modified shell, or some other similar manipulation).
+
+ Normally, the rsync client (as of version 3.2.5) runs two extra validation
+ checks when pulling files from a remote rsync:
+
+ - It verifies that additional arg items didn't get added at the top of the
+ transfer.
+ - It verifies that none of the items in the file list are names that should
+ have been excluded (if filter rules were specified).
+
+ Note that various options can turn off one or both of these checks if the
+ option interferes with the validation. For instance:
+
+ - Using a per-directory filter file reads filter rules that only the server
+ knows about, so the filter checking is disabled.
+ - Using the [`--old-args`](#opt) option allows the sender to manipulate the
+ requested args, so the arg checking is disabled.
+ - Reading the files-from list from the server side means that the client
+ doesn't know the arg list, so the arg checking is disabled.
+ - Using [`--read-batch`](#opt) disables both checks since the batch file's
+ contents will have been verified when it was created.
- Rsync can also be configured (at build time) to have this option enabled by
- default (with is overridden by both the environment and the command-line).
- Run `rsync --version` to check if this is the case, as it will display
- "default protect-args" or "optional protect-args" depending on how it was
- compiled.
+ This option may help an under-powered client server if the extra pattern
+ matching is slowing things down on a huge transfer. It can also be used to
+ work around a currently-unknown bug in the verification logic for a transfer
+ from a trusted sender.
- This option will eventually become a new default setting at some
- as-yet-undetermined point in the future.
+ When using this option it is a good idea to specify a dedicated destination
+ directory, as discussed in the [MULTI-HOST SECURITY](#) section.
0. `--copy-as=USER[:GROUP]`
@@ -2189,14 +2560,14 @@ your home directory (remove the '=' for that).
operation after the remote-shell or daemon connection is established.
The option only affects one side of the transfer unless the transfer is
- local, in which case it affects both sides. Use the `--remote-option` to
- affect the remote side, such as `-M--copy-as=joe`. For a local transfer,
- the lsh (or lsh.sh) support file provides a local-shell helper script that
- can be used to allow a "localhost:" or "lh:" host-spec to be specified
- without needing to setup any remote shells, allowing you to specify remote
- options that affect the side of the transfer that is using the host-spec
- (and using hostname "lh" avoids the overriding of the remote directory to
- the user's home dir).
+ local, in which case it affects both sides. Use the
+ [`--remote-option`](#opt) to affect the remote side, such as
+ `-M--copy-as=joe`. For a local transfer, the lsh (or lsh.sh) support file
+ provides a local-shell helper script that can be used to allow a
+ "localhost:" or "lh:" host-spec to be specified without needing to setup
+ any remote shells, allowing you to specify remote options that affect the
+ side of the transfer that is using the host-spec (and using hostname "lh"
+ avoids the overriding of the remote directory to the user's home dir).
For example, the following rsync writes the local files as user "joe":
@@ -2236,17 +2607,17 @@ your home directory (remove the '=' for that).
new version on the disk at the same time.
If you are using this option for reasons other than a shortage of disk
- space, you may wish to combine it with the `--delay-updates` option, which
- will ensure that all copied files get put into subdirectories in the
- destination hierarchy, awaiting the end of the transfer. If you don't have
- enough room to duplicate all the arriving files on the destination
- partition, another way to tell rsync that you aren't overly concerned about
- disk space is to use the `--partial-dir` option with a relative path;
- because this tells rsync that it is OK to stash off a copy of a single file
- in a subdir in the destination hierarchy, rsync will use the partial-dir as
- a staging area to bring over the copied file, and then rename it into place
- from there. (Specifying a `--partial-dir` with an absolute path does not
- have this side-effect.)
+ space, you may wish to combine it with the [`--delay-updates`](#opt)
+ option, which will ensure that all copied files get put into subdirectories
+ in the destination hierarchy, awaiting the end of the transfer. If you
+ don't have enough room to duplicate all the arriving files on the
+ destination partition, another way to tell rsync that you aren't overly
+ concerned about disk space is to use the [`--partial-dir`](#opt) option
+ with a relative path; because this tells rsync that it is OK to stash off a
+ copy of a single file in a subdir in the destination hierarchy, rsync will
+ use the partial-dir as a staging area to bring over the copied file, and
+ then rename it into place from there. (Specifying a [`--partial-dir`](#opt)
+ with an absolute path does not have this side-effect.)
0. `--fuzzy`, `-y`
@@ -2257,12 +2628,12 @@ your home directory (remove the '=' for that).
the fuzzy basis file to try to speed up the transfer.
If the option is repeated, the fuzzy scan will also be done in any matching
- alternate destination directories that are specified via `--compare-dest`,
- `--copy-dest`, or `--link-dest`.
+ alternate destination directories that are specified via
+ [`--compare-dest`](#opt), [`--copy-dest`](#opt), or [`--link-dest`](#opt).
- Note that the use of the `--delete` option might get rid of any potential
- fuzzy-match files, so either use `--delete-after` or specify some filename
- exclusions if you need to prevent this.
+ Note that the use of the [`--delete`](#opt) option might get rid of any
+ potential fuzzy-match files, so either use [`--delete-after`](#opt) or
+ specify some filename exclusions if you need to prevent this.
0. `--compare-dest=DIR`
@@ -2283,7 +2654,7 @@ your home directory (remove the '=' for that).
transfer.
If _DIR_ is a relative path, it is relative to the destination directory.
- See also `--copy-dest` and `--link-dest`.
+ See also [`--copy-dest`](#opt) and [`--link-dest`](#opt).
NOTE: beginning with version 3.1.0, rsync will remove a file from a
non-empty destination hierarchy if an exact match is found in one of the
@@ -2292,7 +2663,7 @@ your home directory (remove the '=' for that).
0. `--copy-dest=DIR`
- This option behaves like `--compare-dest`, but rsync will also copy
+ This option behaves like [`--compare-dest`](#opt), but rsync will also copy
unchanged files found in _DIR_ to the destination directory using a local
copy. This is useful for doing transfers to a new destination while
leaving existing files intact, and then doing a flash-cutover when all
@@ -2304,14 +2675,14 @@ your home directory (remove the '=' for that).
try to speed up the transfer.
If _DIR_ is a relative path, it is relative to the destination directory.
- See also `--compare-dest` and `--link-dest`.
+ See also [`--compare-dest`](#opt) and [`--link-dest`](#opt).
0. `--link-dest=DIR`
- This option behaves like `--copy-dest`, but unchanged files are hard linked
- from _DIR_ to the destination directory. The files must be identical in
- all preserved attributes (e.g. permissions, possibly ownership) in order
- for the files to be linked together. An example:
+ This option behaves like [`--copy-dest`](#opt), but unchanged files are
+ hard linked from _DIR_ to the destination directory. The files must be
+ identical in all preserved attributes (e.g. permissions, possibly
+ ownership) in order for the files to be linked together. An example:
> rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/
@@ -2335,18 +2706,19 @@ your home directory (remove the '=' for that).
alternate-directory exact match would never be found (nor linked into the
destination) when a destination file already exists.
- Note that if you combine this option with `--ignore-times`, rsync will not
+ Note that if you combine this option with [`--ignore-times`](#opt), rsync will not
link any files together because it only links identical files together as a
substitute for transferring the file, never as an additional check after
the file is updated.
If _DIR_ is a relative path, it is relative to the destination directory.
- See also `--compare-dest` and `--copy-dest`.
+ See also [`--compare-dest`](#opt) and [`--copy-dest`](#opt).
Note that rsync versions prior to 2.6.1 had a bug that could prevent
- `--link-dest` from working properly for a non-super-user when `-o` was
- specified (or implied by `-a`). You can work-around this bug by avoiding
- the `-o` option when sending to an old rsync.
+ `--link-dest` from working properly for a non-super-user when
+ [`--owner`](#opt) (`-o`) was specified (or implied). You can work-around
+ this bug by avoiding the `-o` option (or using `--no-o`) when sending to an
+ old rsync.
0. `--compress`, `-z`
@@ -2355,7 +2727,8 @@ your home directory (remove the '=' for that).
something that is useful over a slow connection.
Rsync supports multiple compression methods and will choose one for you
- unless you force the choice using the `--compress-choice` (`--zc`) option.
+ unless you force the choice using the [`--compress-choice`](#opt) (`--zc`)
+ option.
Run `rsync --version` to see the default compress list compiled into your
version.
@@ -2367,10 +2740,10 @@ your home directory (remove the '=' for that).
its list is assumed to be "zlib".
The default order can be customized by setting the environment variable
- RSYNC_COMPRESS_LIST to a space-separated list of acceptable compression
- names. If the string contains a "`&`" character, it is separated into the
- "client string & server string", otherwise the same string applies to both.
- If the string (or string portion) contains no
+ [`RSYNC_COMPRESS_LIST`](#) to a space-separated list of acceptable
+ compression names. If the string contains a "`&`" character, it is
+ separated into the "client string & server string", otherwise the same
+ string applies to both. If the string (or string portion) contains no
non-whitespace characters, the default compress list is used. Any unknown
compression names are discarded from the list, but a list with only invalid
names results in a failed negotiation.
@@ -2381,15 +2754,12 @@ your home directory (remove the '=' for that).
ignore this weirdness unless the rsync server complains and tells you to
specify `-zz`.
- See also the `--skip-compress` option for the default list of file suffixes
- that will be transferred with no (or minimal) compression.
-
0. `--compress-choice=STR`, `--zc=STR`
This option can be used to override the automatic negotiation of the
- compression algorithm that occurs when `--compress` is used. The option
- implies `--compress` unless "none" was specified, which instead implies
- `--no-compress`.
+ compression algorithm that occurs when [`--compress`](#opt) is used. The
+ option implies [`--compress`](#opt) unless "none" was specified, which
+ instead implies `--no-compress`.
The compression options that you may be able to use are:
@@ -2414,22 +2784,23 @@ your home directory (remove the '=' for that).
0. `--compress-level=NUM`, `--zl=NUM`
- Explicitly set the compression level to use (see `--compress`, `-z`)
- instead of letting it default. The `--compress` option is implied as long
- as the level chosen is not a "don't compress" level for the compression
- algorithm that is in effect (e.g. zlib compression treats level 0 as
- "off").
+ Explicitly set the compression level to use (see [`--compress`](#opt),
+ `-z`) instead of letting it default. The [`--compress`](#opt) option is
+ implied as long as the level chosen is not a "don't compress" level for the
+ compression algorithm that is in effect (e.g. zlib compression treats level
+ 0 as "off").
The level values vary depending on the checksum in effect. Because rsync
will negotiate a checksum choice by default (when the remote rsync is new
- enough), it can be good to combine this option with a `--compress-choice`
- (`--zc`) option unless you're sure of the choice in effect. For example:
+ enough), it can be good to combine this option with a
+ [`--compress-choice`](#opt) (`--zc`) option unless you're sure of the
+ choice in effect. For example:
> rsync -aiv --zc=zstd --zl=22 host:src/ dest/
For zlib & zlibx compression the valid values are from 1 to 9 with 6 being
- the default. Specifying 0 turns compression off, and specifying -1 chooses
- the default of 6.
+ the default. Specifying `--zl=0` turns compression off, and specifying
+ `--zl=-1` chooses the default level of 6.
For zstd compression the valid values are from -131072 to 22 with 3 being
the default. Specifying 0 chooses the default of 3.
@@ -2442,20 +2813,21 @@ your home directory (remove the '=' for that).
compression level no matter what algorithm was chosen.
If you want to know the compression level that is in effect, specify
- `--debug=nstr` to see the "negotiated string" results. This will report
- something like "`Client compress: zstd (level 3)`" (along with the checksum
- choice in effect).
+ [`--debug=nstr`](#opt) to see the "negotiated string" results. This will
+ report something like "`Client compress: zstd (level 3)`" (along with the
+ checksum choice in effect).
0. `--skip-compress=LIST`
+ **NOTE:** no compression method currently supports per-file compression
+ changes, so this option has no effect.
+
Override the list of file suffixes that will be compressed as little as
possible. Rsync sets the compression level on a per-file basis based on
- the file's suffix. If the compression algorithm has an "off" level (such
- as zlib/zlibx) then no compression occurs for those files. Other
- algorithms that support changing the streaming level on-the-fly will have
- the level minimized to reduces the CPU usage as much as possible for a
- matching file. At this time, only zlib & zlibx compression support this
- changing of levels on a per-file basis.
+ the file's suffix. If the compression algorithm has an "off" level, then
+ no compression occurs for those files. Other algorithms that support
+ changing the streaming level on-the-fly will have the level minimized to
+ reduces the CPU usage as much as possible for a matching file.
The **LIST** should be one or more file suffixes (without the dot) separated
by slashes (`/`). You may specify an empty string to indicate that no files
@@ -2591,10 +2963,10 @@ your home directory (remove the '=' for that).
If a user or group has no name on the source system or it has no match on
the destination system, then the numeric ID from the source system is used
- instead. See also the comments on the "`use chroot`" setting in the
- rsyncd.conf manpage for information on how the chroot setting affects
- rsync's ability to look up the names of the users and groups and what you
- can do about it.
+ instead. See also the [`use chroot`](rsyncd.conf.5#use_chroot) setting
+ in the rsyncd.conf manpage for some comments on how the chroot setting
+ affects rsync's ability to look up the names of the users and groups and
+ what you can do about it.
0. `--usermap=STRING`, `--groupmap=STRING`
@@ -2627,36 +2999,39 @@ your home directory (remove the '=' for that).
> --usermap=:nobody --groupmap=*:nobody
- When the `--numeric-ids` option is used, the sender does not send any
+ When the [`--numeric-ids`](#opt) option is used, the sender does not send any
names, so all the IDs are treated as having an empty name. This means that
you will need to specify numeric **FROM** values if you want to map these
nameless IDs to different values.
- For the `--usermap` option to have any effect, the `-o` (`--owner`) option
- must be used (or implied), and the receiver will need to be running as a
- super-user (see also the `--fake-super` option). For the `--groupmap`
- option to have any effect, the `-g` (`--group`) option must be used (or
- implied), and the receiver will need to have permissions to set that group.
+ For the `--usermap` option to work, the receiver will need to be running as
+ a super-user (see also the [`--super`](#opt) and [`--fake-super`](#opt)
+ options). For the `--groupmap` option to work, the receiver will need to
+ have permissions to set that group.
- The `--usermap` option implies the `--owner` option while the `--groupmap`
- option implies the `--group` option.
+ Starting with rsync 3.2.4, the `--usermap` option implies the
+ [`--owner`](#opt) (`-o`) option while the `--groupmap` option implies the
+ [`--group`](#opt) (`-g`) option (since rsync needs to have those options
+ enabled for the mapping options to work).
- If your shell complains about the wildcards, use `--protect-args` (`-s`).
+ An older rsync client may need to use [`-s`](#opt) to avoid a complaint
+ about wildcard characters, but a modern rsync handles this automatically.
0. `--chown=USER:GROUP`
This option forces all files to be owned by USER with group GROUP. This is
- a simpler interface than using `--usermap` and `--groupmap` directly, but
- it is implemented using those options internally, so you cannot mix them.
- If either the USER or GROUP is empty, no mapping for the omitted user/group
- will occur. If GROUP is empty, the trailing colon may be omitted, but if
- USER is empty, a leading colon must be supplied.
+ a simpler interface than using [`--usermap`](#opt) & [`--groupmap`](#opt)
+ directly, but it is implemented using those options internally so they
+ cannot be mixed. If either the USER or GROUP is empty, no mapping for the
+ omitted user/group will occur. If GROUP is empty, the trailing colon may
+ be omitted, but if USER is empty, a leading colon must be supplied.
If you specify "`--chown=foo:bar`", this is exactly the same as specifying
- "`--usermap=*:foo --groupmap=*:bar`", only easier (with the same implied
- `--owner` and/or `--group` option).
+ "`--usermap=*:foo --groupmap=*:bar`", only easier (and with the same
+ implied [`--owner`](#opt) and/or [`--group`](#opt) options).
- If your shell complains about the wildcards, use `--protect-args` (`-s`).
+ An older rsync client may need to use [`-s`](#opt) to avoid a complaint
+ about wildcard characters, but a modern rsync handles this automatically.
0. `--timeout=SECONDS`
@@ -2674,27 +3049,29 @@ your home directory (remove the '=' for that).
By default rsync will bind to the wildcard address when connecting to an
rsync daemon. The `--address` option allows you to specify a specific IP
- address (or hostname) to bind to. See also this option in the `--daemon`
- mode section.
+ address (or hostname) to bind to.
+
+ See also [the daemon version of the `--address` option](#dopt--address).
0. `--port=PORT`
This specifies an alternate TCP port number to use rather than the default
of 873. This is only needed if you are using the double-colon (::) syntax
to connect with an rsync daemon (since the URL syntax has a way to specify
- the port as a part of the URL). See also this option in the `--daemon`
- mode section.
+ the port as a part of the URL).
+
+ See also [the daemon version of the `--port` option](#dopt--port).
0. `--sockopts=OPTIONS`
This option can provide endless fun for people who like to tune their
systems to the utmost degree. You can set all sorts of socket options
- which may make transfers faster (or slower!). Read the man page for the
+ which may make transfers faster (or slower!). Read the manpage for the
`setsockopt()` system call for details on some of the options you may be
able to set. By default no special socket options are set. This only
affects direct socket connections to a remote rsync daemon.
- This option also exists in the `--daemon` mode section.
+ See also [the daemon version of the `--sockopts` option](#dopt--sockopts).
0. `--blocking-io`
@@ -2716,10 +3093,10 @@ your home directory (remove the '=' for that).
Requests a simple itemized list of the changes that are being made to each
file, including attribute changes. This is exactly the same as specifying
- `--out-format='%i %n%L'`. If you repeat the option, unchanged files will
- also be output, but only if the receiving rsync is at least version 2.6.7
- (you can use `-vv` with older versions of rsync, but that also turns on the
- output of other verbose messages).
+ [`--out-format='%i %n%L'`](#opt). If you repeat the option, unchanged
+ files will also be output, but only if the receiving rsync is at least
+ version 2.6.7 (you can use `-vv` with older versions of rsync, but that
+ also turns on the output of other verbose messages).
The "%i" escape has a cryptic output that is 11 letters long. The general
format is like the string `YXcstpoguax`, where **Y** is replaced by the type
@@ -2734,7 +3111,7 @@ your home directory (remove the '=' for that).
- A `c` means that a local change/creation is occurring for the item (such
as the creation of a directory or the changing of a symlink, etc.).
- A `h` means that the item is a hard link to another item (requires
- `--hard-links`).
+ [`--hard-links`](#opt)).
- A `.` means that the item is not being updated (though it might have
attributes that are being modified).
- A `*` means that the rest of the itemized-output area contains a message
@@ -2756,30 +3133,32 @@ your home directory (remove the '=' for that).
The attribute that is associated with each letter is as follows:
- A `c` means either that a regular file has a different checksum (requires
- `--checksum`) or that a symlink, device, or special file has a changed
- value. Note that if you are sending files to an rsync prior to 3.0.1,
- this change flag will be present only for checksum-differing regular
- files.
+ [`--checksum`](#opt)) or that a symlink, device, or special file has a
+ changed value. Note that if you are sending files to an rsync prior to
+ 3.0.1, this change flag will be present only for checksum-differing
+ regular files.
- A `s` means the size of a regular file is different and will be updated
by the file transfer.
- A `t` means the modification time is different and is being updated to
- the sender's value (requires `--times`). An alternate value of `T` means
- that the modification time will be set to the transfer time, which
- happens when a file/symlink/device is updated without `--times` and when
- a symlink is changed and the receiver can't set its time. (Note: when
- using an rsync 3.0.0 client, you might see the `s` flag combined with `t`
- instead of the proper `T` flag for this time-setting failure.)
+ the sender's value (requires [`--times`](#opt)). An alternate value of
+ `T` means that the modification time will be set to the transfer time,
+ which happens when a file/symlink/device is updated without
+ [`--times`](#opt) and when a symlink is changed and the receiver can't
+ set its time. (Note: when using an rsync 3.0.0 client, you might see the
+ `s` flag combined with `t` instead of the proper `T` flag for this
+ time-setting failure.)
- A `p` means the permissions are different and are being updated to the
- sender's value (requires `--perms`).
+ sender's value (requires [`--perms`](#opt)).
- An `o` means the owner is different and is being updated to the sender's
- value (requires `--owner` and super-user privileges).
+ value (requires [`--owner`](#opt) and super-user privileges).
- A `g` means the group is different and is being updated to the sender's
- value (requires `--group` and the authority to set the group).
- - A `u`|`n`|`b` indicates the following information: `u` means the access
- (use) time is different and is being updated to the sender's value
- (requires `--atimes`); `n` means the create time (newness) is different
- and is being updated to the sender's value (requires `--crtimes`); `b`
- means that both the access and create times are being updated.
+ value (requires [`--group`](#opt) and the authority to set the group).
+ - A `u`|`n`|`b` indicates the following information:
+ - `u` means the access (use) time is different and is being updated to
+ the sender's value (requires [`--atimes`](#opt))
+ - `n` means the create time (newness) is different and is being updated
+ to the sender's value (requires [`--crtimes`](#opt))
+ - `b` means that both the access and create times are being updated
- The `a` means that the ACL information is being changed.
- The `x` means that the extended attribute information is being changed.
@@ -2793,26 +3172,27 @@ your home directory (remove the '=' for that).
This allows you to specify exactly what the rsync client outputs to the
user on a per-update basis. The format is a text string containing
embedded single-character escape sequences prefixed with a percent (%)
- character. A default format of "%n%L" is assumed if either `--info=name`
- or `-v` is specified (this tells you just the name of the file and, if the
- item is a link, where it points). For a full list of the possible escape
- characters, see the "`log format`" setting in the rsyncd.conf manpage.
-
- Specifying the `--out-format` option implies the `--info=name` option,
- which will mention each file, dir, etc. that gets updated in a significant
- way (a transferred file, a recreated symlink/device, or a touched
- directory). In addition, if the itemize-changes escape (%i) is included in
- the string (e.g. if the `--itemize-changes` option was used), the logging
- of names increases to mention any item that is changed in any way (as long
- as the receiving side is at least 2.6.4). See the `--itemize-changes`
- option for a description of the output of "%i".
+ character. A default format of "%n%L" is assumed if either
+ [`--info=name`](#opt) or [`-v`](#opt) is specified (this tells you just the
+ name of the file and, if the item is a link, where it points). For a full
+ list of the possible escape characters, see the [`log
+ format`](rsyncd.conf.5#log_format) setting in the rsyncd.conf manpage.
+
+ Specifying the `--out-format` option implies the [`--info=name`](#opt)
+ option, which will mention each file, dir, etc. that gets updated in a
+ significant way (a transferred file, a recreated symlink/device, or a
+ touched directory). In addition, if the itemize-changes escape (%i) is
+ included in the string (e.g. if the [`--itemize-changes`](#opt) option was
+ used), the logging of names increases to mention any item that is changed
+ in any way (as long as the receiving side is at least 2.6.4). See the
+ [`--itemize-changes`](#opt) option for a description of the output of "%i".
Rsync will output the out-format string prior to a file's transfer unless
one of the transfer-statistic escapes is requested, in which case the
logging is done at the end of the file's transfer. When this late logging
- is in effect and `--progress` is also specified, rsync will also output the
- name of the file being transferred prior to its progress information
- (followed, of course, by the out-format output).
+ is in effect and [`--progress`](#opt) is also specified, rsync will also
+ output the name of the file being transferred prior to its progress
+ information (followed, of course, by the out-format output).
0. `--log-file=FILE`
@@ -2820,10 +3200,10 @@ your home directory (remove the '=' for that).
similar to the logging that a daemon does, but can be requested for the
client side and/or the server side of a non-daemon transfer. If specified
as a client option, transfer logging will be enabled with a default format
- of "%i %n%L". See the `--log-file-format` option if you wish to override
- this.
+ of "%i %n%L". See the [`--log-file-format`](#opt) option if you wish to
+ override this.
- Here's a example command that requests the remote side to log what is
+ Here's an example command that requests the remote side to log what is
happening:
> rsync -av --remote-option=--log-file=/tmp/rlog src/ dest/
@@ -2831,24 +3211,30 @@ your home directory (remove the '=' for that).
This is very useful if you need to debug why a connection is closing
unexpectedly.
+ See also [the daemon version of the `--log-file` option](#dopt--log-file).
+
0. `--log-file-format=FORMAT`
This allows you to specify exactly what per-update logging is put into the
- file specified by the `--log-file` option (which must also be specified for
- this option to have any effect). If you specify an empty string, updated
- files will not be mentioned in the log file. For a list of the possible
- escape characters, see the "`log format`" setting in the rsyncd.conf manpage.
+ file specified by the [`--log-file`](#opt) option (which must also be
+ specified for this option to have any effect). If you specify an empty
+ string, updated files will not be mentioned in the log file. For a list of
+ the possible escape characters, see the [`log format`](rsyncd.conf.5#log_format)
+ setting in the rsyncd.conf manpage.
- The default FORMAT used if `--log-file` is specified and this option is not
- is '%i %n%L'.
+ The default FORMAT used if [`--log-file`](#opt) is specified and this
+ option is not is '%i %n%L'.
+
+ See also [the daemon version of the `--log-file-format`
+ option](#dopt--log-file-format).
0. `--stats`
This tells rsync to print a verbose set of statistics on the file transfer,
allowing you to tell how effective rsync's delta-transfer algorithm is for
- your data. This option is equivalent to `--info=stats2` if combined with 0
- or 1 `-v` options, or `--info=stats3` if combined with 2 or more `-v`
- options.
+ your data. This option is equivalent to [`--info=stats2`](#opt) if
+ combined with 0 or 1 [`-v`](#opt) options, or [`--info=stats3`](#opt) if
+ combined with 2 or more [`-v`](#opt) options.
The current statistics are as follows:
@@ -2909,12 +3295,14 @@ your home directory (remove the '=' for that).
0. `--human-readable`, `-h`
- Output numbers in a more human-readable format. There are 3 possible
- levels: (1) output numbers with a separator between each set of 3 digits
- (either a comma or a period, depending on if the decimal point is
- represented by a period or a comma); (2) output numbers in units of 1000
- (with a character suffix for larger units -- see below); (3) output
- numbers in units of 1024.
+ Output numbers in a more human-readable format. There are 3 possible levels:
+
+ 1. output numbers with a separator between each set of 3 digits (either a
+ comma or a period, depending on if the decimal point is represented by a
+ period or a comma).
+ 2. output numbers in units of 1000 (with a character suffix for larger
+ units -- see below).
+ 3. output numbers in units of 1024.
The default is human-readable level 1. Each `-h` option increases the
level by one. You can take the level down to 0 (to output numbers as pure
@@ -2929,7 +3317,7 @@ your home directory (remove the '=' for that).
support human-readable level 1, and they default to level 0. Thus,
specifying one or two `-h` options will behave in a comparable manner in
old and new versions as long as you didn't specify a `--no-h` option prior
- to one or more `-h` options. See the `--list-only` option for one
+ to one or more `-h` options. See the [`--list-only`](#opt) option for one
difference.
0. `--partial`
@@ -2942,56 +3330,64 @@ your home directory (remove the '=' for that).
0. `--partial-dir=DIR`
- A better way to keep partial files than the `--partial` option is to
- specify a _DIR_ that will be used to hold the partial data (instead of
- writing it out to the destination file). On the next transfer, rsync will
- use a file found in this dir as data to speed up the resumption of the
+ This option modifies the behavior of the [`--partial`](#opt) option while
+ also implying that it be enabled. This enhanced partial-file method puts
+ any partially transferred files into the specified _DIR_ instead of writing
+ the partial file out to the destination file. On the next transfer, rsync
+ will use a file found in this dir as data to speed up the resumption of the
transfer and then delete it after it has served its purpose.
- Note that if `--whole-file` is specified (or implied), any partial-dir file
- that is found for a file that is being updated will simply be removed
- (since rsync is sending files without using rsync's delta-transfer
- algorithm).
+ Note that if [`--whole-file`](#opt) is specified (or implied), any
+ partial-dir files that are found for a file that is being updated will
+ simply be removed (since rsync is sending files without using rsync's
+ delta-transfer algorithm).
- Rsync will create the _DIR_ if it is missing (just the last dir -- not the
- whole path). This makes it easy to use a relative path (such as
+ Rsync will create the _DIR_ if it is missing, but just the last dir -- not
+ the whole path. This makes it easy to use a relative path (such as
"`--partial-dir=.rsync-partial`") to have rsync create the
- partial-directory in the destination file's directory when needed, and then
- remove it again when the partial file is deleted. Note that the directory
- is only removed if it is a relative pathname, as it is expected that an
- absolute path is to a directory that is reserved for partial-dir work.
+ partial-directory in the destination file's directory when it is needed,
+ and then remove it again when the partial file is deleted. Note that this
+ directory removal is only done for a relative pathname, as it is expected
+ that an absolute path is to a directory that is reserved for partial-dir
+ work.
If the partial-dir value is not an absolute path, rsync will add an exclude
rule at the end of all your existing excludes. This will prevent the
sending of any partial-dir files that may exist on the sending side, and
will also prevent the untimely deletion of partial-dir items on the
receiving side. An example: the above `--partial-dir` option would add the
- equivalent of "`-f '-p .rsync-partial/'`" at the end of any other filter
- rules.
+ equivalent of this "perishable" exclude at the end of any other filter
+ rules: `-f '-p .rsync-partial/'`
If you are supplying your own exclude rules, you may need to add your own
- exclude/hide/protect rule for the partial-dir because (1) the auto-added
- rule may be ineffective at the end of your other rules, or (2) you may wish
- to override rsync's exclude choice. For instance, if you want to make
- rsync clean-up any left-over partial-dirs that may be lying around, you
- should specify `--delete-after` and add a "risk" filter rule, e.g.
- `-f 'R .rsync-partial/'`. (Avoid using `--delete-before` or
- `--delete-during` unless you don't need rsync to use any of the left-over
- partial-dir data during the current run.)
+ exclude/hide/protect rule for the partial-dir because:
+
+ 1. the auto-added rule may be ineffective at the end of your other rules, or
+ 2. you may wish to override rsync's exclude choice.
+
+ For instance, if you want to make rsync clean-up any left-over partial-dirs
+ that may be lying around, you should specify [`--delete-after`](#opt) and
+ add a "risk" filter rule, e.g. `-f 'R .rsync-partial/'`. Avoid using
+ [`--delete-before`](#opt) or [`--delete-during`](#opt) unless you don't
+ need rsync to use any of the left-over partial-dir data during the current
+ run.
IMPORTANT: the `--partial-dir` should not be writable by other users or it
- is a security risk. E.g. AVOID "/tmp".
-
- You can also set the partial-dir value the RSYNC_PARTIAL_DIR environment
- variable. Setting this in the environment does not force `--partial` to be
- enabled, but rather it affects where partial files go when `--partial` is
- specified. For instance, instead of using `--partial-dir=.rsync-tmp` along
- with `--progress`, you could set RSYNC_PARTIAL_DIR=.rsync-tmp in your
- environment and then just use the `-P` option to turn on the use of the
- .rsync-tmp dir for partial transfers. The only times that the `--partial`
- option does not look for this environment value are (1) when `--inplace`
- was specified (since `--inplace` conflicts with `--partial-dir`), and (2)
- when `--delay-updates` was specified (see below).
+ is a security risk! E.g. AVOID "/tmp"!
+
+ You can also set the partial-dir value the [`RSYNC_PARTIAL_DIR`](#)
+ environment variable. Setting this in the environment does not force
+ [`--partial`](#opt) to be enabled, but rather it affects where partial
+ files go when [`--partial`](#opt) is specified. For instance, instead of
+ using `--partial-dir=.rsync-tmp` along with [`--progress`](#opt), you could
+ set [`RSYNC_PARTIAL_DIR=.rsync-tmp`](#) in your environment and then use
+ the [`-P`](#opt) option to turn on the use of the .rsync-tmp dir for
+ partial transfers. The only times that the [`--partial`](#opt) option does
+ not look for this environment value are:
+
+ 1. when [`--inplace`](#opt) was specified (since [`--inplace`](#opt)
+ conflicts with `--partial-dir`), and
+ 2. when [`--delay-updates`](#opt) was specified (see below).
When a modern rsync resumes the transfer of a file in the partial-dir, that
partial file is now updated in-place instead of creating yet another
@@ -3000,10 +3396,10 @@ your home directory (remove the '=' for that).
3.2.0.
For the purposes of the daemon-config's "`refuse options`" setting,
- `--partial-dir` does _not_ imply `--partial`. This is so that a refusal of
- the `--partial` option can be used to disallow the overwriting of
- destination files with a partial transfer, while still allowing the safer
- idiom provided by `--partial-dir`.
+ `--partial-dir` does _not_ imply [`--partial`](#opt). This is so that a
+ refusal of the [`--partial`](#opt) option can be used to disallow the
+ overwriting of destination files with a partial transfer, while still
+ allowing the safer idiom provided by `--partial-dir`.
0. `--delay-updates`
@@ -3012,27 +3408,30 @@ your home directory (remove the '=' for that).
renamed into place in rapid succession. This attempts to make the updating
of the files a little more atomic. By default the files are placed into a
directory named `.~tmp~` in each file's destination directory, but if
- you've specified the `--partial-dir` option, that directory will be used
- instead. See the comments in the `--partial-dir` section for a discussion
- of how this `.~tmp~` dir will be excluded from the transfer, and what you
- can do if you want rsync to cleanup old `.~tmp~` dirs that might be lying
- around. Conflicts with `--inplace` and `--append`.
+ you've specified the [`--partial-dir`](#opt) option, that directory will be
+ used instead. See the comments in the [`--partial-dir`](#opt) section for
+ a discussion of how this `.~tmp~` dir will be excluded from the transfer,
+ and what you can do if you want rsync to cleanup old `.~tmp~` dirs that
+ might be lying around. Conflicts with [`--inplace`](#opt) and
+ [`--append`](#opt).
- This option implies `--no-inc-recursive` since it needs the full file list
- in memory in order to be able to iterate over it at the end.
+ This option implies [`--no-inc-recursive`](#opt) since it needs the full
+ file list in memory in order to be able to iterate over it at the end.
This option uses more memory on the receiving side (one bit per file
transferred) and also requires enough free disk space on the receiving side
to hold an additional copy of all the updated files. Note also that you
- should not use an absolute path to `--partial-dir` unless (1) there is no
- chance of any of the files in the transfer having the same name (since all
- the updated files will be put into a single directory if the path is
- absolute) and (2) there are no mount points in the hierarchy (since the
- delayed updates will fail if they can't be renamed into place).
+ should not use an absolute path to [`--partial-dir`](#opt) unless:
+
+ 1. there is no chance of any of the files in the transfer having the same
+ name (since all the updated files will be put into a single directory if
+ the path is absolute), and
+ 2. there are no mount points in the hierarchy (since the delayed updates
+ will fail if they can't be renamed into place).
- See also the "atomic-rsync" perl script in the "support" subdir for an
- update algorithm that is even more atomic (it uses `--link-dest` and a
- parallel hierarchy of files).
+ See also the "atomic-rsync" python script in the "support" subdir for an
+ update algorithm that is even more atomic (it uses [`--link-dest`](#opt)
+ and a parallel hierarchy of files).
0. `--prune-empty-dirs`, `-m`
@@ -3042,10 +3441,8 @@ your home directory (remove the '=' for that).
directories when the sending rsync is recursively scanning a hierarchy of
files using include/exclude/filter rules.
- Note that the use of transfer rules, such as the `--min-size` option, does
- not affect what goes into the file list, and thus does not leave
- directories empty, even if none of the files in a directory match the
- transfer rule.
+ This option can still leave empty directories on the receiving side if you
+ make use of [TRANSFER_RULES](#).
Because the file-list is actually being pruned, this option also affects
what directories get deleted when a delete is active. However, keep in
@@ -3075,9 +3472,9 @@ your home directory (remove the '=' for that).
This option tells rsync to print information showing the progress of the
transfer. This gives a bored user something to watch. With a modern rsync
- this is the same as specifying `--info=flist2,name,progress`, but any
- user-supplied settings for those info flags takes precedence (e.g.
- "`--info=flist0 --progress`").
+ this is the same as specifying [`--info=flist2,name,progress`](#opt), but
+ any user-supplied settings for those info flags takes precedence (e.g.
+ [`--info=flist0 --progress`](#opt)).
While rsync is transferring a regular file, it updates a progress line that
looks like this:
@@ -3120,16 +3517,17 @@ your home directory (remove the '=' for that).
0. `-P`
- The `-P` option is equivalent to `--partial --progress`. Its purpose is
- to make it much easier to specify these two options for a long transfer
- that may be interrupted.
+ The `-P` option is equivalent to "[`--partial`](#opt)
+ [`--progress`](#opt)". Its purpose is to make it much easier to specify
+ these two options for a long transfer that may be interrupted.
- There is also a `--info=progress2` option that outputs statistics based on
- the whole transfer, rather than individual files. Use this flag without
- outputting a filename (e.g. avoid `-v` or specify `--info=name0`) if you
- want to see how the transfer is doing without scrolling the screen with a
- lot of names. (You don't need to specify the `--progress` option in order
- to use `--info=progress2`.)
+ There is also a [`--info=progress2`](#opt) option that outputs statistics
+ based on the whole transfer, rather than individual files. Use this flag
+ without outputting a filename (e.g. avoid `-v` or specify
+ [`--info=name0`](#opt)) if you want to see how the transfer is doing
+ without scrolling the screen with a lot of names. (You don't need to
+ specify the [`--progress`](#opt) option in order to use
+ [`--info=progress2`](#opt).)
Finally, you can get an instant progress report by sending rsync a signal
of either SIGINFO or SIGVTALRM. On BSD systems, a SIGINFO is generated by
@@ -3138,8 +3536,8 @@ your home directory (remove the '=' for that).
output a single progress report which is output when the current file
transfer finishes (so it may take a little time if a big file is being
handled when the signal arrives). A filename is output (if needed)
- followed by the `--info=progress2` format of progress info. If you don't
- know which of the 3 rsync processes is the client process, it's OK to
+ followed by the [`--info=progress2`](#opt) format of progress info. If you
+ don't know which of the 3 rsync processes is the client process, it's OK to
signal all of them (since the non-client processes ignore the signal).
CAUTION: sending SIGVTALRM to an older rsync (pre-3.2.0) will kill it.
@@ -3172,40 +3570,48 @@ your home directory (remove the '=' for that).
This option will cause the source files to be listed instead of
transferred. This option is inferred if there is a single source arg and
- no destination specified, so its main uses are: (1) to turn a copy command
- that includes a destination arg into a file-listing command, or (2) to be
- able to specify more than one source arg (note: be sure to include the
- destination). Caution: keep in mind that a source arg with a wild-card is
- expanded by the shell into multiple args, so it is never safe to try to
- list such an arg without using this option. For example:
+ no destination specified, so its main uses are:
+
+ 1. to turn a copy command that includes a destination arg into a
+ file-listing command, or
+ 2. to be able to specify more than one source arg. Note: be sure to
+ include the destination.
+
+ CAUTION: keep in mind that a source arg with a wild-card is expanded by the
+ shell into multiple args, so it is never safe to try to specify a single
+ wild-card arg to try to infer this option. A safe example is:
> rsync -av --list-only foo* dest/
- Starting with rsync 3.1.0, the sizes output by `--list-only` are affected
- by the `--human-readable` option. By default they will contain digit
- separators, but higher levels of readability will output the sizes with
- unit suffixes. Note also that the column width for the size output has
- increased from 11 to 14 characters for all human-readable levels. Use
- `--no-h` if you want just digits in the sizes, and the old column width of
- 11 characters.
+ This option always uses an output format that looks similar to this:
+
+ > drwxrwxr-x 4,096 2022/09/30 12:53:11 support
+ > -rw-rw-r-- 80 2005/01/11 10:37:37 support/Makefile
+
+ The only option that affects this output style is (as of 3.1.0) the
+ [`--human-readable`](#opt) (`-h`) option. The default is to output sizes
+ as byte counts with digit separators (in a 14-character-width column).
+ Specifying at least one `-h` option makes the sizes output with unit
+ suffixes. If you want old-style bytecount sizes without digit separators
+ (and an 11-character-width column) use `--no-h`.
Compatibility note: when requesting a remote listing of files from an rsync
that is version 2.6.3 or older, you may encounter an error if you ask for a
- non-recursive listing. This is because a file listing implies the `--dirs`
- option w/o `--recursive`, and older rsyncs don't have that option. To
- avoid this problem, either specify the `--no-dirs` option (if you don't
- need to expand a directory's content), or turn on recursion and exclude the
- content of subdirectories: `-r --exclude='/*/*'`.
+ non-recursive listing. This is because a file listing implies the
+ [`--dirs`](#opt) option w/o [`--recursive`](#opt), and older rsyncs don't
+ have that option. To avoid this problem, either specify the `--no-dirs`
+ option (if you don't need to expand a directory's content), or turn on
+ recursion and exclude the content of subdirectories: `-r --exclude='/*/*'`.
0. `--bwlimit=RATE`
This option allows you to specify the maximum transfer rate for the data
sent over the socket, specified in units per second. The RATE value can be
suffixed with a string to indicate a size multiplier, and may be a
- fractional value (e.g. "`--bwlimit=1.5m`"). If no suffix is specified, the
+ fractional value (e.g. `--bwlimit=1.5m`). If no suffix is specified, the
value will be assumed to be in units of 1024 bytes (as if "K" or "KiB" had
- been appended). See the `--max-size` option for a description of all the
- available suffixes. A value of 0 specifies no limit.
+ been appended). See the [`--max-size`](#opt) option for a description of
+ all the available suffixes. A value of 0 specifies no limit.
For backward-compatibility reasons, the rate limit will be rounded to the
nearest KiB unit, so no rate smaller than 1024 bytes per second is
@@ -3217,26 +3623,28 @@ your home directory (remove the '=' for that).
rsync writes out a block of data and then sleeps to bring the average rate
into compliance.
- Due to the internal buffering of data, the `--progress` option may not be
- an accurate reflection on how fast the data is being sent. This is because
- some files can show up as being rapidly sent when the data is quickly
- buffered, while other can show up as very slow when the flushing of the
- output buffer occurs. This may be fixed in a future version.
+ Due to the internal buffering of data, the [`--progress`](#opt) option may
+ not be an accurate reflection on how fast the data is being sent. This is
+ because some files can show up as being rapidly sent when the data is
+ quickly buffered, while other can show up as very slow when the flushing of
+ the output buffer occurs. This may be fixed in a future version.
+
+ See also [the daemon version of the `--bwlimit` option](#dopt--bwlimit).
-0. `--stop-after=MINS
+0. `--stop-after=MINS`, (`--time-limit=MINS`)
This option tells rsync to stop copying when the specified number of
minutes has elapsed.
- Rsync also accepts an earlier version of this option: `--time-limit=MINS`.
-
For maximal flexibility, rsync does not communicate this option to the
remote rsync since it is usually enough that one side of the connection
quits as specified. This allows the option's use even when only one side
of the connection supports it. You can tell the remote side about the time
- limit using `--remote-option` (`-M`), should the need arise.
+ limit using [`--remote-option`](#opt) (`-M`), should the need arise.
+
+ The `--time-limit` version of this option is deprecated.
-0. `--stop-at=y-m-dTh:m
+0. `--stop-at=y-m-dTh:m`
This option tells rsync to stop copying when the specified point in time
has been reached. The date & time can be fully specified in a numeric
@@ -3259,9 +3667,9 @@ your home directory (remove the '=' for that).
remote rsync since it is usually enough that one side of the connection
quits as specified. This allows the option's use even when only one side
of the connection supports it. You can tell the remote side about the time
- limit using `--remote-option` (`-M`), should the need arise. Do keep in
- mind that the remote host may have a different default timezone than your
- local host.
+ limit using [`--remote-option`](#opt) (`-M`), should the need arise. Do
+ keep in mind that the remote host may have a different default timezone
+ than your local host.
0. `--fsync`
@@ -3272,20 +3680,20 @@ your home directory (remove the '=' for that).
0. `--write-batch=FILE`
Record a file that can later be applied to another identical destination
- with `--read-batch`. See the "BATCH MODE" section for details, and also
- the `--only-write-batch` option.
+ with [`--read-batch`](#opt). See the "BATCH MODE" section for details, and
+ also the [`--only-write-batch`](#opt) option.
This option overrides the negotiated checksum & compress lists and always
negotiates a choice based on old-school md5/md4/zlib choices. If you want
- a more modern choice, use the `--checksum-choice` (`--cc`) and/or
- `--compress-choice` (`--zc`) options.
+ a more modern choice, use the [`--checksum-choice`](#opt) (`--cc`) and/or
+ [`--compress-choice`](#opt) (`--zc`) options.
0. `--only-write-batch=FILE`
- Works like `--write-batch`, except that no updates are made on the
+ Works like [`--write-batch`](#opt), except that no updates are made on the
destination system when creating the batch. This lets you transport the
changes to the destination system via some other means and then apply the
- changes via `--read-batch`.
+ changes via [`--read-batch`](#opt).
Note that you can feel free to write the batch directly to some portable
media: if this media fills to capacity before the end of the transfer, you
@@ -3302,18 +3710,18 @@ your home directory (remove the '=' for that).
0. `--read-batch=FILE`
Apply all of the changes stored in FILE, a file previously generated by
- `--write-batch`. If _FILE_ is `-`, the batch data will be read from
- standard input. See the "BATCH MODE" section for details.
+ [`--write-batch`](#opt). If _FILE_ is `-`, the batch data will be read
+ from standard input. See the "BATCH MODE" section for details.
0. `--protocol=NUM`
Force an older protocol version to be used. This is useful for creating a
batch file that is compatible with an older version of rsync. For
- instance, if rsync 2.6.4 is being used with the `--write-batch` option, but
- rsync 2.6.3 is what will be used to run the `--read-batch` option, you
- should use "--protocol=28" when creating the batch file to force the older
- protocol version to be used in the batch file (assuming you can't upgrade
- the rsync on the reading system).
+ instance, if rsync 2.6.4 is being used with the [`--write-batch`](#opt)
+ option, but rsync 2.6.3 is what will be used to run the
+ [`--read-batch`](#opt) option, you should use "--protocol=28" when creating
+ the batch file to force the older protocol version to be used in the batch
+ file (assuming you can't upgrade the rsync on the reading system).
0. `--iconv=CONVERT_SPEC`
@@ -3325,15 +3733,15 @@ your home directory (remove the '=' for that).
This order ensures that the option will stay the same whether you're
pushing or pulling files. Finally, you can specify either `--no-iconv` or
a CONVERT_SPEC of "-" to turn off any conversion. The default setting of
- this option is site-specific, and can also be affected via the RSYNC_ICONV
- environment variable.
+ this option is site-specific, and can also be affected via the
+ [`RSYNC_ICONV`](#) environment variable.
For a list of what charset names your local iconv library supports, you can
run "`iconv --list`".
- If you specify the `--protect-args` option (`-s`), rsync will translate the
- filenames you specify on the command-line that are being sent to the remote
- host. See also the `--files-from` option.
+ If you specify the [`--secluded-args`](#opt) (`-s`) option, rsync will
+ translate the filenames you specify on the command-line that are being sent
+ to the remote host. See also the [`--files-from`](#opt) option.
Note that rsync does not do any conversion of names in filter files
(including include/exclude files). It is up to you to ensure that you're
@@ -3354,12 +3762,12 @@ your home directory (remove the '=' for that).
socket when directly contacting an rsync daemon, as well as the forwarding
of the `-4` or `-6` option to ssh when rsync can deduce that ssh is being
used as the remote shell. For other remote shells you'll need to specify
- the "`--rsh SHELL -4`" option directly (or whatever ipv4/ipv6 hint options
+ the "`--rsh SHELL -4`" option directly (or whatever IPv4/IPv6 hint options
it uses).
- These options also exist in the `--daemon` mode section.
+ See also [the daemon version of these options](#dopt--ipv4).
- If rsync was complied without support for IPv6, the `--ipv6` option will
+ If rsync was compiled without support for IPv6, the `--ipv6` option will
have no effect. The `rsync --version` output will contain "`no IPv6`" if
is the case.
@@ -3374,7 +3782,7 @@ your home directory (remove the '=' for that).
user wants a more random checksum seed. Setting NUM to 0 causes rsync to
use the default of **time**() for checksum seed.
-# DAEMON OPTIONS
+## DAEMON OPTIONS
The options allowed when starting an rsync daemon are as follows:
@@ -3387,31 +3795,37 @@ The options allowed when starting an rsync daemon are as follows:
If standard input is a socket then rsync will assume that it is being run
via inetd, otherwise it will detach from the current terminal and become a
background daemon. The daemon will read the config file (rsyncd.conf) on
- each connect made by a client and respond to requests accordingly. See the
- **rsyncd.conf**(5) man page for more details.
+ each connect made by a client and respond to requests accordingly.
+
+ See the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage for more details.
0. `--address=ADDRESS`
By default rsync will bind to the wildcard address when run as a daemon
with the `--daemon` option. The `--address` option allows you to specify a
specific IP address (or hostname) to bind to. This makes virtual hosting
- possible in conjunction with the `--config` option. See also the "address"
- global option in the rsyncd.conf manpage.
+ possible in conjunction with the `--config` option.
+
+ See also the [address](rsyncd.conf.5#address) global option in the
+ rsyncd.conf manpage and the [client version of the `--address`
+ option](#opt--address).
0. `--bwlimit=RATE`
This option allows you to specify the maximum transfer rate for the data
the daemon sends over the socket. The client can still specify a smaller
- `--bwlimit` value, but no larger value will be allowed. See the client
- version of this option (above) for some extra details.
+ `--bwlimit` value, but no larger value will be allowed.
+
+ See the [client version of the `--bwlimit` option](#opt--bwlimit) for some
+ extra details.
0. `--config=FILE`
This specifies an alternate config file than the default. This is only
- relevant when `--daemon` is specified. The default is /etc/rsyncd.conf
- unless the daemon is running over a remote shell program and the remote
- user is not the super-user; in that case the default is rsyncd.conf in the
- current directory (typically $HOME).
+ relevant when [`--daemon`](#dopt) is specified. The default is
+ /etc/rsyncd.conf unless the daemon is running over a remote shell program
+ and the remote user is not the super-user; in that case the default is
+ rsyncd.conf in the current directory (typically $HOME).
0. `--dparam=OVERRIDE`, `-M`
@@ -3435,14 +3849,18 @@ The options allowed when starting an rsync daemon are as follows:
0. `--port=PORT`
This specifies an alternate TCP port number for the daemon to listen on
- rather than the default of 873. See also the "port" global option in the
- rsyncd.conf manpage.
+ rather than the default of 873.
+
+ See also [the client version of the `--port` option](#opt--port) and the
+ [port](rsyncd.conf.5#port) global setting in the rsyncd.conf manpage.
0. `--log-file=FILE`
This option tells the rsync daemon to use the given log-file name instead
of using the "`log file`" setting in the config file.
+ See also [the client version of the `--log-file` option](#opt--log-file).
+
0. `--log-file-format=FORMAT`
This option tells the rsync daemon to use the given FORMAT string instead
@@ -3450,10 +3868,15 @@ The options allowed when starting an rsync daemon are as follows:
"`transfer logging`" unless the string is empty, in which case transfer
logging is turned off.
+ See also [the client version of the `--log-file-format`
+ option](#opt--log-file-format).
+
0. `--sockopts`
- This overrides the `socket options` setting in the rsyncd.conf file and has
- the same syntax.
+ This overrides the [`socket options`](rsyncd.conf.5#socket_options)
+ setting in the rsyncd.conf file and has the same syntax.
+
+ See also [the client version of the `--sockopts` option](#opt--sockopts).
0. `--verbose`, `-v`
@@ -3462,6 +3885,8 @@ The options allowed when starting an rsync daemon are as follows:
will be controlled by the options that the client used and the
"`max verbosity`" setting in the module's config section.
+ See also [the client version of the `--verbose` option](#opt--verbose).
+
0. `--ipv4`, `-4` or `--ipv6`, `-6`
Tells rsync to prefer IPv4/IPv6 when creating the incoming sockets that the
@@ -3471,9 +3896,9 @@ The options allowed when starting an rsync daemon are as follows:
using the port, try specifying `--ipv6` or `--ipv4` when starting the
daemon).
- These options also exist in the regular rsync options section.
+ See also [the client version of these options](#opt--ipv4).
- If rsync was complied without support for IPv6, the `--ipv6` option will
+ If rsync was compiled without support for IPv6, the `--ipv6` option will
have no effect. The `rsync --version` output will contain "`no IPv6`" if
is the case.
@@ -3482,21 +3907,148 @@ The options allowed when starting an rsync daemon are as follows:
When specified after `--daemon`, print a short help page describing the
options available for starting an rsync daemon.
-# FILTER RULES
+## FILTER RULES
-The filter rules allow for flexible selection of which files to transfer
-(include) and which files to skip (exclude). The rules either directly specify
-include/exclude patterns or they specify a way to acquire more include/exclude
-patterns (e.g. to read them from a file).
+The filter rules allow for custom control of several aspects of how files are
+handled:
-As the list of files/directories to transfer is built, rsync checks each name
-to be transferred against the list of include/exclude patterns in turn, and the
-first matching pattern is acted on: if it is an exclude pattern, then that file
-is skipped; if it is an include pattern then that filename is not skipped; if
-no matching pattern is found, then the filename is not skipped.
+- Control which files the sending side puts into the file list that describes
+ the transfer hierarchy
+- Control which files the receiving side protects from deletion when the file
+ is not in the sender's file list
+- Control which extended attribute names are skipped when copying xattrs
+
+The rules are either directly specified via option arguments or they can be
+read in from one or more files. The filter-rule files can even be a part of
+the hierarchy of files being copied, affecting different parts of the tree in
+different ways.
+
+### SIMPLE INCLUDE/EXCLUDE RULES
+
+We will first cover the basics of how include & exclude rules affect what files
+are transferred, ignoring any deletion side-effects. Filter rules mainly
+affect the contents of directories that rsync is "recursing" into, but they can
+also affect a top-level item in the transfer that was specified as a argument.
+
+The default for any unmatched file/dir is for it to be included in the
+transfer, which puts the file/dir into the sender's file list. The use of an
+exclude rule causes one or more matching files/dirs to be left out of the
+sender's file list. An include rule can be used to limit the effect of an
+exclude rule that is matching too many files.
+
+The order of the rules is important because the first rule that matches is the
+one that takes effect. Thus, if an early rule excludes a file, no include rule
+that comes after it can have any effect. This means that you must place any
+include overrides somewhere prior to the exclude that it is intended to limit.
-Rsync builds an ordered list of filter rules as specified on the command-line.
-Filter rules have the following syntax:
+When a directory is excluded, all its contents and sub-contents are also
+excluded. The sender doesn't scan through any of it at all, which can save a
+lot of time when skipping large unneeded sub-trees.
+
+It is also important to understand that the include/exclude rules are applied
+to every file and directory that the sender is recursing into. Thus, if you
+want a particular deep file to be included, you have to make sure that none of
+the directories that must be traversed on the way down to that file are
+excluded or else the file will never be discovered to be included. As an
+example, if the directory "`a/path`" was given as a transfer argument and you
+want to ensure that the file "`a/path/down/deep/wanted.txt`" is a part of the
+transfer, then the sender must not exclude the directories "`a/path`",
+"`a/path/down`", or "`a/path/down/deep`" as it makes it way scanning through
+the file tree.
+
+When you are working on the rules, it can be helpful to ask rsync to tell you
+what is being excluded/included and why. Specifying `--debug=FILTER` or (when
+pulling files) `-M--debug=FILTER` turns on level 1 of the FILTER debug
+information that will output a message any time that a file or directory is
+included or excluded and which rule it matched. Beginning in 3.2.4 it will
+also warn if a filter rule has trailing whitespace, since an exclude of "foo "
+(with a trailing space) will not exclude a file named "foo".
+
+Exclude and include rules can specify wildcard [PATTERN MATCHING RULES](#)
+(similar to shell wildcards) that allow you to match things like a file suffix
+or a portion of a filename.
+
+A rule can be limited to only affecting a directory by putting a trailing slash
+onto the filename.
+
+### SIMPLE INCLUDE/EXCLUDE EXAMPLE
+
+With the following file tree created on the sending side:
+
+> mkdir x/
+> touch x/file.txt
+> mkdir x/y/
+> touch x/y/file.txt
+> touch x/y/zzz.txt
+> mkdir x/z/
+> touch x/z/file.txt
+
+Then the following rsync command will transfer the file "`x/y/file.txt`" and
+the directories needed to hold it, resulting in the path "`/tmp/x/y/file.txt`"
+existing on the remote host:
+
+> rsync -ai -f'+ x/' -f'+ x/y/' -f'+ x/y/file.txt' -f'- *' x host:/tmp/
+
+Aside: this copy could also have been accomplished using the [`-R`](#opt)
+option (though the 2 commands behave differently if deletions are enabled):
+
+> rsync -aiR x/y/file.txt host:/tmp/
+
+The following command does not need an include of the "x" directory because it
+is not a part of the transfer (note the trailing slash). Running this command
+would copy just "`/tmp/x/file.txt`" because the "y" and "z" dirs get excluded:
+
+> rsync -ai -f'+ file.txt' -f'- *' x/ host:/tmp/x/
+
+This command would omit the zzz.txt file while copying "x" and everything else
+it contains:
+
+> rsync -ai -f'- zzz.txt' x host:/tmp/
+
+### FILTER RULES WHEN DELETING
+
+By default the include & exclude filter rules affect both the sender
+(as it creates its file list)
+and the receiver (as it creates its file lists for calculating deletions). If
+no delete option is in effect, the receiver skips creating the delete-related
+file lists. This two-sided default can be manually overridden so that you are
+only specifying sender rules or receiver rules, as described in the [FILTER
+RULES IN DEPTH](#) section.
+
+When deleting, an exclude protects a file from being removed on the receiving
+side while an include overrides that protection (putting the file at risk of
+deletion). The default is for a file to be at risk -- its safety depends on it
+matching a corresponding file from the sender.
+
+An example of the two-sided exclude effect can be illustrated by the copying of
+a C development directory between 2 systems. When doing a touch-up copy, you
+might want to skip copying the built executable and the `.o` files (sender
+hide) so that the receiving side can build their own and not lose any object
+files that are already correct (receiver protect). For instance:
+
+> rsync -ai --del -f'- *.o' -f'- cmd' src host:/dest/
+
+Note that using `-f'-p *.o'` is even better than `-f'- *.o'` if there is a
+chance that the directory structure may have changed. The "p" modifier is
+discussed in [FILTER RULE MODIFIERS](#).
+
+One final note, if your shell doesn't mind unexpanded wildcards, you could
+simplify the typing of the filter options by using an underscore in place of
+the space and leaving off the quotes. For instance, `-f -_*.o -f -_cmd` (and
+similar) could be used instead of the filter options above.
+
+### FILTER RULES IN DEPTH
+
+Rsync supports old-style include/exclude rules and new-style filter rules. The
+older rules are specified using [`--include`](#opt) and [`--exclude`](#opt) as
+well as the [`--include-from`](#opt) and [`--exclude-from`](#opt). These are
+limited in behavior but they don't require a "-" or "+" prefix. An old-style
+exclude rule is turned into a "`- name`" filter rule (with no modifiers) and an
+old-style include rule is turned into a "`+ name`" filter rule (with no
+modifiers).
+
+Rsync builds an ordered list of filter rules as specified on the command-line
+and/or read-in from files. New style filter rules have the following syntax:
> RULE [PATTERN_OR_FILENAME]
> RULE,MODIFIERS [PATTERN_OR_FILENAME]
@@ -3504,174 +4056,147 @@ Filter rules have the following syntax:
You have your choice of using either short or long RULE names, as described
below. If you use a short-named rule, the ',' separating the RULE from the
MODIFIERS is optional. The PATTERN or FILENAME that follows (when present)
-must come after either a single space or an underscore (\_). Here are the
-available rule prefixes:
-
-0. `exclude, '-'` specifies an exclude pattern.
-0. `include, '+'` specifies an include pattern.
-0. `merge, '.'` specifies a merge-file to read for more rules.
-0. `dir-merge, ':'` specifies a per-directory merge-file.
+must come after either a single space or an underscore (\_). Any additional
+spaces and/or underscores are considered to be a part of the pattern name.
+Here are the available rule prefixes:
+
+0. `exclude, '-'` specifies an exclude pattern that (by default) is both a
+ `hide` and a `protect`.
+0. `include, '+'` specifies an include pattern that (by default) is both a
+ `show` and a `risk`.
+0. `merge, '.'` specifies a merge-file on the client side to read for more
+ rules.
+0. `dir-merge, ':'` specifies a per-directory merge-file. Using this kind of
+ filter rule requires that you trust the sending side's filter checking, so
+ it has the side-effect mentioned under the [`--trust-sender`](#opt) option.
0. `hide, 'H'` specifies a pattern for hiding files from the transfer.
-0. `show, 'S'` files that match the pattern are not hidden.
+ Equivalent to a sender-only exclude, so `-f'H foo'` could also be specified
+ as `-f'-s foo'`.
+0. `show, 'S'` files that match the pattern are not hidden. Equivalent to a
+ sender-only include, so `-f'S foo'` could also be specified as `-f'+s
+ foo'`.
0. `protect, 'P'` specifies a pattern for protecting files from deletion.
-0. `risk, 'R'` files that match the pattern are not protected.
+ Equivalent to a receiver-only exclude, so `-f'P foo'` could also be
+ specified as `-f'-r foo'`.
+0. `risk, 'R'` files that match the pattern are not protected. Equivalent to a
+ receiver-only include, so `-f'R foo'` could also be specified as `-f'+r
+ foo'`.
0. `clear, '!'` clears the current include/exclude list (takes no arg)
-When rules are being read from a file, empty lines are ignored, as are
-whole-line comments that start with a '`#`' (filename rules that contain a hash
-are unaffected).
-
-[comment]: # (Remember that markdown strips spaces from start/end of ` ... ` sequences!)
-[comment]: # (Thus, the `x ` sequences below use a literal non-breakable space!)
-
-Note that the `--include` & `--exclude` command-line options do not allow the
-full range of rule parsing as described above -- they only allow the
-specification of include / exclude patterns plus a "`!`" token to clear the
-list (and the normal comment parsing when rules are read from a file). If a
-pattern does not begin with "`-Â `" (dash, space) or "`+Â `" (plus, space), then
-the rule will be interpreted as if "`+Â `" (for an include option) or "`-Â `"
-(for an exclude option) were prefixed to the string. A `--filter` option, on
-the other hand, must always contain either a short or long rule name at the
-start of the rule.
-
-Note also that the `--filter`, `--include`, and `--exclude` options take one
-rule/pattern each. To add multiple ones, you can repeat the options on the
-command-line, use the merge-file syntax of the `--filter` option, or the
-`--include-from` / `--exclude-from` options.
-
-# INCLUDE/EXCLUDE PATTERN RULES
-
-You can include and exclude files by specifying patterns using the "+", "-",
-etc. filter rules (as introduced in the FILTER RULES section above). The
-include/exclude rules each specify a pattern that is matched against the names
-of the files that are going to be transferred. These patterns can take several
-forms:
-
-- if the pattern starts with a `/` then it is anchored to a particular spot in
- the hierarchy of files, otherwise it is matched against the end of the
- pathname. This is similar to a leading `^` in regular expressions. Thus
- `/foo` would match a name of "foo" at either the "root of the transfer" (for
- a global rule) or in the merge-file's directory (for a per-directory rule).
- An unqualified `foo` would match a name of "foo" anywhere in the tree because
- the algorithm is applied recursively from the top down; it behaves as if each
- path component gets a turn at being the end of the filename. Even the
- unanchored "sub/foo" would match at any point in the hierarchy where a "foo"
- was found within a directory named "sub". See the section on ANCHORING
- INCLUDE/EXCLUDE PATTERNS for a full discussion of how to specify a pattern
- that matches at the root of the transfer.
-- if the pattern ends with a `/` then it will only match a directory, not a
- regular file, symlink, or device.
-- rsync chooses between doing a simple string match and wildcard matching by
- checking if the pattern contains one of these three wildcard characters:
- '`*`', '`?`', and '`[`' .
-- a '`*`' matches any path component, but it stops at slashes.
-- use '`**`' to match anything, including slashes.
-- a '`?`' matches any character except a slash (`/`).
-- a '`[`' introduces a character class, such as `[a-z]` or `[[:alpha:]]`.
-- in a wildcard pattern, a backslash can be used to escape a wildcard
- character, but it is matched literally when no wildcards are present. This
- means that there is an extra level of backslash removal when a pattern
- contains wildcard characters compared to a pattern that has none. e.g. if
- you add a wildcard to "`foo\bar`" (which matches the backslash) you would
- need to use "`foo\\bar*`" to avoid the "`\b`" becoming just "b".
-- if the pattern contains a `/` (not counting a trailing /) or a "`**`", then it
- is matched against the full pathname, including any leading directories. If
- the pattern doesn't contain a `/` or a "`**`", then it is matched only against
- the final component of the filename. (Remember that the algorithm is applied
- recursively so "full filename" can actually be any portion of a path from the
- starting directory on down.)
-- a trailing "`dir_name/***`" will match both the directory (as if "dir_name/"
+When rules are being read from a file (using merge or dir-merge), empty lines
+are ignored, as are whole-line comments that start with a '`#`' (filename rules
+that contain a hash character are unaffected).
+
+Note also that the [`--filter`](#opt), [`--include`](#opt), and
+[`--exclude`](#opt) options take one rule/pattern each. To add multiple ones,
+you can repeat the options on the command-line, use the merge-file syntax of
+the [`--filter`](#opt) option, or the [`--include-from`](#opt) /
+[`--exclude-from`](#opt) options.
+
+### PATTERN MATCHING RULES
+
+Most of the rules mentioned above take an argument that specifies what the rule
+should match. If rsync is recursing through a directory hierarchy, keep in
+mind that each pattern is matched against the name of every directory in the
+descent path as rsync finds the filenames to send.
+
+The matching rules for the pattern argument take several forms:
+
+- If a pattern contains a `/` (not counting a trailing slash) or a "`**`"
+ (which can match a slash), then the pattern is matched against the full
+ pathname, including any leading directories within the transfer. If the
+ pattern doesn't contain a (non-trailing) `/` or a "`**`", then it is matched
+ only against the final component of the filename or pathname. For example,
+ `foo` means that the final path component must be "foo" while `foo/bar` would
+ match the last 2 elements of the path (as long as both elements are within
+ the transfer).
+- A pattern that ends with a `/` only matches a directory, not a regular file,
+ symlink, or device.
+- A pattern that starts with a `/` is anchored to the start of the transfer
+ path instead of the end. For example, `/foo/**` or `/foo/bar/**` match only
+ leading elements in the path. If the rule is read from a per-directory
+ filter file, the transfer path being matched will begin at the level of the
+ filter file instead of the top of the transfer. See the section on
+ [ANCHORING INCLUDE/EXCLUDE PATTERNS](#) for a full discussion of how to
+ specify a pattern that matches at the root of the transfer.
+
+Rsync chooses between doing a simple string match and wildcard matching by
+checking if the pattern contains one of these three wildcard characters: '`*`',
+'`?`', and '`[`' :
+
+- a '`?`' matches any single character except a slash (`/`).
+- a '`*`' matches zero or more non-slash characters.
+- a '`**`' matches zero or more characters, including slashes.
+- a '`[`' introduces a character class, such as `[a-z]` or `[[:alpha:]]`, that
+ must match one character.
+- a trailing `***` in the pattern is a shorthand that allows you to match a
+ directory and all its contents using a single rule. For example, specifying
+ "`dir_name/***`" will match both the "dir_name" directory (as if "`dir_name/`"
had been specified) and everything in the directory (as if "`dir_name/**`"
- had been specified). This behavior was added in version 2.6.7.
-
-Note that, when using the `--recursive` (`-r`) option (which is implied by
-`-a`), every subdir component of every path is visited left to right, with each
-directory having a chance for exclusion before its content. In this way
-include/exclude patterns are applied recursively to the pathname of each node
-in the filesystem's tree (those inside the transfer). The exclude patterns
-short-circuit the directory traversal stage as rsync finds the files to send.
-
-For instance, to include "`/foo/bar/baz`", the directories "`/foo`" and "`/foo/bar`"
-must not be excluded. Excluding one of those parent directories prevents the
-examination of its content, cutting off rsync's recursion into those paths and
-rendering the include for "`/foo/bar/baz`" ineffectual (since rsync can't match
-something it never sees in the cut-off section of the directory hierarchy).
-
-The concept path exclusion is particularly important when using a trailing '`*`'
-rule. For instance, this won't work:
-
-> + /some/path/this-file-will-not-be-found
-> + /file-is-included
-> - *
-
-This fails because the parent directory "some" is excluded by the '`*`' rule, so
-rsync never visits any of the files in the "some" or "some/path" directories.
-One solution is to ask for all directories in the hierarchy to be included by
-using a single rule: "`+ */`" (put it somewhere before the "`- *`" rule), and
-perhaps use the `--prune-empty-dirs` option. Another solution is to add
-specific include rules for all the parent dirs that need to be visited. For
-instance, this set of rules works fine:
-
-> + /some/
-> + /some/path/
-> + /some/path/this-file-is-found
-> + /file-also-included
-> - *
+ had been specified).
+- a backslash can be used to escape a wildcard character, but it is only
+ interpreted as an escape character if at least one wildcard character is
+ present in the match pattern. For instance, the pattern "`foo\bar`" matches
+ that single backslash literally, while the pattern "`foo\bar*`" would need to
+ be changed to "`foo\\bar*`" to avoid the "`\b`" becoming just "b".
Here are some examples of exclude/include matching:
-- "`- *.o`" would exclude all names matching `*.o`
-- "`- /foo`" would exclude a file (or directory) named foo in the transfer-root
- directory
-- "`- foo/`" would exclude any directory named foo
-- "`- /foo/*/bar`" would exclude any file named bar which is at two levels
- below a directory named foo in the transfer-root directory
-- "`- /foo/**/bar`" would exclude any file named bar two or more levels below a
- directory named foo in the transfer-root directory
-- The combination of "`+ */`", "`+ *.c`", and "`- *`" would include all
- directories and C source files but nothing else (see also the
- `--prune-empty-dirs` option)
-- The combination of "`+ foo/`", "`+ foo/bar.c`", and "`- *`" would include
- only the foo directory and foo/bar.c (the foo directory must be explicitly
- included or it would be excluded by the "`*`")
-
-The following modifiers are accepted after a "`+`" or "`-`":
+- Option `-f'- *.o'` would exclude all filenames ending with `.o`
+- Option `-f'- /foo'` would exclude a file (or directory) named foo in the
+ transfer-root directory
+- Option `-f'- foo/'` would exclude any directory named foo
+- Option `-f'- foo/*/bar'` would exclude any file/dir named bar which is at two
+ levels below a directory named foo (if foo is in the transfer)
+- Option `-f'- /foo/**/bar'` would exclude any file/dir named bar that was two
+ or more levels below a top-level directory named foo (note that /foo/bar is
+ **not** excluded by this)
+- Options `-f'+ */' -f'+ *.c' -f'- *'` would include all directories and .c
+ source files but nothing else
+- Options `-f'+ foo/' -f'+ foo/bar.c' -f'- *'` would include only the foo
+ directory and foo/bar.c (the foo directory must be explicitly included or it
+ would be excluded by the "`- *`")
+
+### FILTER RULE MODIFIERS
+
+The following modifiers are accepted after an include (+) or exclude (-) rule:
- A `/` specifies that the include/exclude rule should be matched against the
- absolute pathname of the current item. For example, "`-/ /etc/passwd`" would
- exclude the passwd file any time the transfer was sending files from the
- "/etc" directory, and "-/ subdir/foo" would always exclude "foo" when it is
- in a dir named "subdir", even if "foo" is at the root of the current
+ absolute pathname of the current item. For example, `-f'-/ /etc/passwd'`
+ would exclude the passwd file any time the transfer was sending files from
+ the "/etc" directory, and "-/ subdir/foo" would always exclude "foo" when it
+ is in a dir named "subdir", even if "foo" is at the root of the current
transfer.
- A `!` specifies that the include/exclude should take effect if the pattern
- fails to match. For instance, "`-! */`" would exclude all non-directories.
+ fails to match. For instance, `-f'-! */'` would exclude all non-directories.
- A `C` is used to indicate that all the global CVS-exclude rules should be
inserted as excludes in place of the "-C". No arg should follow.
- An `s` is used to indicate that the rule applies to the sending side. When a
- rule affects the sending side, it prevents files from being transferred. The
- default is for a rule to affect both sides unless `--delete-excluded` was
- specified, in which case default rules become sender-side only. See also the
- hide (H) and show (S) rules, which are an alternate way to specify
- sending-side includes/excludes.
+ rule affects the sending side, it affects what files are put into the
+ sender's file list. The default is for a rule to affect both sides unless
+ [`--delete-excluded`](#opt) was specified, in which case default rules become
+ sender-side only. See also the hide (H) and show (S) rules, which are an
+ alternate way to specify sending-side includes/excludes.
- An `r` is used to indicate that the rule applies to the receiving side. When
a rule affects the receiving side, it prevents files from being deleted. See
the `s` modifier for more info. See also the protect (P) and risk (R) rules,
which are an alternate way to specify receiver-side includes/excludes.
- A `p` indicates that a rule is perishable, meaning that it is ignored in
- directories that are being deleted. For instance, the `-C` option's default
- rules that exclude things like "CVS" and "`*.o`" are marked as perishable,
- and will not prevent a directory that was removed on the source from being
- deleted on the destination.
+ directories that are being deleted. For instance, the
+ [`--cvs-exclude`](#opt) (`-C`) option's default rules that exclude things
+ like "CVS" and "`*.o`" are marked as perishable, and will not prevent a
+ directory that was removed on the source from being deleted on the
+ destination.
- An `x` indicates that a rule affects xattr names in xattr copy/delete
operations (and is thus ignored when matching file/dir names). If no
xattr-matching rules are specified, a default xattr filtering rule is used
- (see the `--xattrs` option).
+ (see the [`--xattrs`](#opt) option).
-# MERGE-FILE FILTER RULES
+### MERGE-FILE FILTER RULES
You can merge whole files into your filter rules by specifying either a merge
-(.) or a dir-merge (:) filter rule (as introduced in the FILTER RULES section
-above).
+(.) or a dir-merge (:) filter rule (as introduced in the [FILTER RULES](#)
+section above).
There are two kinds of merged files -- single-instance ('.') and per-directory
(':'). A single-instance merge file is read one time, and its rules are
@@ -3682,7 +4207,7 @@ list of inherited rules. These per-directory rule files must be created on the
sending side because it is the sending side that is being scanned for the
available files to transfer. These rule files may also need to be transferred
to the receiving side if you want them to affect what files don't get deleted
-(see PER-DIRECTORY RULES AND DELETE below).
+(see [PER-DIRECTORY RULES AND DELETE](#) below).
Some examples:
@@ -3753,7 +4278,7 @@ transfer).
If a per-directory merge-file is specified with a path that is a parent
directory of the first transfer directory, rsync will scan all the parent dirs
from that starting point to the transfer directory for the indicated
-per-directory file. For instance, here is a common filter (see `-F`):
+per-directory file. For instance, here is a common filter (see [`-F`](#opt)):
> --filter=': /.rsync-filter'
@@ -3777,11 +4302,11 @@ the ".rsync-filter" files in each directory that is a part of the transfer.
If you want to include the contents of a ".cvsignore" in your patterns, you
should use the rule ":C", which creates a dir-merge of the .cvsignore file, but
parsed in a CVS-compatible manner. You can use this to affect where the
-`--cvs-exclude` (`-C`) option's inclusion of the per-directory .cvsignore file
-gets placed into your rules by putting the ":C" wherever you like in your
-filter rules. Without this, rsync would add the dir-merge rule for the
-.cvsignore file at the end of all your other rules (giving it a lower priority
-than your command-line rules). For example:
+[`--cvs-exclude`](#opt) (`-C`) option's inclusion of the per-directory
+.cvsignore file gets placed into your rules by putting the ":C" wherever you
+like in your filter rules. Without this, rsync would add the dir-merge rule
+for the .cvsignore file at the end of all your other rules (giving it a lower
+priority than your command-line rules). For example:
> ```
> cat < Target file: /dest/you/bar/baz
> ```
-The easiest way to see what name you should filter is to just
-look at the output when using `--verbose` and put a / in front of the name
-(use the `--dry-run` option if you're not yet ready to copy any files).
+The easiest way to see what name you should filter is to just look at the
+output when using [`--verbose`](#opt) and put a / in front of the name (use the
+`--dry-run` option if you're not yet ready to copy any files).
-# PER-DIRECTORY RULES AND DELETE
+### PER-DIRECTORY RULES AND DELETE
Without a delete option, per-directory rules are only relevant on the sending
side, so you can feel free to exclude the merge files themselves without
@@ -3877,9 +4402,9 @@ for you, as seen in these two equivalent commands:
However, if you want to do a delete on the receiving side AND you want some
files to be excluded from being deleted, you'll need to be sure that the
receiving side knows what files to exclude. The easiest way is to include the
-per-directory merge files in the transfer and use `--delete-after`, because
-this ensures that the receiving side gets all the same exclude rules as the
-sending side before it tries to delete anything:
+per-directory merge files in the transfer and use [`--delete-after`](#opt),
+because this ensures that the receiving side gets all the same exclude rules as
+the sending side before it tries to delete anything:
> rsync -avF --delete-after host:src/dir /dest
@@ -3910,7 +4435,39 @@ one of these commands:
> rsync -avFF --delete host:src/dir /dest
> ```
-# BATCH MODE
+## TRANSFER RULES
+
+In addition to the [FILTER RULES](#) that affect the recursive file scans that
+generate the file list on the sending and (when deleting) receiving sides,
+there are transfer rules. These rules affect which files the generator decides
+need to be transferred without the side effects of an exclude filter rule.
+Transfer rules affect only files and never directories.
+
+Because a transfer rule does not affect what goes into the sender's (and
+receiver's) file list, it cannot have any effect on which files get deleted on
+the receiving side. For example, if the file "foo" is present in the sender's
+list but its size is such that it is omitted due to a transfer rule, the
+receiving side does not request the file. However, its presence in the file
+list means that a delete pass will not remove a matching file named "foo" on
+the receiving side. On the other hand, a server-side exclude (hide) of the
+file "foo" leaves the file out of the server's file list, and absent a
+receiver-side exclude (protect) the receiver will remove a matching file named
+"foo" if deletions are requested.
+
+Given that the files are still in the sender's file list, the
+[`--prune-empty-dirs`](#opt) option will not judge a directory as being empty
+even if it contains only files that the transfer rules omitted.
+
+Similarly, a transfer rule does not have any extra effect on which files are
+deleted on the receiving side, so setting a maximum file size for the transfer
+does not prevent big files from being deleted.
+
+Examples of transfer rules include the default "quick check" algorithm (which
+compares size & modify time), the [`--update`](#opt) option, the
+[`--max-size`](#opt) option, the [`--ignore-non-existing`](#opt) option, and a
+few others.
+
+## BATCH MODE
Batch mode can be used to apply the same set of updates to many identical
systems. Suppose one has a tree which is replicated on a number of hosts. Now
@@ -3963,10 +4520,10 @@ flexibility you have in how you deal with batches:
options when running the read-batch command on the remote host.
- The second example reads the batch data via standard input so that the batch
file doesn't need to be copied to the remote machine first. This example
- avoids the foo.sh script because it needed to use a modified `--read-batch`
- option, but you could edit the script file if you wished to make use of it
- (just be sure that no other option is trying to use standard input, such as
- the "`--exclude-from=-`" option).
+ avoids the foo.sh script because it needed to use a modified
+ [`--read-batch`](#opt) option, but you could edit the script file if you
+ wished to make use of it (just be sure that no other option is trying to use
+ standard input, such as the [`--exclude-from=-`](#opt) option).
Caveats:
@@ -3978,37 +4535,38 @@ already) or the file-update may be attempted and then, if the file fails to
verify, the update discarded with an error. This means that it should be safe
to re-run a read-batch operation if the command got interrupted. If you wish
to force the batched-update to always be attempted regardless of the file's
-size and date, use the `-I` option (when reading the batch). If an error
-occurs, the destination tree will probably be in a partially updated state. In
-that case, rsync can be used in its regular (non-batch) mode of operation to
-fix up the destination tree.
+size and date, use the [`-I`](#opt) option (when reading the batch). If an
+error occurs, the destination tree will probably be in a partially updated
+state. In that case, rsync can be used in its regular (non-batch) mode of
+operation to fix up the destination tree.
The rsync version used on all destinations must be at least as new as the one
used to generate the batch file. Rsync will die with an error if the protocol
version in the batch file is too new for the batch-reading rsync to handle.
-See also the `--protocol` option for a way to have the creating rsync generate
-a batch file that an older rsync can understand. (Note that batch files
-changed format in version 2.6.3, so mixing versions older than that with newer
-versions will not work.)
+See also the [`--protocol`](#opt) option for a way to have the creating rsync
+generate a batch file that an older rsync can understand. (Note that batch
+files changed format in version 2.6.3, so mixing versions older than that with
+newer versions will not work.)
When reading a batch file, rsync will force the value of certain options to
match the data in the batch file if you didn't set them to the same as the
batch-writing command. Other options can (and should) be changed. For
-instance `--write-batch` changes to `--read-batch`, `--files-from` is dropped,
-and the `--filter` / `--include` / `--exclude` options are not needed unless
-one of the `--delete` options is specified.
+instance [`--write-batch`](#opt) changes to [`--read-batch`](#opt),
+[`--files-from`](#opt) is dropped, and the [`--filter`](#opt) /
+[`--include`](#opt) / [`--exclude`](#opt) options are not needed unless one of
+the [`--delete`](#opt) options is specified.
The code that creates the BATCH.sh file transforms any filter/include/exclude
options into a single list that is appended as a "here" document to the shell
script file. An advanced user can use this to modify the exclude list if a
-change in what gets deleted by `--delete` is desired. A normal user can ignore
-this detail and just use the shell script as an easy way to run the appropriate
-`--read-batch` command for the batched data.
+change in what gets deleted by [`--delete`](#opt) is desired. A normal user
+can ignore this detail and just use the shell script as an easy way to run the
+appropriate [`--read-batch`](#opt) command for the batched data.
The original batch mode in rsync was based on "rsync+", but the latest
version uses a new implementation.
-# SYMBOLIC LINKS
+## SYMBOLIC LINKS
Three basic behaviors are possible when rsync encounters a symbolic
link in the source directory.
@@ -4016,40 +4574,53 @@ link in the source directory.
By default, symbolic links are not transferred at all. A message "skipping
non-regular" file is emitted for any symlinks that exist.
-If `--links` is specified, then symlinks are recreated with the same target on
-the destination. Note that `--archive` implies `--links`.
+If [`--links`](#opt) is specified, then symlinks are added to the transfer
+(instead of being noisily ignored), and the default handling is to recreate
+them with the same target on the destination. Note that [`--archive`](#opt)
+implies [`--links`](#opt).
-If `--copy-links` is specified, then symlinks are "collapsed" by
+If [`--copy-links`](#opt) is specified, then symlinks are "collapsed" by
copying their referent, rather than the symlink.
Rsync can also distinguish "safe" and "unsafe" symbolic links. An example
where this might be used is a web site mirror that wishes to ensure that the
rsync module that is copied does not include symbolic links to `/etc/passwd` in
-the public section of the site. Using `--copy-unsafe-links` will cause any
-links to be copied as the file they point to on the destination. Using
-`--safe-links` will cause unsafe links to be omitted altogether. (Note that you
-must specify `--links` for `--safe-links` to have any effect.)
+the public section of the site. Using [`--copy-unsafe-links`](#opt) will cause
+any links to be copied as the file they point to on the destination. Using
+[`--safe-links`](#opt) will cause unsafe links to be omitted by the receiver.
+(Note that you must specify or imply [`--links`](#opt) for
+[`--safe-links`](#opt) to have any effect.)
-Symbolic links are considered unsafe if they are absolute symlinks
-(start with `/`), empty, or if they contain enough ".."
-components to ascend from the directory being copied.
+Symbolic links are considered unsafe if they are absolute symlinks (start with
+`/`), empty, or if they contain enough ".." components to ascend from the top
+of the transfer.
Here's a summary of how the symlink options are interpreted. The list is in
order of precedence, so if your combination of options isn't mentioned, use the
first line that is a complete subset of your options:
-0. `--copy-links` Turn all symlinks into normal files (leaving no symlinks for
- any other options to affect).
-0. `--links --copy-unsafe-links` Turn all unsafe symlinks into files and
- duplicate all safe symlinks.
-0. `--copy-unsafe-links` Turn all unsafe symlinks into files, noisily skip all
- safe symlinks.
-0. `--links --safe-links` Duplicate safe symlinks and skip unsafe ones.
-0. `--links` Duplicate all symlinks.
+0. `--copy-links` Turn all symlinks into normal files and directories
+ (leaving no symlinks in the transfer for any other options to affect).
+0. `--copy-dirlinks` Turn just symlinks to directories into real
+ directories, leaving all other symlinks to be handled as described below.
+0. `--links --copy-unsafe-links` Turn all unsafe symlinks
+ into files and create all safe symlinks.
+0. `--copy-unsafe-links` Turn all unsafe symlinks into files, noisily
+ skip all safe symlinks.
+0. `--links --safe-links` The receiver skips creating
+ unsafe symlinks found in the transfer and creates the safe ones.
+0. `--links` Create all symlinks.
+
+For the effect of [`--munge-links`](#opt), see the discussion in that option's
+section.
+
+Note that the [`--keep-dirlinks`](#opt) option does not effect symlinks in the
+transfer but instead affects how rsync treats a symlink to a directory that
+already exists on the receiving side. See that option's section for a warning.
-# DIAGNOSTICS
+## DIAGNOSTICS
-rsync occasionally produces error messages that may seem a little cryptic. The
+Rsync occasionally produces error messages that may seem a little cryptic. The
one that seems to cause the most confusion is "protocol version mismatch -- is
your shell clean?".
@@ -4070,108 +4641,166 @@ If you are having trouble debugging filter patterns, then try specifying the
`-vv` option. At this level of verbosity rsync will show why each individual
file is included or excluded.
-# EXIT VALUES
-
-0. **0** Success
-0. **1** Syntax or usage error
-0. **2** Protocol incompatibility
-0. **3** Errors selecting input/output files, dirs
-0. **4** Requested action not supported: an attempt was made to manipulate
- 64-bit files on a platform that cannot support them; or an option was
- specified that is supported by the client and not by the server.
-0. **5** Error starting client-server protocol
-0. **6** Daemon unable to append to log-file
-0. **10** Error in socket I/O
-0. **11** Error in file I/O
-0. **12** Error in rsync protocol data stream
-0. **13** Errors with program diagnostics
-0. **14** Error in IPC code
-0. **20** Received SIGUSR1 or SIGINT
-0. **21** Some error returned by **waitpid()**
-0. **22** Error allocating core memory buffers
-0. **23** Partial transfer due to error
-0. **24** Partial transfer due to vanished source files
-0. **25** The --max-delete limit stopped deletions
-0. **30** Timeout in data send/receive
-0. **35** Timeout waiting for daemon connection
-
-# ENVIRONMENT VARIABLES
+## EXIT VALUES
+
+- **0** - Success
+- **1** - Syntax or usage error
+- **2** - Protocol incompatibility
+- **3** - Errors selecting input/output files, dirs
+- **4** - Requested action not supported. Either:
+ - an attempt was made to manipulate 64-bit files on a platform that cannot support them
+ - an option was specified that is supported by the client and not by the server
+- **5** - Error starting client-server protocol
+- **6** - Daemon unable to append to log-file
+- **10** - Error in socket I/O
+- **11** - Error in file I/O
+- **12** - Error in rsync protocol data stream
+- **13** - Errors with program diagnostics
+- **14** - Error in IPC code
+- **20** - Received SIGUSR1 or SIGINT
+- **21** - Some error returned by **waitpid()**
+- **22** - Error allocating core memory buffers
+- **23** - Partial transfer due to error
+- **24** - Partial transfer due to vanished source files
+- **25** - The --max-delete limit stopped deletions
+- **30** - Timeout in data send/receive
+- **35** - Timeout waiting for daemon connection
+
+## ENVIRONMENT VARIABLES
0. `CVSIGNORE`
The CVSIGNORE environment variable supplements any ignore patterns in
- .cvsignore files. See the `--cvs-exclude` option for more details.
+ .cvsignore files. See the [`--cvs-exclude`](#opt) option for more details.
0. `RSYNC_ICONV`
- Specify a default `--iconv` setting using this environment variable. (First
- supported in 3.0.0.)
+ Specify a default [`--iconv`](#opt) setting using this environment
+ variable. First supported in 3.0.0.
+
+0. `RSYNC_OLD_ARGS`
+
+ Specify a "1" if you want the [`--old-args`](#opt) option to be enabled by
+ default, a "2" (or more) if you want it to be enabled in the
+ repeated-option state, or a "0" to make sure that it is disabled by
+ default. When this environment variable is set to a non-zero value, it
+ supersedes the [`RSYNC_PROTECT_ARGS`](#) variable.
+
+ This variable is ignored if [`--old-args`](#opt), `--no-old-args`, or
+ [`--secluded-args`](#opt) is specified on the command line.
+
+ First supported in 3.2.4.
0. `RSYNC_PROTECT_ARGS`
- Specify a non-zero numeric value if you want the `--protect-args` option to
- be enabled by default, or a zero value to make sure that it is disabled by
- default. (First supported in 3.1.0.)
+ Specify a non-zero numeric value if you want the [`--secluded-args`](#opt)
+ option to be enabled by default, or a zero value to make sure that it is
+ disabled by default.
+
+ This variable is ignored if [`--secluded-args`](#opt), `--no-secluded-args`,
+ or [`--old-args`](#opt) is specified on the command line.
+
+ First supported in 3.1.0. Starting in 3.2.4, this variable is ignored if
+ [`RSYNC_OLD_ARGS`](#) is set to a non-zero value.
0. `RSYNC_RSH`
- The RSYNC_RSH environment variable allows you to override the default shell
- used as the transport for rsync. Command line options are permitted after
- the command name, just as in the `-e` option.
+ This environment variable allows you to override the default shell used as
+ the transport for rsync. Command line options are permitted after the
+ command name, just as in the [`--rsh`](#opt) (`-e`) option.
0. `RSYNC_PROXY`
- The RSYNC_PROXY environment variable allows you to redirect your rsync
- client to use a web proxy when connecting to a rsync daemon. You should
- set RSYNC_PROXY to a hostname:port pair.
+ This environment variable allows you to redirect your rsync
+ client to use a web proxy when connecting to an rsync daemon. You should
+ set `RSYNC_PROXY` to a hostname:port pair.
0. `RSYNC_PASSWORD`
- Setting RSYNC_PASSWORD to the required password allows you to run
- authenticated rsync connections to an rsync daemon without user
- intervention. Note that this does not supply a password to a remote shell
- transport such as ssh; to learn how to do that, consult the remote shell's
- documentation.
+ This environment variable allows you to set the password for an rsync
+ **daemon** connection, which avoids the password prompt. Note that this
+ does **not** supply a password to a remote shell transport such as ssh
+ (consult its documentation for how to do that).
0. `USER` or `LOGNAME`
The USER or LOGNAME environment variables are used to determine the default
username sent to an rsync daemon. If neither is set, the username defaults
- to "nobody".
+ to "nobody". If both are set, `USER` takes precedence.
+
+0. `RSYNC_PARTIAL_DIR`
+
+ This environment variable specifies the directory to use for a
+ [`--partial`](#opt) transfer without implying that partial transfers be
+ enabled. See the [`--partial-dir`](#opt) option for full details.
+
+0. `RSYNC_COMPRESS_LIST`
+
+ This environment variable allows you to customize the negotiation of the
+ compression algorithm by specifying an alternate order or a reduced list of
+ names. Use the command `rsync --version` to see the available compression
+ names. See the [`--compress`](#opt) option for full details.
+
+0. `RSYNC_CHECKSUM_LIST`
+
+ This environment variable allows you to customize the negotiation of the
+ checksum algorithm by specifying an alternate order or a reduced list of
+ names. Use the command `rsync --version` to see the available checksum
+ names. See the [`--checksum-choice`](#opt) option for full details.
+
+0. `RSYNC_MAX_ALLOC`
+
+ This environment variable sets an allocation maximum as if you had used the
+ [`--max-alloc`](#opt) option.
+
+0. `RSYNC_PORT`
+
+ This environment variable is not read by rsync, but is instead set in
+ its sub-environment when rsync is running the remote shell in combination
+ with a daemon connection. This allows a script such as
+ [`rsync-ssl`](rsync-ssl.1) to be able to know the port number that the user
+ specified on the command line.
0. `HOME`
- The HOME environment variable is used to find the user's default .cvsignore
+ This environment variable is used to find the user's default .cvsignore
file.
-# FILES
+0. `RSYNC_CONNECT_PROG`
-/etc/rsyncd.conf or rsyncd.conf
+ This environment variable is mainly used in debug setups to set the program
+ to use when making a daemon connection. See [CONNECTING TO AN RSYNC
+ DAEMON](#) for full details.
-# SEE ALSO
+0. `RSYNC_SHELL`
-**rsync-ssl**(1), **rsyncd.conf**(5)
+ This environment variable is mainly used in debug setups to set the program
+ to use to run the program specified by [`RSYNC_CONNECT_PROG`](#). See
+ [CONNECTING TO AN RSYNC DAEMON](#) for full details.
-# BUGS
+## FILES
-times are transferred as \*nix time_t values
+/etc/rsyncd.conf or rsyncd.conf
-When transferring to FAT filesystems rsync may re-sync
-unmodified files.
-See the comments on the `--modify-window` option.
+## SEE ALSO
-file permissions, devices, etc. are transferred as native numerical
-values
+[**rsync-ssl**(1)](rsync-ssl.1), [**rsyncd.conf**(5)](rsyncd.conf.5), [**rrsync**(1)](rrsync.1)
-see also the comments on the `--delete` option
+## BUGS
+
+- Times are transferred as \*nix time_t values.
+- When transferring to FAT filesystems rsync may re-sync unmodified files. See
+ the comments on the [`--modify-window`](#opt) option.
+- File permissions, devices, etc. are transferred as native numerical values.
+- See also the comments on the [`--delete`](#opt) option.
Please report bugs! See the web site at .
-# VERSION
+## VERSION
-This man page is current for version @VERSION@ of rsync.
+This manpage is current for version @VERSION@ of rsync.
-# INTERNAL OPTIONS
+## INTERNAL OPTIONS
The options `--server` and `--sender` are used internally by rsync, and should
never be typed by a user under normal circumstances. Some awareness of these
@@ -4180,13 +4809,16 @@ that can only run an rsync command. For instance, the support directory of the
rsync distribution has an example script named rrsync (for restricted rsync)
that can be used with a restricted ssh login.
-# CREDITS
+## CREDITS
+
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
-rsync is distributed under the GNU General Public License. See the file
-COPYING for details.
+An rsync web site is available at . The site
+includes an FAQ-O-Matic which may cover questions unanswered by this manual
+page.
-A web site is available at . The site includes an
-FAQ-O-Matic which may cover questions unanswered by this manual page.
+The rsync github project is .
We would be delighted to hear from you if you like this program. Please
contact the mailing-list at .
@@ -4194,7 +4826,7 @@ contact the mailing-list at .
This program uses the excellent zlib compression library written by Jean-loup
Gailly and Mark Adler.
-# THANKS
+## THANKS
Special thanks go out to: John Van Essen, Matt McCutchen, Wesley W. Terpstra,
David Dykstra, Jos Backus, Sebastian Krahmer, Martin Pool, and our
@@ -4203,11 +4835,10 @@ gone-but-not-forgotten compadre, J.W. Schultz.
Thanks also to Richard Brent, Brendan Mackay, Bill Waite, Stephen Rothwell and
David Bell. I've probably missed some people, my apologies if I have.
-# AUTHOR
+## AUTHOR
-rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
-people have later contributed to it. It is currently maintained by Wayne
-Davison.
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people from around the world have helped to maintain and improve it.
Mailing lists for support and development are available at
.
diff --git a/rsync.c b/rsync.c
index 60029c2d8..b130aba56 100644
--- a/rsync.c
+++ b/rsync.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -437,7 +437,10 @@ int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, cha
*/
void free_sums(struct sum_struct *s)
{
- if (s->sums) free(s->sums);
+ if (s->sums) {
+ free(s->sums);
+ free(s->sum2_array);
+ }
free(s);
}
@@ -642,7 +645,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
#ifdef SUPPORT_ACLS
/* It's OK to call set_acl() now, even for a dir, as the generator
* will enable owner-writability using chmod, if necessary.
- *
+ *
* If set_acl() changes permission bits in the process of setting
* an access ACL, it changes sxp->st.st_mode so we know whether we
* need to chmod(). */
diff --git a/rsync.h b/rsync.h
index a6c0d1bb3..479ac4848 100644
--- a/rsync.h
+++ b/rsync.h
@@ -2,7 +2,7 @@
* Copyright (C) 1996, 2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,11 +18,6 @@
* with this program; if not, visit the http://fsf.org website.
*/
-/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
- incompatible with older versions :-( */
-#define CHAR_OFFSET 0
-
-#ifndef AVX2_ASM /* do not include the rest of file for assembly */
#define False 0
#define True 1
#define Unset (-1) /* Our BOOL values are always an int. */
@@ -43,6 +38,9 @@
#define BACKUP_SUFFIX "~"
+/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
+ incompatible with older versions :-( */
+#define CHAR_OFFSET 0
/* These flags are only used during the flist transfer. */
@@ -94,6 +92,7 @@
#define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */
#define FLAG_TIME_FAILED (1<<11)/* generator */
#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */
+#define FLAG_GOT_DIR_FLIST (1<<13)/* sender/receiver/generator - dir_flist only */
/* These flags are passed to functions but not stored. */
@@ -112,7 +111,7 @@
/* Update this if you make incompatible changes and ALSO update the
* SUBPROTOCOL_VERSION if it is not a final (official) release. */
-#define PROTOCOL_VERSION 31
+#define PROTOCOL_VERSION 32
/* This is used when working on a new protocol version or for any unofficial
* protocol tweaks. It should be a non-zero value for each pre-release repo
@@ -340,6 +339,9 @@ enum delret {
# endif
# include
#endif
+#ifdef HAVE_BSD_STRING_H
+# include
+#endif
#ifdef HAVE_STRINGS_H
# include
#endif
@@ -365,16 +367,10 @@ enum delret {
#include
#endif
-#ifdef TIME_WITH_SYS_TIME
-#include
-#include
-#else
#ifdef HAVE_SYS_TIME_H
#include
-#else
-#include
-#endif
#endif
+#include
#ifdef HAVE_FCNTL_H
#include
@@ -780,6 +776,11 @@ struct ht_int64_node {
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define USE_FLEXIBLE_ARRAY 1
+#define SIZE_T_FMT_MOD "z" /* printf supports %zd */
+#define SIZE_T_FMT_CAST size_t
+#else
+#define SIZE_T_FMT_MOD "l" /* printf supports %ld */
+#define SIZE_T_FMT_CAST long
#endif
union file_extras {
@@ -820,6 +821,7 @@ extern int uid_ndx;
extern int gid_ndx;
extern int acls_ndx;
extern int xattrs_ndx;
+extern int file_sum_extra_cnt;
#ifdef USE_FLEXIBLE_ARRAY
#define FILE_STRUCT_LEN (sizeof (struct file_struct))
@@ -830,7 +832,7 @@ extern int xattrs_ndx;
#define DEV_EXTRA_CNT 2
#define DIRNODE_EXTRA_CNT 3
#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN)
-#define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
+#define SUM_EXTRA_CNT file_sum_extra_cnt
#define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
#define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
@@ -957,12 +959,12 @@ struct sum_buf {
uint32 sum1; /**< simple checksum */
int32 chain; /**< next hash-table collision */
short flags; /**< flag bits */
- char sum2[SUM_LENGTH]; /**< checksum */
};
struct sum_struct {
OFF_T flength; /**< total file length */
struct sum_buf *sums; /**< points to info for each chunk */
+ char *sum2_array; /**< checksums of length xfer_sum_len */
int32 count; /**< how many chunks */
int32 blength; /**< block_length */
int32 remainder; /**< flength % block_length */
@@ -981,6 +983,8 @@ struct map_struct {
int status; /* first errno from read errors */
};
+#define sum2_at(s, i) ((s)->sum2_array + ((size_t)(i) * xfer_sum_len))
+
#define NAME_IS_FILE (0) /* filter name as a file */
#define NAME_IS_DIR (1<<0) /* filter name as a dir */
#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
@@ -1017,6 +1021,7 @@ typedef struct filter_struct {
int slash_cnt;
struct filter_list_struct *mergelist;
} u;
+ uchar elide;
} filter_rule;
typedef struct filter_list_struct {
@@ -1156,19 +1161,23 @@ typedef struct {
#define NSTR_COMPRESS 1
struct name_num_item {
- int num;
- const char *name, *main_name;
+ int num, flags;
+ const char *name;
+ struct name_num_item *main_nni;
};
struct name_num_obj {
const char *type;
- const char *negotiated_name;
+ struct name_num_item *negotiated_nni;
uchar *saw;
int saw_len;
- int negotiated_num;
- struct name_num_item list[10]; /* we'll get a compile error/warning if this is ever too small */
+ struct name_num_item *list;
};
+#ifdef EXTERNAL_ZLIB
+#define read_buf read_buf_
+#endif
+
#ifndef __cplusplus
#include "proto.h"
#endif
@@ -1416,7 +1425,8 @@ extern short info_levels[], debug_levels[];
#define INFO_MISC (INFO_FLIST+1)
#define INFO_MOUNT (INFO_MISC+1)
#define INFO_NAME (INFO_MOUNT+1)
-#define INFO_PROGRESS (INFO_NAME+1)
+#define INFO_NONREG (INFO_NAME+1)
+#define INFO_PROGRESS (INFO_NONREG+1)
#define INFO_REMOVE (INFO_PROGRESS+1)
#define INFO_SKIP (INFO_REMOVE+1)
#define INFO_STATS (INFO_SKIP+1)
@@ -1471,4 +1481,9 @@ const char *get_panic_action(void);
fprintf(stderr, "%s in %s at line %d\n", msg, __FILE__, __LINE__); \
exit_cleanup(RERR_UNSUPPORTED); \
} while (0)
-#endif /* AVX2_ASM */
+
+#ifdef HAVE_MALLINFO2
+#define MEM_ALLOC_INFO mallinfo2
+#elif defined HAVE_MALLINFO
+#define MEM_ALLOC_INFO mallinfo
+#endif
diff --git a/rsyncd.conf.5.md b/rsyncd.conf.5.md
index 730ef71e1..2f257659d 100644
--- a/rsyncd.conf.5.md
+++ b/rsyncd.conf.5.md
@@ -1,12 +1,15 @@
-# NAME
+## NAME
rsyncd.conf - configuration file for rsync in daemon mode
-# SYNOPSIS
+## SYNOPSIS
rsyncd.conf
-# DESCRIPTION
+The online version of this manpage (that includes cross-linking of topics)
+is available at .
+
+## DESCRIPTION
The rsyncd.conf file is the runtime configuration file for rsync when run as an
rsync daemon.
@@ -14,7 +17,7 @@ rsync daemon.
The rsyncd.conf file controls authentication, access, logging and available
modules.
-# FILE FORMAT
+## FILE FORMAT
The file consists of modules and parameters. A module begins with the name of
the module in square brackets and continues until the next module begins.
@@ -40,10 +43,9 @@ The values following the equals sign in parameters are all either a string (no
quotes needed) or a boolean, which may be given as yes/no, 0/1 or true/false.
Case is not significant in boolean values, but is preserved in string values.
-# LAUNCHING THE RSYNC DAEMON
+## LAUNCHING THE RSYNC DAEMON
-The rsync daemon is launched by specifying the `--daemon` option to
-rsync.
+The rsync daemon is launched by specifying the `--daemon` option to rsync.
The daemon must run with root privileges if you wish to use chroot, to bind to
a port numbered under 1024 (as is the default 873), or to set file ownership.
@@ -69,36 +71,18 @@ reread its config file.
Note that you should **not** send the rsync daemon a HUP signal to force it to
reread the `rsyncd.conf` file. The file is re-read on each client connection.
-# GLOBAL PARAMETERS
+## GLOBAL PARAMETERS
The first parameters in the file (before a [module] header) are the global
-parameters. Rsync also allows for the use of a "[global]" module name to
-indicate the start of one or more global-parameter sections (the name must be
-lower case).
-
-You may also include any module parameters in the global part of the config
-file in which case the supplied value will override the default for that
-parameter.
-
-You may use references to environment variables in the values of parameters.
-String parameters will have %VAR% references expanded as late as possible (when
-the string is first used in the program), allowing for the use of variables
-that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
-parameters (such as true/false settings) are expanded when read from the config
-file. If a variable does not exist in the environment, or if a sequence of
-characters is not a valid reference (such as an un-paired percent sign), the
-raw characters are passed through unchanged. This helps with backward
-compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
-string in a path could result in a very unsafe path). The safest way to insert
-a literal % into a value is to use %%.
+parameters:
[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
0. `motd file`
- This parameter allows you to specify a "message of the day" to display to
- clients on each connect. This usually contains site information and any
- legal notices. The default is no motd file. This can be overridden by the
+ This parameter allows you to specify a "message of the day" (MOTD) to display
+ to clients on each connect. This usually contains site information and any
+ legal notices. The default is no MOTD file. This can be overridden by the
`--dparam=motdfile=FILE` command-line option when starting the daemon.
0. `pid file`
@@ -126,7 +110,7 @@ a literal % into a value is to use %%.
This parameter can provide endless fun for people who like to tune their
systems to the utmost degree. You can set all sorts of socket options which
- may make transfers faster (or slower!). Read the man page for the
+ may make transfers faster (or slower!). Read the manpage for the
**setsockopt()** system call for details on some of the options you may be
able to set. By default no special socket options are set. These settings
can also be specified via the `--sockopts` command-line option.
@@ -136,7 +120,23 @@ a literal % into a value is to use %%.
You can override the default backlog value when the daemon listens for
connections. It defaults to 5.
-# MODULE PARAMETERS
+You may also include any [MODULE PARAMETERS](#) in the global part of the
+config file, in which case the supplied value will override the default for
+that parameter.
+
+You may use references to environment variables in the values of parameters.
+String parameters will have %VAR% references expanded as late as possible (when
+the string is first used in the program), allowing for the use of variables
+that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
+parameters (such as true/false settings) are expanded when read from the config
+file. If a variable does not exist in the environment, or if a sequence of
+characters is not a valid reference (such as an un-paired percent sign), the
+raw characters are passed through unchanged. This helps with backward
+compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
+string in a path could result in a very unsafe path). The safest way to insert
+a literal % into a value is to use %%.
+
+## MODULE PARAMETERS
After the global parameters you should define a number of modules, each module
exports a directory tree as a symbolic name. Modules are exported by specifying
@@ -144,11 +144,17 @@ a module name in square brackets [module] followed by the parameters for that
module. The module name cannot contain a slash or a closing square bracket.
If the name contains whitespace, each internal sequence of whitespace will be
changed into a single space, while leading or trailing whitespace will be
-discarded. Also, the name cannot be "global" as that exact name indicates that
-global parameters follow (see above).
+discarded.
-As with GLOBAL PARAMETERS, you may use references to environment variables in
-the values of parameters. See the GLOBAL PARAMETERS section for more details.
+There is also a special module name of "[global]" that does not define a module
+but instead switches back to the global settings context where default
+parameters can be specified. Because each defined module gets its full set of
+parameters as a combination of the default values that are set at that position
+in the config file plus its own parameter list, the use of a "[global]" section
+can help to maintain shared config values for multiple modules.
+
+As with [GLOBAL PARAMETERS](#), you may use references to environment variables
+in the values of parameters. See that section for details.
0. `comment`
@@ -162,6 +168,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
available in this module. You must specify this parameter for each module
in `rsyncd.conf`.
+ If the value contains a "/./" element then the path will be divided at that
+ point into a chroot dir and an inner-chroot subdir. If [`use chroot`](#)
+ is set to false, though, the extraneous dot dir is just cleaned out of the
+ path. An example of this idiom is:
+
+ > path = /var/rsync/./module1
+
+ This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot
+ path to "/module1".
+
You may base the path's value off of an environment variable by surrounding
the variable name with percent signs. You can even reference a variable
that is set by rsync when the user connects. For example, this would use
@@ -177,7 +193,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
0. `use chroot`
- If "use chroot" is true, the rsync daemon will chroot to the "path" before
+ If "use chroot" is true, the rsync daemon will chroot to the "[path](#)" before
starting the file transfer with the client. This has the advantage of
extra protection against possible implementation security holes, but it has
the disadvantages of requiring super-user privileges, of not being able to
@@ -185,30 +201,48 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
path, and of complicating the preservation of users and groups by name (see
below).
- As an additional safety feature, you can specify a dot-dir in the module's
- "path" to indicate the point where the chroot should occur. This allows
- rsync to run in a chroot with a non-"/" path for the top of the transfer
- hierarchy. Doing this guards against unintended library loading (since
- those absolute paths will not be inside the transfer hierarchy unless you
- have used an unwise pathname), and lets you setup libraries for the chroot
- that are outside of the transfer. For example, specifying
- "/var/rsync/./module1" will chroot to the "/var/rsync" directory and set
- the inside-chroot path to "/module1". If you had omitted the dot-dir, the
- chroot would have used the whole path, and the inside-chroot path would
- have been "/".
-
- When both "use chroot" and "daemon chroot" are false, OR the inside-chroot
- path of "use chroot" is not "/", rsync will: (1) munge symlinks by default
- for security reasons (see "munge symlinks" for a way to turn this off, but
- only if you trust your users), (2) substitute leading slashes in absolute
- paths with the module's path (so that options such as `--backup-dir`,
- `--compare-dest`, etc. interpret an absolute path as rooted in the module's
- "path" dir), and (3) trim ".." path elements from args if rsync believes
- they would escape the module hierarchy. The default for "use chroot" is
- true, and is the safer choice (especially if the module is not read-only).
-
- When this parameter is enabled *and* the "name converter" parameter is
- *not* set, the "numeric ids" parameter will default to being enabled
+ If `use chroot` is not set, it defaults to trying to enable a chroot but
+ allows the daemon to continue (after logging a warning) if it fails. The
+ one exception to this is when a module's [`path`](#) has a "/./" chroot
+ divider in it -- this causes an unset value to be treated as true for that
+ module.
+
+ Prior to rsync 3.2.7, the default value was "true". The new "unset"
+ default makes it easier to setup an rsync daemon as a non-root user or to
+ run a daemon on a system where chroot fails. Explicitly setting the value
+ to "true" in rsyncd.conf will always require the chroot to succeed.
+
+ It is also possible to specify a dot-dir in the module's "[path](#)" to
+ indicate that you want to chdir to the earlier part of the path and then
+ serve files from inside the latter part of the path (with sanitizing and
+ default symlink munging). This can be useful if you need some library dirs
+ inside the chroot (typically for uid & gid lookups) but don't want to put
+ the lib dir into the top of the served path (even though they can be hidden
+ with an [`exclude`](#) directive). However, a better choice for a modern
+ rsync setup is to use a [`name converter`](#)" and try to avoid inner lib
+ dirs altogether. See also the [`daemon chroot`](#) parameter, which causes
+ rsync to chroot into its own chroot area before doing any path-related
+ chrooting.
+
+ If the daemon is serving the "/" dir (either directly or due to being
+ chrooted to the module's path), rsync does not do any path sanitizing or
+ (default) munging.
+
+ When it has to limit access to a particular subdir (either due to chroot
+ being disabled or having an inside-chroot path set), rsync will munge
+ symlinks (by default) and sanitize paths. Those that dislike munged
+ symlinks (and really, really trust their users to not break out of the
+ subdir) can disable the symlink munging via the "[munge symlinks](#)"
+ parameter.
+
+ When rsync is sanitizing paths, it trims ".." path elements from args that
+ it believes would escape the module hierarchy. It also substitutes leading
+ slashes in absolute paths with the module's path (so that options such as
+ `--backup-dir` & `--compare-dest` interpret an absolute path as rooted in
+ the module's "[path](#)" dir).
+
+ When a chroot is in effect *and* the "[name converter](#)" parameter is
+ *not* set, the "[numeric ids](#)" parameter will default to being enabled
(disabling name lookups). This means that if you manually setup
name-lookup libraries in your chroot (instead of using a name converter)
that you need to explicitly set `numeric ids = false` for rsync to do name
@@ -217,16 +251,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
If you copy library resources into the module's chroot area, you should
protect them through your OS's normal user/group or ACL settings (to
prevent the rsync module's user from being able to change them), and then
- hide them from the user's view via "exclude" (see how in the discussion of
+ hide them from the user's view via "[exclude](#)" (see how in the discussion of
that parameter). However, it's easier and safer to setup a name converter.
0. `daemon chroot`
This parameter specifies a path to which the daemon will chroot before
- beginning communication with clients. Module paths (and any "use chroot"
+ beginning communication with clients. Module paths (and any "[use chroot](#)"
settings) will then be related to this one. This lets you choose if you
want the whole daemon to be chrooted (with this setting), just the
- transfers to be chrooted (with "use chroot"), or both. Keep in mind that
+ transfers to be chrooted (with "[use chroot](#)"), or both. Keep in mind that
the "daemon chroot" area may need various OS/lib/etc files installed to
allow the daemon to function. By default the daemon runs without any
chrooting.
@@ -284,11 +318,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
transfer behave as if the client had passed the `--numeric-ids`
command-line option. By default, this parameter is enabled for chroot
modules and disabled for non-chroot modules. Also keep in mind that
- uid/gid preservation requires the module to be running as root (see "uid")
- or for "fake super" to be configured.
+ uid/gid preservation requires the module to be running as root (see "[uid](#)")
+ or for "[fake super](#)" to be configured.
A chroot-enabled module should not have this parameter set to false unless
- you're using a "name converter" program *or* you've taken steps to ensure
+ you're using a "[name converter](#)" program *or* you've taken steps to ensure
that the module has the necessary resources it needs to translate names and
that it is not possible for a user to change those resources.
@@ -298,12 +332,12 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
(non-daemon-affecting) `--munge-links` command-line option (using a method
described below). This should help protect your files from user trickery
when your daemon module is writable. The default is disabled when
- "use chroot" is on with an inside-chroot path of "/", OR if "daemon chroot"
+ "[use chroot](#)" is on with an inside-chroot path of "/", OR if "[daemon chroot](#)"
is on, otherwise it is enabled.
If you disable this parameter on a daemon that is not read-only, there are
tricks that a user can play with uploaded symlinks to access
- daemon-excluded items (if your module has any), and, if "use chroot" is
+ daemon-excluded items (if your module has any), and, if "[use chroot](#)" is
off, rsync can even be tricked into showing or changing data that is
outside the module's path (as access-permissions allow).
@@ -324,7 +358,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
the source code named "munge-symlinks" that can be used to add or remove
this prefix from your symlinks.
- When this parameter is disabled on a writable module and "use chroot" is
+ When this parameter is disabled on a writable module and "[use chroot](#)" is
off (or the inside-chroot path is not "/"), incoming symlinks will be
modified to drop a leading slash and to remove ".." path elements that
rsync believes will allow a symlink to escape the module's hierarchy.
@@ -340,10 +374,10 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
conversion in a chroot module without extra files in the chroot area, and
also ensures that name-translation is done in a consistent manner. If the
"charset" parameter is not set, the `--iconv` option is refused, just as if
- "iconv" had been specified via "refuse options".
+ "iconv" had been specified via "[refuse options](#)".
If you wish to force users to always use `--iconv` for a particular module,
- add "no-iconv" to the "refuse options" parameter. Keep in mind that this
+ add "no-iconv" to the "[refuse options](#)" parameter. Keep in mind that this
will restrict access to your module to very new rsync clients.
0. `max connections`
@@ -352,7 +386,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
connections you will allow. Any clients connecting when the maximum has
been reached will receive a message telling them to try later. The default
is 0, which means no limit. A negative value disables the module. See
- also the "lock file" parameter.
+ also the "[lock file](#)" parameter.
0. `log file`
@@ -381,7 +415,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
facility name which is defined on your system. Common names are auth,
authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, syslog, user,
uucp, local0, local1, local2, local3, local4, local5, local6 and local7.
- The default is daemon. This setting has no effect if the "log file"
+ The default is daemon. This setting has no effect if the "[log file](#)"
setting is a non-empty string (either set in the per-modules settings, or
inherited from the global settings).
@@ -389,7 +423,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter allows you to specify the syslog tag to use when logging
messages from the rsync daemon. The default is "rsyncd". This setting has
- no effect if the "log file" setting is a non-empty string (either set in
+ no effect if the "[log file](#)" setting is a non-empty string (either set in
the per-modules settings, or inherited from the global settings).
For example, if you wanted each authenticated user's name to be included in
@@ -414,7 +448,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
0. `lock file`
- This parameter specifies the file to use to support the "max connections"
+ This parameter specifies the file to use to support the "[max connections](#)"
parameter. The rsync daemon uses record locking on this file to ensure that
the max connections limit is not exceeded for the modules sharing the lock
file. The default is `/var/run/rsyncd.lock`.
@@ -426,7 +460,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
"read only" is false then uploads will be possible if file permissions on
the daemon side allow them. The default is for all modules to be read only.
- Note that "auth users" can override this setting on a per-user basis.
+ Note that "[auth users](#)" can override this setting on a per-user basis.
0. `write only`
@@ -460,8 +494,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter determines whether this module is listed when the client
asks for a listing of available modules. In addition, if this is false,
the daemon will pretend the module does not exist when a client denied by
- "hosts allow" or "hosts deny" attempts to access it. Realize that if
- "reverse lookup" is disabled globally but enabled for the module, the
+ "[hosts allow](#)" or "[hosts deny](#)" attempts to access it. Realize that if
+ "[reverse lookup](#)" is disabled globally but enabled for the module, the
resulting reverse lookup to a potentially client-controlled DNS server may
still reveal to the client that it hit an existing module. The default is
for modules to be listable.
@@ -470,10 +504,10 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter specifies the user name or user ID that file transfers to
and from that module should take place as when the daemon was run as root.
- In combination with the "gid" parameter this determines what file
+ In combination with the "[gid](#)" parameter this determines what file
permissions are available. The default when run by a super-user is to
switch to the system's "nobody" user. The default for a non-super-user is
- to not try to change the user. See also the "gid" parameter.
+ to not try to change the user. See also the "[gid](#)" parameter.
The RSYNC_USER_NAME environment variable may be used to request that rsync
run as the authorizing user. For example, if you want a rsync to run as
@@ -489,7 +523,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
accessing the module. The first one will be the default group, and any
extra ones be set as supplemental groups. You may also specify a "`*`" as
the first gid in the list, which will be replaced by all the normal groups
- for the transfer's user (see "uid"). The default when run by a super-user
+ for the transfer's user (see "[uid](#)"). The default when run by a super-user
is to switch to your OS's "nobody" (or perhaps "nogroup") group with no
other supplementary groups. The default for a non-super-user is to not
change any group attributes (and indeed, your OS may not allow a
@@ -505,13 +539,13 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter specifies a uid under which the daemon will run. The daemon
usually runs as user root, and when this is left unset the user is left
- unchanged. See also the "uid" parameter.
+ unchanged. See also the "[uid](#)" parameter.
0. `daemon gid`
This parameter specifies a gid under which the daemon will run. The daemon
usually runs as group root, and when this is left unset, the group is left
- unchanged. See also the "gid" parameter.
+ unchanged. See also the "[gid](#)" parameter.
0. `fake super`
@@ -532,8 +566,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
or tampering with private administrative files, such as files you may add
to support uid/gid name translations.
- The daemon filter chain is built from the "filter", "include from",
- "include", "exclude from", and "exclude" parameters, in that order of
+ The daemon filter chain is built from the "filter", "[include from](#)",
+ "[include](#)", "[exclude from](#)", and "[exclude](#)" parameters, in that order of
priority. Anchored patterns are anchored at the root of the module. To
prevent access to an entire subtree, for example, "`/secret`", you **must**
exclude everything in the subtree; the easiest way to do this is with a
@@ -560,8 +594,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
0. `include`
- Use an "include" to override the effects of the "exclude" parameter. Only
- one "include" parameter can apply to a given module. See the "filter"
+ Use an "include" to override the effects of the "[exclude](#)" parameter. Only
+ one "include" parameter can apply to a given module. See the "[filter](#)"
parameter for a description of how excluded files affect the daemon.
0. `exclude from`
@@ -569,14 +603,14 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter specifies the name of a file on the daemon that contains
daemon exclude patterns, one per line. Only one "exclude from" parameter
can apply to a given module; if you have multiple exclude-from files, you
- can specify them as a merge file in the "filter" parameter. See the
- "filter" parameter for a description of how excluded files affect the
+ can specify them as a merge file in the "[filter](#)" parameter. See the
+ "[filter](#)" parameter for a description of how excluded files affect the
daemon.
0. `include from`
- Analogue of "exclude from" for a file of daemon include patterns. Only one
- "include from" parameter can apply to a given module. See the "filter"
+ Analogue of "[exclude from](#)" for a file of daemon include patterns. Only one
+ "include from" parameter can apply to a given module. See the "[filter](#)"
parameter for a description of how excluded files affect the daemon.
0. `incoming chmod`
@@ -611,7 +645,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
to supply a username and password to connect to the module. A challenge
response authentication protocol is used for this exchange. The plain text
usernames and passwords are stored in the file specified by the
- "secrets file" parameter. The default is for all users to be able to
+ "[secrets file](#)" parameter. The default is for all users to be able to
connect without a password (this is called "anonymous rsync").
In addition to username matching, you can specify groupname matching via a
@@ -623,7 +657,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
Finally, options may be specified after a colon (:). The options allow you
to "deny" a user or a group, set the access to "ro" (read-only), or set the
access to "rw" (read/write). Setting an auth-rule-specific ro/rw setting
- overrides the module's "read only" setting.
+ overrides the module's "[read only](#)" setting.
Be sure to put the rules in the order you want them to be matched, because
the checking stops at the first matching user or group, and that is the
@@ -661,7 +695,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter specifies the name of a file that contains the
username:password and/or @groupname:password pairs used for authenticating
- this module. This file is only consulted if the "auth users" parameter is
+ this module. This file is only consulted if the "[auth users](#)" parameter is
specified. The file is line-based and contains one name:password pair per
line. Any line has a hash (#) as the very first character on the line is
considered a comment and is skipped. The passwords can contain any
@@ -675,14 +709,14 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
"@groupname:password" line for the group that triggered the authentication.
It is up to you what kind of password entries you want to include, either
- users, groups, or both. The use of group rules in "auth users" does not
+ users, groups, or both. The use of group rules in "[auth users](#)" does not
require that you specify a group password if you do not want to use shared
passwords.
There is no default for the "secrets file" parameter, you must choose a
name (such as `/etc/rsyncd.secrets`). The file must normally not be
- readable by "other"; see "strict modes". If the file is not found or is
- rejected, no logins for a "user auth" module will be possible.
+ readable by "other"; see "[strict modes](#)". If the file is not found or is
+ rejected, no logins for an "[auth users](#)" module will be possible.
0. `strict modes`
@@ -714,11 +748,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
addresses which match the masked IP address will be allowed in.
- a hostname pattern using wildcards. If the hostname of the connecting IP
(as determined by a reverse lookup) matches the wildcarded name (using
- the same rules as normal unix filename matching), the client is allowed
- in. This only works if "reverse lookup" is enabled (the default).
+ the same rules as normal Unix filename matching), the client is allowed
+ in. This only works if "[reverse lookup](#)" is enabled (the default).
- a hostname. A plain hostname is matched against the reverse DNS of the
- connecting IP (if "reverse lookup" is enabled), and/or the IP of the
- given hostname is matched against the connecting IP (if "forward lookup"
+ connecting IP (if "[reverse lookup](#)" is enabled), and/or the IP of the
+ given hostname is matched against the connecting IP (if "[forward lookup](#)"
is enabled, as it is by default). Any match will be allowed in.
- an '@' followed by a netgroup name, which will match if the reverse DNS
of the connecting IP is in the specified netgroup.
@@ -730,11 +764,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
> fe80::%link1/64
> fe80::%link1/ffff:ffff:ffff:ffff::
- You can also combine "hosts allow" with "hosts deny" as a way to add
+ You can also combine "hosts allow" with "[hosts deny](#)" as a way to add
exceptions to your deny list. When both parameters are specified, the
"hosts allow" parameter is checked first and a match results in the client
being able to connect. A non-allowed host is then matched against the
- "hosts deny" list to see if it should be rejected. A host that does not
+ "[hosts deny](#)" list to see if it should be rejected. A host that does not
match either list is allowed to connect.
The default is no "hosts allow" parameter, which means all hosts can
@@ -745,7 +779,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
This parameter allows you to specify a list of comma- and/or
whitespace-separated patterns that are matched against a connecting clients
hostname and IP address. If the pattern matches then the connection is
- rejected. See the "hosts allow" parameter for more information.
+ rejected. See the "[hosts allow](#)" parameter for more information.
The default is no "hosts deny" parameter, which means all hosts can
connect.
@@ -753,8 +787,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
0. `reverse lookup`
Controls whether the daemon performs a reverse lookup on the client's IP
- address to determine its hostname, which is used for "hosts allow" &
- "hosts deny" checks and the "%h" log escape. This is enabled by default,
+ address to determine its hostname, which is used for "[hosts allow](#)" &
+ "[hosts deny](#)" checks and the "%h" log escape. This is enabled by default,
but you may wish to disable it to save time if you know the lookup will not
return a useful result, in which case the daemon will use the name
"UNDETERMINED" instead.
@@ -794,7 +828,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
logs the transfer at the end, so if a transfer is aborted, no mention will
be made in the log file.
- If you want to customize the log lines, see the "log format" parameter.
+ If you want to customize the log lines, see the "[log format](#)" parameter.
0. `log format`
@@ -811,7 +845,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
(e.g. "`%''l %'b %f`").
The default log format is "`%o %h [%a] %m (%u) %f %l`", and a "`%t [%p] `"
- is always prefixed when using the "log file" parameter. (A perl script
+ is always prefixed when using the "[log file](#)" parameter. (A perl script
that will summarize this default log format is included in the rsync source
code distribution in the "support" subdirectory: rsyncstats.)
@@ -892,7 +926,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
> refuse options = * !a !v !compress*
Don't worry that the "`*`" will refuse certain vital options such as
- `--dry-run`, `--server`, `--no-iconv`, `--protect-args`, etc. These
+ `--dry-run`, `--server`, `--no-iconv`, `--seclude-args`, etc. These
important options are not matched by wild-card, so they must be overridden
by their exact name. For instance, if you're forcing iconv transfers you
could use something like this:
@@ -922,17 +956,19 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
> refuse options = * !a !delete* delete-after
- A note on refusing "compress" -- it is better to set the "dont compress"
- daemon parameter to "`*`" because that disables compression silently
+ A note on refusing "compress": it may be better to set the "[dont compress](#)"
+ daemon parameter to "`*`" and ensure that `RSYNC_COMPRESS_LIST=zlib` is set
+ in the environment of the daemon in order to disable compression silently
instead of returning an error that forces the client to remove the `-z`
option.
- If you are un-refusing the compress option, you probably want to match
- "`!compress*`" so that you also accept the `--compress-level` option.
+ If you are un-refusing the compress option, you may want to match
+ "`!compress*`" if you also want to allow the `--compress-level` option.
- Note that the "write-devices" option is refused by default, but can be
- explicitly accepted with "`!write-devices`". The options "log-file" and
- "log-file-format" are forcibly refused and cannot be accepted.
+ Note that the "copy-devices" & "write-devices" options are refused by
+ default, but they can be explicitly accepted with "`!copy-devices`" and/or
+ "`!write-devices`". The options "log-file" and "log-file-format" are
+ forcibly refused and cannot be accepted.
Here are all the options that are not matched by wild-cards:
@@ -942,18 +978,22 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
receiver. While rsync passes the older alias `--log-format` for
compatibility reasons, this options should not be confused with
`--log-file-format`.
- - `--sender`: Use "write only" parameter instead of refusing this.
+ - `--sender`: Use "[write only](#)" parameter instead of refusing this.
- `--dry-run`, `-n`: Who would want to disable this?
- - `--protect-args`, `-s`: This actually makes transfers safer.
+ - `--seclude-args`, `-s`: Is the oldest arg-protection method.
- `--from0`, `-0`: Makes it easier to accept/refuse `--files-from` without
affecting this helpful modifier.
- - `--iconv`: This is auto-disabled based on "charset" parameter.
+ - `--iconv`: This is auto-disabled based on "[charset](#)" parameter.
- `--no-iconv`: Most transfers use this option.
- `--checksum-seed`: Is a fairly rare, safe option.
- `--write-devices`: Is non-wild but also auto-disabled.
0. `dont compress`
+ **NOTE:** This parameter currently has no effect except in one instance: if
+ it is set to "`*`" then it minimizes or disables compression for all files
+ (for those that don't want to refuse the `--compress` option completely).
+
This parameter allows you to select filenames based on wildcard patterns
that should not be compressed when pulling files from the daemon (no
analogous parameter exists to govern the pushing of files to a daemon).
@@ -964,14 +1004,14 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
The "dont compress" parameter takes a space-separated list of
case-insensitive wildcard patterns. Any source filename matching one of the
patterns will be compressed as little as possible during the transfer. If
- the compression algorithm has an "off" level (such as zlib/zlibx) then no
- compression occurs for those files. Other algorithms have the level
- minimized to reduces the CPU usage as much as possible.
+ the compression algorithm has an "off" level, then no compression occurs
+ for those files. If an algorithms has the ability to change the level in
+ mid-stream, it will be minimized to reduce the CPU usage as much as
+ possible.
See the `--skip-compress` parameter in the **rsync**(1) manpage for the
- list of file suffixes that are not compressed by default. Specifying a
- value for the "dont compress" parameter changes the default when the daemon
- is the sender.
+ list of file suffixes that are skipped by default if this parameter is not
+ set.
0. `early exec`, `pre-xfer exec`, `post-xfer exec`
@@ -983,7 +1023,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
_not_ displayed if the script returns success. The other programs cannot
send any text to the user. All output except for the `pre-xfer exec`
stdout goes to the corresponding daemon's stdout/stderr, which is typically
- discarded. See the `--no-detatch` option for a way to see the daemon's
+ discarded. See the `--no-detach` option for a way to see the daemon's
output, which can assist with debugging.
Note that the `early exec` command runs before any part of the transfer
@@ -1033,7 +1073,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
**system()** call's default shell), and use RSYNC_NO_XFER_EXEC to disable
both options completely.
-# CONFIG DIRECTIVES
+## CONFIG DIRECTIVES
There are currently two config directives available that allow a config file to
incorporate the contents of other files: `&include` and `&merge`. Both allow
@@ -1088,7 +1128,7 @@ This would merge any `/etc/rsyncd.d/*.inc` files (for global values that should
stay in effect), and then include any `/etc/rsyncd.d/*.conf` files (defining
modules without any global-value cross-talk).
-# AUTHENTICATION STRENGTH
+## AUTHENTICATION STRENGTH
The authentication protocol used in rsync is a 128 bit MD4 based challenge
response system. This is fairly weak protection, though (with at least one
@@ -1103,18 +1143,18 @@ authentication is provided. Use ssh as the transport if you want encryption.
You can also make use of SSL/TLS encryption if you put rsync behind an
SSL proxy.
-# SSL/TLS Daemon Setup
+## SSL/TLS Daemon Setup
When setting up an rsync daemon for access via SSL/TLS, you will need to
-configure a proxy (such as haproxy or nginx) as the front-end that handles the
-encryption.
+configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
+the encryption.
- You should limit the access to the backend-rsyncd port to only allow the
proxy to connect. If it is on the same host as the proxy, then configuring
it to only listen on localhost is a good idea.
-- You should consider turning on the `proxy protocol` parameter if your proxy
- supports sending that information. The examples below assume that this is
- enabled.
+- You should consider turning on the `proxy protocol` rsync-daemon parameter if
+ your proxy supports sending that information. The examples below assume that
+ this is enabled.
An example haproxy setup is as follows:
@@ -1141,14 +1181,17 @@ An example nginx proxy setup is as follows:
> ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
>
> proxy_pass localhost:873;
-> proxy_protocol on; # Requires "proxy protocol = true"
+> proxy_protocol on; # Requires rsyncd.conf "proxy protocol = true"
> proxy_timeout 1m;
> proxy_connect_timeout 5s;
> }
> }
> ```
-# EXAMPLES
+If rsyncd should be accessible encrypted and unencrypted at the same time make
+the proxy listen on port 873 as well and let it handle both streams.
+
+## DAEMON CONFIG EXAMPLES
A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
`/home/ftp` would be:
@@ -1197,46 +1240,40 @@ The /etc/rsyncd.secrets file would look something like this:
> tridge:mypass
> susan:herpass
-# FILES
+## FILES
/etc/rsyncd.conf or rsyncd.conf
-# SEE ALSO
+## SEE ALSO
-**rsync**(1), **rsync-ssl**(1)
+[**rsync**(1)](rsync.1), [**rsync-ssl**(1)](rsync-ssl.1)
-# BUGS
+## BUGS
Please report bugs! The rsync bug tracking system is online at
.
-# VERSION
-
-This man page is current for version @VERSION@ of rsync.
-
-# CREDITS
-
-rsync is distributed under the GNU General Public License. See the file
-COPYING for details.
+## VERSION
-The primary ftp site for rsync is
+This manpage is current for version @VERSION@ of rsync.
-A web site is available at .
+## CREDITS
-We would be delighted to hear from you if you like this program.
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
-This program uses the zlib compression library written by Jean-loup Gailly and
-Mark Adler.
+An rsync web site is available at and its github
+project is .
-# THANKS
+## THANKS
Thanks to Warren Stanley for his original idea and patch for the rsync daemon.
Thanks to Karsten Thygesen for his many suggestions and documentation!
-# AUTHOR
+## AUTHOR
-rsync was written by Andrew Tridgell and Paul Mackerras. Many people have
-later contributed to it.
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people from around the world have helped to maintain and improve it.
Mailing lists for support and development are available at
.
diff --git a/runtests.sh b/runtests.sh
index 38f814d22..0c463bec7 100755
--- a/runtests.sh
+++ b/runtests.sh
@@ -1,7 +1,7 @@
#! /bin/sh
# Copyright (C) 2001, 2002 by Martin Pool
-# Copyright (C) 2003-2020 Wayne Davison
+# Copyright (C) 2003-2022 Wayne Davison
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version
@@ -155,7 +155,7 @@ if test x"$TOOLDIR" = x; then
TOOLDIR=`pwd`
fi
srcdir=`dirname $0`
-if test x"$srcdir" = x -o x"$srcdir" = x.; then
+if test x"$srcdir" = x || test x"$srcdir" = x.; then
srcdir="$TOOLDIR"
fi
if test x"$rsync_bin" = x; then
@@ -238,7 +238,7 @@ failed=0
# failure to aid investigation. We don't remove the testtmp subdir at
# the end so that it can be configured as a symlink to a filesystem that
# has ACLs and xattr support enabled (if desired).
-scratchbase="$TOOLDIR"/testtmp
+scratchbase="${scratchbase:-$TOOLDIR}"/testtmp
echo " scratchbase=$scratchbase"
[ -d "$scratchbase" ] || mkdir "$scratchbase"
@@ -288,7 +288,7 @@ for testscript in $suitedir/$whichtests; do
result=$?
set -e
- if [ "x$always_log" = xyes -o \( $result != 0 -a $result != 77 -a $result != 78 \) ]
+ if [ "x$always_log" = xyes ] || ( [ $result != 0 ] && [ $result != 77 ] && [ $result != 78 ] )
then
echo "----- $testbase log follows"
cat "$scratchdir/test.log"
@@ -336,7 +336,7 @@ echo " $passed passed"
[ "$failed" -gt 0 ] && echo " $failed failed"
[ "$skipped" -gt 0 ] && echo " $skipped skipped"
[ "$missing" -gt 0 ] && echo " $missing missing"
-if [ "$full_run" = yes -a "$expect_skipped" != IGNORE ]; then
+if [ "$full_run" = yes ] && [ "$expect_skipped" != IGNORE ]; then
skipped_list=`echo "$skipped_list" | sed 's/^,//'`
echo "----- skipped results:"
echo " expected: $expect_skipped"
@@ -353,7 +353,7 @@ echo '------------------------------------------------------------'
# because -e is set.
result=`expr $failed + $missing || true`
-if [ "$result" = 0 -a "$skipped_list" != "$expect_skipped" ]; then
+if [ "$result" = 0 ] && [ "$skipped_list" != "$expect_skipped" ]; then
result=1
fi
echo "overall result is $result"
diff --git a/sender.c b/sender.c
index 83603b995..a4d46c39e 100644
--- a/sender.c
+++ b/sender.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,11 +25,13 @@
extern int do_xfers;
extern int am_server;
extern int am_daemon;
+extern int local_server;
extern int inc_recurse;
extern int log_before_transfer;
extern int stdout_format_has_i;
extern int logfile_format_has_i;
extern int want_xattr_optim;
+extern int xfer_sum_len;
extern int csum_length;
extern int append_mode;
extern int copy_links;
@@ -37,6 +39,7 @@ extern int io_error;
extern int flist_eof;
extern int whole_file;
extern int allowed_lull;
+extern int copy_devices;
extern int preserve_xattrs;
extern int protocol_version;
extern int remove_source_files;
@@ -50,6 +53,7 @@ extern int file_old_total;
extern BOOL want_progress_now;
extern struct stats stats;
extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern char num_dev_ino_buf[4 + 8 + 8];
BOOL extra_flist_sending_enabled;
@@ -91,10 +95,11 @@ static struct sum_struct *receive_sums(int f)
return(s);
s->sums = new_array(struct sum_buf, s->count);
+ s->sum2_array = new_array(char, (size_t)s->count * xfer_sum_len);
for (i = 0; i < s->count; i++) {
s->sums[i].sum1 = read_int(f);
- read_buf(f, s->sums[i].sum2, s->s2length);
+ read_buf(f, sum2_at(s, i), s->s2length);
s->sums[i].offset = offset;
s->sums[i].flags = 0;
@@ -143,6 +148,13 @@ void successful_send(int ndx)
goto failed;
}
+ if (local_server
+ && (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
+ && (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
+ rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
+ return;
+ }
+
if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
#ifdef ST_MTIME_NSEC
|| (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))
@@ -338,7 +350,7 @@ void send_files(int f_in, int f_out)
exit_cleanup(RERR_PROTOCOL);
}
- fd = do_open(fname, O_RDONLY, 0);
+ fd = do_open_checklinks(fname);
if (fd == -1) {
if (errno == ENOENT) {
enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;
@@ -366,6 +378,15 @@ void send_files(int f_in, int f_out)
exit_cleanup(RERR_FILEIO);
}
+ if (IS_DEVICE(st.st_mode)) {
+ if (!copy_devices) {
+ rprintf(FERROR, "attempt to copy device contents without --copy-devices\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (st.st_size == 0)
+ st.st_size = get_device_size(fd, fname);
+ }
+
if (append_mode > 0 && st.st_size < F_LENGTH(file)) {
rprintf(FWARNING, "skipped diminished file: %s\n",
full_fname(fname));
diff --git a/simd-checksum-avx2.S b/simd-checksum-avx2.S
index dc8d145b7..549cc3ef9 100644
--- a/simd-checksum-avx2.S
+++ b/simd-checksum-avx2.S
@@ -1,15 +1,21 @@
+#include "config.h"
+
+#ifdef USE_ROLL_ASM /* { */
+
+#define CHAR_OFFSET 0 /* Keep this the same as rsync.h, which isn't likely to change. */
+
#ifdef __APPLE__
-#define get_checksum1_avx2 _get_checksum1_avx2
+#define get_checksum1_avx2_asm _get_checksum1_avx2_asm
#endif
.intel_syntax noprefix
.text
.p2align 5
- .globl get_checksum1_avx2
+ .globl get_checksum1_avx2_asm
# rdi=*buf, esi=len, edx=i, rcx= *ps1, r8= *ps2
-get_checksum1_avx2:
+get_checksum1_avx2_asm:
vmovd xmm6,[rcx] # load *ps1
lea eax, [rsi-128] # at least 128 bytes to process?
cmp edx, eax
@@ -167,3 +173,5 @@ get_checksum1_avx2:
.byte 3
.byte 2
.byte 1
+
+#endif /* } USE_ROLL_ASM */
diff --git a/simd-checksum-x86_64.cpp b/simd-checksum-x86_64.cpp
index ebeeac2dc..d649091ea 100644
--- a/simd-checksum-x86_64.cpp
+++ b/simd-checksum-x86_64.cpp
@@ -51,12 +51,12 @@
* GCC 4.x are not supported to ease configure.ac logic.
*/
-#ifdef __x86_64__
-#ifdef __cplusplus
+#ifdef __x86_64__ /* { */
+#ifdef __cplusplus /* { */
#include "rsync.h"
-#ifdef HAVE_SIMD
+#ifdef USE_ROLL_SIMD /* { */
#include
@@ -68,8 +68,8 @@
#endif
// Missing from the headers on gcc 6 and older, clang 8 and older
-typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
-typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
+typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(16)));
+typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(16)));
/* Compatibility macros to let our SSSE3 algorithm run with only SSE2.
These used to be neat individual functions with target attributes switching between SSE2 and SSSE3 implementations
@@ -85,6 +85,9 @@ typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, _
#define SSE2_HADDS_EPI16(a, b) _mm_adds_epi16(SSE2_INTERLEAVE_EVEN_EPI16(a, b), SSE2_INTERLEAVE_ODD_EPI16(a, b))
#define SSE2_MADDUBS_EPI16(a, b) _mm_adds_epi16(SSE2_MULU_EVEN_EPI8(a, b), SSE2_MULU_ODD_EPI8(a, b))
+#ifndef USE_ROLL_ASM
+__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
+#endif
__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_ssse3_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
@@ -245,7 +248,7 @@ __attribute__ ((target("sse2"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf
// (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
__m128i mul_const = _mm_set1_epi32(4 + (3 << 8) + (2 << 16) + (1 << 24));
- __m128i mul_add16_1 = SSE2_MADDUBS_EPI16(mul_const, in8_1);
+ __m128i mul_add16_1 = SSE2_MADDUBS_EPI16(mul_const, in8_1);
__m128i mul_add16_2 = SSE2_MADDUBS_EPI16(mul_const, in8_2);
// s2 += 32*s1
@@ -310,7 +313,127 @@ __attribute__ ((target("sse2"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf
return i;
}
-extern "C" __attribute__ ((target("avx2"))) int32 get_checksum1_avx2(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2);
+#ifdef USE_ROLL_ASM /* { */
+
+extern "C" __attribute__ ((target("avx2"))) int32 get_checksum1_avx2_asm(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2);
+
+#else /* } { */
+
+/*
+ AVX2 loop per 64 bytes:
+ int16 t1[16];
+ int16 t2[16];
+ for (int j = 0; j < 16; j++) {
+ t1[j] = buf[j*4 + i] + buf[j*4 + i+1] + buf[j*4 + i+2] + buf[j*4 + i+3];
+ t2[j] = 4*buf[j*4 + i] + 3*buf[j*4 + i+1] + 2*buf[j*4 + i+2] + buf[j*4 + i+3];
+ }
+ s2 += 64*s1 + (uint32)(
+ 60*t1[0] + 56*t1[1] + 52*t1[2] + 48*t1[3] + 44*t1[4] + 40*t1[5] + 36*t1[6] + 32*t1[7] + 28*t1[8] + 24*t1[9] + 20*t1[10] + 16*t1[11] + 12*t1[12] + 8*t1[13] + 4*t1[14] +
+ t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7] + t2[8] + t2[9] + t2[10] + t2[11] + t2[12] + t2[13] + t2[14] + t2[15]
+ ) + 2080*CHAR_OFFSET;
+ s1 += (uint32)(t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7] + t1[8] + t1[9] + t1[10] + t1[11] + t1[12] + t1[13] + t1[14] + t1[15]) +
+ 64*CHAR_OFFSET;
+ */
+
+__attribute__ ((target("avx2"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
+{
+ if (len > 64) {
+
+ uint32 x[4] = {0};
+ __m128i ss1 = _mm_cvtsi32_si128(*ps1);
+ __m128i ss2 = _mm_cvtsi32_si128(*ps2);
+
+ const char mul_t1_buf[16] = {60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0};
+ __m128i tmp = _mm_load_si128((__m128i*) mul_t1_buf);
+ __m256i mul_t1 = _mm256_cvtepu8_epi16(tmp);
+ __m256i mul_const = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(4 | (3 << 8) | (2 << 16) | (1 << 24)));
+ __m256i mul_one;
+ mul_one = _mm256_abs_epi8(_mm256_cmpeq_epi16(mul_one,mul_one)); // set all vector elements to 1
+
+ for (; i < (len-64); i+=64) {
+ // Load ... 4*[int8*16]
+ __m256i in8_1, in8_2;
+ __m128i in8_1_low, in8_2_low, in8_1_high, in8_2_high;
+ in8_1_low = _mm_loadu_si128((__m128i_u*)&buf[i]);
+ in8_2_low = _mm_loadu_si128((__m128i_u*)&buf[i+16]);
+ in8_1_high = _mm_loadu_si128((__m128i_u*)&buf[i+32]);
+ in8_2_high = _mm_loadu_si128((__m128i_u*)&buf[i+48]);
+ in8_1 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_1_low), in8_1_high,1);
+ in8_2 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_2_low), in8_2_high,1);
+
+ // (1*buf[i] + 1*buf[i+1]), (1*buf[i+2], 1*buf[i+3]), ... 2*[int16*8]
+ // Fastest, even though multiply by 1
+ __m256i add16_1 = _mm256_maddubs_epi16(mul_one, in8_1);
+ __m256i add16_2 = _mm256_maddubs_epi16(mul_one, in8_2);
+
+ // (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
+ __m256i mul_add16_1 = _mm256_maddubs_epi16(mul_const, in8_1);
+ __m256i mul_add16_2 = _mm256_maddubs_epi16(mul_const, in8_2);
+
+ // s2 += 64*s1
+ ss2 = _mm_add_epi32(ss2, _mm_slli_epi32(ss1, 6));
+
+ // [sum(t1[0]..t1[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m256i sum_add32 = _mm256_add_epi16(add16_1, add16_2);
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_epi32(sum_add32, 16));
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 4));
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 8));
+
+ // [sum(t2[0]..t2[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m256i sum_mul_add32 = _mm256_add_epi16(mul_add16_1, mul_add16_2);
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_epi32(sum_mul_add32, 16));
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 4));
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 8));
+
+ // s1 += t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]
+ __m128i sum_add32_hi = _mm256_extracti128_si256(sum_add32, 0x1);
+ ss1 = _mm_add_epi32(ss1, _mm256_castsi256_si128(sum_add32));
+ ss1 = _mm_add_epi32(ss1, sum_add32_hi);
+
+ // s2 += t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
+ __m128i sum_mul_add32_hi = _mm256_extracti128_si256(sum_mul_add32, 0x1);
+ ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(sum_mul_add32));
+ ss2 = _mm_add_epi32(ss2, sum_mul_add32_hi);
+
+ // [t1[0] + t1[1], t1[2] + t1[3] ...] [int16*8]
+ // We could've combined this with generating sum_add32 above and
+ // save an instruction but benchmarking shows that as being slower
+ __m256i add16 = _mm256_hadds_epi16(add16_1, add16_2);
+
+ // [t1[0], t1[1], ...] -> [t1[0]*28 + t1[1]*24, ...] [int32*4]
+ __m256i mul32 = _mm256_madd_epi16(add16, mul_t1);
+
+ // [sum(mul32), X, X, X] [int32*4]; faster than multiple _mm_hadd_epi32
+ mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 4));
+ mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 8));
+ // prefetch 2 cacheline ahead
+ _mm_prefetch(&buf[i + 160], _MM_HINT_T0);
+
+ // s2 += 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6]
+ __m128i mul32_hi = _mm256_extracti128_si256(mul32, 0x1);
+ ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(mul32));
+ ss2 = _mm_add_epi32(ss2, mul32_hi);
+
+#if CHAR_OFFSET != 0
+ // s1 += 32*CHAR_OFFSET
+ __m128i char_offset_multiplier = _mm_set1_epi32(32 * CHAR_OFFSET);
+ ss1 = _mm_add_epi32(ss1, char_offset_multiplier);
+
+ // s2 += 528*CHAR_OFFSET
+ char_offset_multiplier = _mm_set1_epi32(528 * CHAR_OFFSET);
+ ss2 = _mm_add_epi32(ss2, char_offset_multiplier);
+#endif
+ }
+
+ _mm_store_si128((__m128i_u*)x, ss1);
+ *ps1 = x[0];
+ _mm_store_si128((__m128i_u*)x, ss2);
+ *ps2 = x[0];
+ }
+ return i;
+}
+
+#endif /* } !USE_ROLL_ASM */
static int32 get_checksum1_default_1(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
{
@@ -338,7 +461,11 @@ static inline uint32 get_checksum1_cpp(char *buf1, int32 len)
uint32 s2 = 0;
// multiples of 64 bytes using AVX2 (if available)
- i = get_checksum1_avx2((schar*)buf1, len, i, &s1, &s2);
+#ifdef USE_ROLL_ASM
+ i = get_checksum1_avx2_asm((schar*)buf1, len, i, &s1, &s2);
+#else
+ i = get_checksum1_avx2_64((schar*)buf1, len, i, &s1, &s2);
+#endif
// multiples of 32 bytes using SSSE3 (if available)
i = get_checksum1_ssse3_32((schar*)buf1, len, i, &s1, &s2);
@@ -407,7 +534,11 @@ int main() {
benchmark("Raw-C", get_checksum1_default_1, (schar*)buf, BLOCK_LEN);
benchmark("SSE2", get_checksum1_sse2_32, (schar*)buf, BLOCK_LEN);
benchmark("SSSE3", get_checksum1_ssse3_32, (schar*)buf, BLOCK_LEN);
- benchmark("AVX2", get_checksum1_avx2, (schar*)buf, BLOCK_LEN);
+#ifdef USE_ROLL_ASM
+ benchmark("AVX2-ASM", get_checksum1_avx2_asm, (schar*)buf, BLOCK_LEN);
+#else
+ benchmark("AVX2", get_checksum1_avx2_64, (schar*)buf, BLOCK_LEN);
+#endif
free(buf);
return 0;
@@ -417,6 +548,6 @@ int main() {
#pragma clang optimize on
#endif /* BENCHMARK_SIMD_CHECKSUM1 */
-#endif /* HAVE_SIMD */
-#endif /* __cplusplus */
-#endif /* __x86_64__ */
+#endif /* } USE_ROLL_SIMD */
+#endif /* } __cplusplus */
+#endif /* } __x86_64__ */
diff --git a/support/atomic-rsync b/support/atomic-rsync
index 0346fb49a..1964090da 100755
--- a/support/atomic-rsync
+++ b/support/atomic-rsync
@@ -1,92 +1,94 @@
-#!/usr/bin/env perl
-#
+#!/usr/bin/env python3
# This script lets you update a hierarchy of files in an atomic way by
# first creating a new hierarchy using rsync's --link-dest option, and
# then swapping the hierarchy into place. **See the usage message for
# more details and some important caveats!**
-use strict;
-use warnings;
-use Cwd 'abs_path';
-
-my $RSYNC_PROG = '/usr/bin/rsync';
-my $RM_PROG = '/bin/rm';
-
-my $dest_dir = $ARGV[-1];
-&usage if !defined $dest_dir || $dest_dir =~ /(^-|^$)/ || grep(/^--help/, @ARGV);
-$dest_dir =~ s{(?<=.)/+$} {};
-
-if (!-d $dest_dir) {
- die "$dest_dir is not a directory.\nUse --help for help.\n";
-}
-
-if (@_ = grep(/^--[a-z]+-dest\b/, @ARGV)) {
- $_ = join(' or ', @_);
- die "You cannot use the $_ option with atomic-rsync.\nUse --help for help.\n";
-}
-
-my $symlink_content = readlink $dest_dir; # undef when a real dir
-
-my $dest_arg = $dest_dir;
-# This gives us the real destination dir, with all symlinks dereferenced.
-$dest_dir = abs_path($dest_dir);
-if ($dest_dir eq '/') {
- die qq|You must not use "/" as the destination directory.\nUse --help for help.\n|;
-}
-
-my($old_dir, $new_dir);
-if (defined $symlink_content && $dest_dir =~ /-([12])$/) {
- my $num = 3 - $1;
- $old_dir = undef;
- ($new_dir = $dest_dir) =~ s/-[12]$/-$num/;
- $symlink_content =~ s/-[12]$/-$num/;
-} else {
- $old_dir = "$dest_dir~old~";
- $new_dir = "$dest_dir~new~";
-}
-
-$ARGV[-1] = "$new_dir/";
-
-system($RM_PROG, '-rf', $old_dir) if defined $old_dir && -d $old_dir;
-system($RM_PROG, '-rf', $new_dir) if -d $new_dir;
-
-if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) {
- if ($? == -1) {
- print "failed to execute $RSYNC_PROG: $!\n";
- } elsif ($? & 127) {
- printf "child died with signal %d, %s coredump\n",
- ($? & 127), ($? & 128) ? 'with' : 'without';
- } else {
- printf "child exited with value %d\n", $? >> 8;
- }
- exit 1;
-}
-
-if (!defined $old_dir) {
- atomic_symlink($symlink_content, $dest_arg);
- exit;
-}
-
-rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!";
-rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
-
-exit;
-
-sub atomic_symlink
-{
- my($target, $link) = @_;
- my $newlink = "$link~new~";
-
- unlink($newlink); # Just in case
- symlink($target, $newlink) or die "Unable to symlink $newlink -> $target: $!\n";
- rename($newlink, $link) or die "Unable to rename $newlink to $link: $!\n";
-}
-
-
-sub usage
-{
- die <) {
- chomp;
- s#^\./##;
-
- my $entries = "$_/Entries";
- s/CVS$/.cvsinclude/;
- my $filter = $_;
-
- open(ENTRIES, $entries) or die "Unable to open $entries: $!\n";
- my @includes;
- while () {
- push(@includes, $1) if m#/(.+?)/#;
- }
- close ENTRIES;
- if (@includes) {
- open(FILTER, ">$filter") or die "Unable to write $filter: $!\n";
- print FILTER map "+ /$_\n", @includes;
- close FILTER;
- print "Updated $filter\n";
- } elsif (-f $filter) {
- unlink($filter);
- print "Removed $filter\n";
- }
-}
-close FIND;
+# CVS gets an added or removed file. Maybe just run it before every copy.
+
+import os, argparse
+
+INC_NAME = '.cvsinclude'
+
+def main():
+ if args.dir:
+ os.chdir(args.dir)
+
+ cvs_includes = set()
+ for root, dirs, files in os.walk('.'):
+ if INC_NAME in files:
+ cvs_includes.add((root + '/' + INC_NAME)[2:])
+ if root.endswith('/CVS') and 'Entries' in files:
+ entries = root[2:] + '/Entries'
+ includes = [ ]
+ with open(entries) as fh:
+ for line in fh:
+ if line.startswith(('/', 'D/')):
+ includes.append(line.split('/', 2)[1])
+ if includes:
+ inc = root[2:-3] + INC_NAME
+ cvs_includes.discard(inc)
+ try:
+ with open(inc) as fh:
+ old_txt = fh.read()
+ except OSError:
+ old_txt = ''
+ txt = ''.join(f"+ /{x}\n" for x in includes)
+ if txt == old_txt:
+ print("Unchanged", inc)
+ else:
+ print("Updating", inc)
+ with open(inc, 'w') as fh:
+ fh.write(txt)
+ dirs.sort()
+
+ for inc in sorted(cvs_includes):
+ print("Removing", inc)
+ os.unlink(inc)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=f"Transform CVS/Entries into {INC_NAME} files.", add_help=False)
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument("dir", nargs='?', help="The top CVS dir. Defaults to the current directory.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/files-to-excludes b/support/files-to-excludes
index a28955cb6..a47d7f8a2 100755
--- a/support/files-to-excludes
+++ b/support/files-to-excludes
@@ -1,27 +1,37 @@
-#!/usr/bin/env perl
-# This script takes an input of filenames and outputs a set of
-# include/exclude directives that can be used by rsync to copy
-# just the indicated files using an --exclude-from=FILE option.
-use strict;
+#!/usr/bin/env python3
+# This script takes an input of filenames and outputs a set of include/exclude
+# directives that can be used by rsync to copy just the indicated files using
+# an --exclude-from=FILE or -f'. FILE' option. To be able to delete files on
+# the receiving side, either use --delete-excluded or change the exclude (-)
+# rules to hide filter rules (H) that only affect the sending side.
-my %hash;
+import os, fileinput, argparse
-while (<>) {
- chomp;
- s#^/+##;
- my $path = '/';
- while (m#([^/]+/)/*#g) {
- $path .= $1;
- print "+ $path\n" unless $hash{$path}++;
- }
- if (m#([^/]+)$#) {
- print "+ $path$1\n";
- } else {
- delete $hash{$path};
- }
-}
+def main():
+ paths = set()
+ for line in fileinput.input(args.files):
+ dirs = line.strip().lstrip('/').split('/')
+ if not dirs:
+ continue
+ for j in range(1, len(dirs)):
+ if dirs[j] == '':
+ continue
+ path = '/' + '/'.join(dirs[:j]) + '/'
+ if path not in paths:
+ print('+', path)
+ paths.add(path)
+ print('+', '/' + '/'.join(dirs))
-foreach (sort keys %hash) {
- print "- $_*\n";
-}
-print "- /*\n";
+ for path in sorted(paths):
+ print('-', path + '*')
+ print('-', '/*')
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Transform a list of files into a set of include/exclude rules.", add_help=False)
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the pathnames to translate. Defaults to stdin.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/git-set-file-times b/support/git-set-file-times
index 24b3fde54..601248b90 100755
--- a/support/git-set-file-times
+++ b/support/git-set-file-times
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import os, re, argparse, subprocess
-from datetime import datetime
+from datetime import datetime, UTC
NULL_COMMIT_RE = re.compile(r'\0\0commit [a-f0-9]{40}$|\0$')
@@ -38,7 +38,7 @@ def main():
print_line(fn, mtime, mtime)
ls.discard(fn)
- cmd = git + 'log -r --name-only --no-color --pretty=raw --no-renames -z'.split()
+ cmd = git + 'log -r --name-only --format=%x00commit%x20%H%n%x00commit_time%x20%ct%n --no-renames -z'.split()
if args.tree:
cmd.append(args.tree)
cmd += ['--'] + args.files
@@ -46,7 +46,7 @@ def main():
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf-8')
for line in proc.stdout:
line = line.strip()
- m = re.match(r'^committer .*? (\d+) [-+]\d+$', line)
+ m = re.match(r'^\0commit_time (\d+)$', line)
if m:
commit_time = int(m[1])
elif NULL_COMMIT_RE.search(line):
@@ -74,7 +74,7 @@ def print_line(fn, mtime, commit_time):
if args.list > 1:
ts = str(commit_time).rjust(10)
else:
- ts = datetime.utcfromtimestamp(commit_time).strftime("%Y-%m-%d %H:%M:%S")
+ ts = datetime.fromtimestamp(commit_time, UTC).strftime("%Y-%m-%d %H:%M:%S")
chg = '.' if mtime == commit_time else '*'
print(chg, ts, fn)
diff --git a/support/idmap b/support/idmap
new file mode 100755
index 000000000..b212aaf6c
--- /dev/null
+++ b/support/idmap
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+# This helper script makes it easy to use a passwd or group file to map values
+# in a LOCAL transfer. For instance, if you mount a backup that does not have
+# the same passwd setup as the local machine, you can do a copy to/from the
+# backup area as follows and get the differing ID values mapped just like a
+# remote transfer to/from the backed-up machine would do:
+#
+# rsync -av --usermap=`idmap --to /mnt/backup/etc/passwd` \
+# --groupmap=`idmap --to /mnt/backup/etc/group` \
+# /some/src/ /mnt/backup/some/dest/
+#
+# rsync -av --usermap=`idmap --from /mnt/backup/etc/passwd` \
+# --groupmap=`idmap --from /mnt/backup/etc/group` \
+# /mnt/backup/some/src/ /some/dest/
+
+import re, fileinput, argparse
+
+NAME_ID_RE = re.compile(r'^(\w+):[^:]+:(\d+)')
+
+def main():
+ maps = [ ]
+ for line in fileinput.input(args.files):
+ m = NAME_ID_RE.match(line)
+ if not m:
+ continue
+ if args.to:
+ pair = (m[1], m[2])
+ else:
+ pair = (m[2], m[1])
+ maps.append(':'.join(pair))
+ print(','.join(maps))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output usermap or groupmap args for rsync.", add_help=False)
+ action = parser.add_argument_group()
+ action = parser.add_mutually_exclusive_group(required=True)
+ action.add_argument("--from", action="store_true", help="Output the map for use on the sending side.")
+ action.add_argument("--to", action="store_true", help="Output the map for use on the receiving side.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the name & id pairs. Defaults to stdin.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/install_deps_ubuntu.sh b/support/install_deps_ubuntu.sh
new file mode 100755
index 000000000..ac49055bb
--- /dev/null
+++ b/support/install_deps_ubuntu.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# install script for build dependencies for ubuntu/debian systems
+
+sudo apt install -y gcc g++ gawk autoconf automake python3-cmarkgfm
+sudo apt install -y acl libacl1-dev
+sudo apt install -y attr libattr1-dev
+sudo apt install -y libxxhash-dev
+sudo apt install -y libzstd-dev
+sudo apt install -y liblz4-dev
+sudo apt install -y libssl-dev
diff --git a/support/json-rsync-version b/support/json-rsync-version
new file mode 100755
index 000000000..b01e1e495
--- /dev/null
+++ b/support/json-rsync-version
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+
+import sys, argparse, subprocess, json
+
+TWEAK_NAME = {
+ 'asm': 'asm_roll',
+ 'ASM': 'asm_roll',
+ 'hardlink_special': 'hardlink_specials',
+ 'protect_args': 'secluded_args',
+ 'protected_args': 'secluded_args',
+ 'SIMD': 'SIMD_roll',
+ }
+
+MOVE_OPTIM = set('asm_roll SIMD_roll'.split())
+
+def main():
+ if not args.rsync or args.rsync == '-':
+ ver_out = sys.stdin.read().strip()
+ else:
+ ver_out = subprocess.check_output([args.rsync, '--version', '--version'], encoding='utf-8').strip()
+ if ver_out.startswith('{'):
+ print(ver_out)
+ return
+ info = { }
+ misplaced_optims = { }
+ for line in ver_out.splitlines():
+ if line.startswith('rsync '):
+ prog, vstr, ver, pstr, vstr2, proto = line.split()
+ info['program'] = prog
+ if ver.startswith('v'):
+ ver = ver[1:]
+ info[vstr] = ver
+ if '.' not in proto:
+ proto += '.0'
+ else:
+ proto = proto.replace('.PR', '.')
+ info[pstr] = proto
+ elif line.startswith('Copyright '):
+ info['copyright'] = line[10:]
+ elif line.startswith('Web site: '):
+ info['url'] = line[10:]
+ elif line.startswith(' '):
+ if not saw_comma and ',' in line:
+ saw_comma = True
+ info[sect_name] = { }
+ if saw_comma:
+ for x in line.strip(' ,').split(', '):
+ if ' ' in x:
+ val, var = x.split(' ', 1)
+ if val == 'no':
+ val = False
+ elif val.endswith('-bit'):
+ var = var[:-1] + '_bits'
+ val = int(val.split('-')[0])
+ else:
+ var = x
+ val = True
+ var = var.replace(' ', '_').replace('-', '_')
+ if var in TWEAK_NAME:
+ var = TWEAK_NAME[var]
+ if sect_name[0] != 'o' and var in MOVE_OPTIM:
+ misplaced_optims[var] = val
+ else:
+ info[sect_name][var] = val
+ else:
+ info[sect_name] += [ x for x in line.split() if not x.startswith('(') ]
+ elif line == '':
+ break
+ else:
+ sect_name = line.strip(' :').replace(' ', '_').lower()
+ info[sect_name] = [ ]
+ saw_comma = False
+ for chk in 'capabilities optimizations'.split():
+ if chk not in info:
+ info[chk] = { }
+ if misplaced_optims:
+ info['optimizations'].update(misplaced_optims)
+ for chk in 'checksum_list compress_list daemon_auth_list'.split():
+ if chk not in info:
+ info[chk] = [ ]
+ info['license'] = 'GPLv3' if ver[0] == '3' else 'GPLv2'
+ info['caveat'] = 'rsync comes with ABSOLUTELY NO WARRANTY'
+ print(json.dumps(info))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output rsync's version data in JSON format, even if the rsync doesn't support a native json-output method.", add_help=False)
+ parser.add_argument('rsync', nargs='?', help="Specify an rsync command to run. Otherwise stdin is consumed.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/lsh b/support/lsh
index be29310c7..7b3c0656d 100755
--- a/support/lsh
+++ b/support/lsh
@@ -15,6 +15,8 @@ GetOptions(
'b|c|D|e|F|i|L|m|O|o|p|R|S|w=s' => sub { }, # Ignore
'no-cd' => \( my $no_chdir ),
'sudo' => \( my $use_sudo ),
+ 'rrsync=s' => \( my $rrsync_dir ),
+ 'rropts=s' => \( my $rrsync_opts ),
) or &usage;
&usage unless @ARGV > 1;
@@ -67,22 +69,40 @@ unless ($no_chdir) {
chdir $home_dir or die "Unable to chdir to $home_dir: $!\n";
}
-push @cmd, '/bin/sh', '-c', "@ARGV";
+if ($rrsync_dir) {
+ $ENV{SSH_ORIGINAL_COMMAND} = join(' ', @ARGV);
+ push @cmd, 'rrsync';
+ if ($rrsync_opts) {
+ foreach my $opt (split(/[ ,]+/, $rrsync_opts)) {
+ $opt = "-$opt" unless $opt =~ /^-/;
+ push @cmd, $opt;
+ }
+ }
+ push @cmd, $rrsync_dir;
+} else {
+ push @cmd, '/bin/sh', '-c', "@ARGV";
+}
exec @cmd;
die "Failed to exec: $!\n";
sub usage
{
die <) {
- push @_, "$2:$1" if /^(\w+):[^:]+:(\d+)/;
-}
-print join(',', @_), "\n";
diff --git a/support/mapto b/support/mapto
deleted file mode 100755
index 958875237..000000000
--- a/support/mapto
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env perl
-# This helper script makes it easy to use a passwd or group file to map
-# values in a LOCAL transfer. For instance, if you mount a backup that
-# does not have the same passwd setup as the local machine, you can do
-# a copy TO the backup area as follows and get the differing ID values
-# mapped just like a remote transfer TO the backed-up machine would do:
-#
-# rsync -av --usermap=`mapto /mnt/backup/etc/passwd` \
-# --groupmap=`mapto /mnt/backup/etc/group` \
-# /some/src/ /mnt/backup/some/dest/
-
-while (<>) {
- push @_, "$1:$2" if /^(\w+):[^:]+:(\d+)/;
-}
-print join(',', @_), "\n";
diff --git a/support/mnt-excl b/support/mnt-excl
index ed7b49ba5..bc8b5bcd8 100755
--- a/support/mnt-excl
+++ b/support/mnt-excl
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!/usr/bin/env python3
# This script takes a command-line arg of a source directory
# that will be passed to rsync, and generates a set of excludes
# that will exclude all mount points from the list. This is
@@ -27,23 +27,33 @@
# awk '{print $2}' /proc/mounts | grep -v '^/$' | \
# rsync -avf 'merge,/- -' /dir host:/dest/
-use strict;
-use warnings;
-use Cwd 'abs_path';
+import os, argparse
-my $file = '/proc/mounts';
-my $dir = shift || '/';
-my $trailing_slash = $dir =~ m{./$} ? '/' : '';
-$dir = abs_path($dir) . $trailing_slash;
-$dir =~ s{([^/]*)$}{};
-my $trailing = $1;
-$trailing = '' if $trailing eq '.' || !-d "$dir$trailing";
-$trailing .= '/' if $trailing ne '';
+MNT_FILE = '/proc/mounts';
-open(IN, $file) or die "Unable to open $file: $!\n";
-while () {
- $_ = (split)[1];
- next unless s{^\Q$dir$trailing\E}{}o && $_ ne '';
- print "- /$trailing$_\n";
-}
-close IN;
+def main():
+ trailing_slash = '/' if args.path.endswith(('/', '/.')) and args.path != '/' else ''
+ args.path = os.path.realpath(args.path) + trailing_slash
+ parent_dir = os.path.dirname(args.path)
+ trailing = os.path.basename(args.path)
+ if not os.path.isdir(args.path):
+ trailing = ''
+ elif trailing != '':
+ trailing += '/'
+ want_path = os.path.join(parent_dir, trailing)
+ wp_len = len(want_path)
+
+ with open(MNT_FILE) as fh:
+ for line in fh:
+ mnt_path = line.split()[1]
+ if mnt_path.startswith(want_path) and mnt_path != want_path:
+ print(f"- /{trailing}{mnt_path[wp_len:]}")
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output mount points as rsync excludes.", add_help=False)
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument('path', metavar='PATH', nargs='?', default='/', help="Limit output to those within the PATH hierarchy.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/munge-symlinks b/support/munge-symlinks
index 3e5f3ad2a..e7a547416 100755
--- a/support/munge-symlinks
+++ b/support/munge-symlinks
@@ -1,60 +1,71 @@
-#!/usr/bin/env perl
+#!/usr/bin/env python3
# This script will either prefix all symlink values with the string
# "/rsyncd-munged/" or remove that prefix.
-use strict;
-use Getopt::Long;
-
-my $SYMLINK_PREFIX = '/rsyncd-munged/';
-
-my $munge_opt;
-
-&GetOptions(
- 'munge' => sub { $munge_opt = 1 },
- 'unmunge' => sub { $munge_opt = 0 },
- 'all' => \( my $all_opt ),
- 'help|h' => \( my $help_opt ),
-) or &usage;
-
-&usage if $help_opt || !defined $munge_opt;
-
-my $munged_re = $all_opt ? qr/^($SYMLINK_PREFIX)+(?=.)/ : qr/^$SYMLINK_PREFIX(?=.)/;
-
-push(@ARGV, '.') unless @ARGV;
-
-open(PIPE, '-|', 'find', @ARGV, '-type', 'l') or die $!;
-
-while () {
- chomp;
- my $lnk = readlink($_) or next;
- if ($munge_opt) {
- next if !$all_opt && $lnk =~ /$munged_re/;
- $lnk =~ s/^/$SYMLINK_PREFIX/;
- } else {
- next unless $lnk =~ s/$munged_re//;
- }
- if (!unlink($_)) {
- warn "Unable to unlink symlink: $_ ($!)\n";
- } elsif (!symlink($lnk, $_)) {
- warn "Unable to recreate symlink: $_ -> $lnk ($!)\n";
- } else {
- print "$_ -> $lnk\n";
- }
-}
-
-close PIPE;
-exit;
-
-sub usage
-{
- die <', lnk + ':', str(e), file=sys.stderr)
+ return
+ print(fn, '->', lnk)
+
+
+if __name__ == '__main__':
+ our_desc = """\
+Adds or removes the %s prefix to/from the start of each symlink's value.
+When given the name of a directory, affects all the symlinks in that directory hierarchy.
+""" % SYMLINK_PREFIX
+ epilog = 'See the "munge symlinks" option in the rsyncd.conf manpage for more details.'
+ parser = argparse.ArgumentParser(description=our_desc, epilog=epilog, add_help=False)
+ uniq_group = parser.add_mutually_exclusive_group()
+ uniq_group.add_argument('--munge', action='store_true', help="Add the prefix to symlinks (the default).")
+ uniq_group.add_argument('--unmunge', action='store_true', help="Remove the prefix from symlinks.")
+ parser.add_argument('--all', action='store_true', help="Always adds the prefix when munging (even if already munged) or removes multiple instances of the prefix when unmunging.")
+ parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
+ parser.add_argument('names', metavar='NAME', nargs='+', help="One or more directories and/or symlinks to process.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/rrsync b/support/rrsync
old mode 100644
new mode 100755
index 438e3a24a..e8b0cc0d2
--- a/support/rrsync
+++ b/support/rrsync
@@ -1,257 +1,387 @@
-#!/usr/bin/env perl
-# Name: /usr/local/bin/rrsync (should also have a symlink in /usr/bin)
-# Purpose: Restricts rsync to subdirectory declared in .ssh/authorized_keys
-# Author: Joe Smith 30-Sep-2004
-# Modified by: Wayne Davison
-use strict;
-
-use Socket;
-use Cwd 'abs_path';
-use File::Glob ':glob';
-
-# You may configure these values to your liking. See also the section
-# of options if you want to disable any options that rsync accepts.
-use constant RSYNC => '/usr/bin/rsync';
-use constant LOGFILE => 'rrsync.log';
-
-my $Usage = < 30-Sep-2004
+# Python version by: Wayne Davison
+
+# You may configure these 2 values to your liking. See also the section of
+# short & long options if you want to disable any options that rsync accepts.
+RSYNC = '/usr/bin/rsync'
+LOGFILE = 'rrsync.log' # NOTE: the file must exist for a line to be appended!
+
+# The following options are mainly the options that a client rsync can send
+# to the server, and usually just in the one option format that the stock
+# rsync produces. However, there are some additional convenience options
+# added as well, and thus a few options are present in both the short and
+# long lists (such as --group, --owner, and --perms).
-our $subdir = shift;
-die "$0: No subdirectory specified\n$Usage" unless defined $subdir;
-$subdir = abs_path($subdir);
-die "$0: Restricted directory does not exist!\n" if $subdir ne '/' && !-d $subdir;
-
-# The client uses "rsync -av -e ssh src/ server:dir/", and sshd on the server
-# executes this program when .ssh/authorized_keys has 'command="..."'.
-# For example:
-# command="rrsync logs/client" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzGhEeNlPr...
-# command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmkHG1WCjC...
-#
-# Format of the environment variables set by sshd:
-# SSH_ORIGINAL_COMMAND=rsync --server -vlogDtpr --partial . ARG # push
-# SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtpr --partial . ARGS # pull
-# SSH_CONNECTION=client_addr client_port server_port
-
-my $command = $ENV{SSH_ORIGINAL_COMMAND};
-die "$0: Not invoked via sshd\n$Usage" unless defined $command;
-die "$0: SSH_ORIGINAL_COMMAND='$command' is not rsync\n" unless $command =~ s/^rsync\s+//;
-die "$0: --server option is not first\n" unless $command =~ /^--server\s/;
-our $am_sender = $command =~ /^--server\s+--sender\s/; # Restrictive on purpose!
-die "$0 sending to read-only server not allowed\n" if $only eq 'r' && !$am_sender;
-die "$0 reading from write-only server not allowed\n" if $only eq 'w' && $am_sender;
-
-### START of options data produced by the cull_options script. ###
-
-# These options are the only options that rsync might send to the server,
-# and only in the option format that the stock rsync produces.
+# NOTE when disabling: check for both a short & long version of the option!
+
+### START of options data produced by the cull-options script. ###
# To disable a short-named option, add its letter to this string:
-our $short_disabled = 's';
+short_disabled = 's'
+
+# These are also disabled when the restricted dir is not "/":
+short_disabled_subdir = 'KLk'
-our $short_no_arg = 'ACDEHIJKLNORSUWXbcdgklmnopqrstuvxyz'; # DO NOT REMOVE ANY
-our $short_with_num = '@B'; # DO NOT REMOVE ANY
+# These are all possible short options that we will accept (when not disabled above):
+short_no_arg = 'ACDEHIJKLNORSUWXbcdgklmnopqrstuvxyz' # DO NOT REMOVE ANY
+short_with_num = '@B' # DO NOT REMOVE ANY
# To disable a long-named option, change its value to a -1. The values mean:
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
# check the arg when receiving; and 3 = always check the arg.
-our %long_opt = (
- 'append' => 0,
- 'backup-dir' => 2,
- 'block-size' => 1,
- 'bwlimit' => 1,
- 'checksum-choice' => 1,
- 'checksum-seed' => 1,
- 'compare-dest' => 2,
- 'compress-choice' => 1,
- 'compress-level' => 1,
- 'copy-dest' => 2,
- 'copy-unsafe-links' => 0,
- 'daemon' => -1,
- 'debug' => 1,
- 'delay-updates' => 0,
- 'delete' => 0,
- 'delete-after' => 0,
- 'delete-before' => 0,
- 'delete-delay' => 0,
- 'delete-during' => 0,
- 'delete-excluded' => 0,
- 'delete-missing-args' => 0,
- 'existing' => 0,
- 'fake-super' => 0,
- 'files-from' => 3,
- 'force' => 0,
- 'from0' => 0,
- 'fsync' => 2,
- 'fuzzy' => 0,
- 'group' => 0,
- 'groupmap' => 1,
- 'hard-links' => 0,
- 'iconv' => 1,
- 'ignore-errors' => 0,
- 'ignore-existing' => 0,
- 'ignore-missing-args' => 0,
- 'ignore-times' => 0,
- 'info' => 1,
- 'inplace' => 0,
- 'link-dest' => 2,
- 'links' => 0,
- 'list-only' => 0,
- 'log-file' => $only eq 'r' ? -1 : 3,
- 'log-format' => 1,
- 'max-alloc' => 1,
- 'max-delete' => 1,
- 'max-size' => 1,
- 'min-size' => 1,
- 'mkpath' => 0,
- 'modify-window' => 1,
- 'msgs2stderr' => 0,
- 'new-compress' => 0,
- 'no-W' => 0,
- 'no-implied-dirs' => 0,
- 'no-msgs2stderr' => 0,
- 'no-r' => 0,
- 'no-relative' => 0,
- 'no-specials' => 0,
- 'numeric-ids' => 0,
- 'old-compress' => 0,
- 'one-file-system' => 0,
- 'only-write-batch' => 1,
- 'open-noatime' => 0,
- 'owner' => 0,
- 'partial' => 0,
- 'partial-dir' => 2,
- 'perms' => 0,
- 'preallocate' => 0,
- 'recursive' => 0,
- 'remove-sent-files' => $only eq 'r' ? -1 : 0,
- 'remove-source-files' => $only eq 'r' ? -1 : 0,
- 'safe-links' => 0,
- 'sender' => $only eq 'w' ? -1 : 0,
- 'server' => 0,
- 'size-only' => 0,
- 'skip-compress' => 1,
- 'specials' => 0,
- 'stats' => 0,
- 'suffix' => 1,
- 'super' => 0,
- 'temp-dir' => 2,
- 'timeout' => 1,
- 'times' => 0,
- 'use-qsort' => 0,
- 'usermap' => 1,
- 'write-devices' => -1,
-);
-
-### END of options data produced by the cull_options script. ###
-
-if ($short_disabled ne '') {
- $short_no_arg =~ s/[$short_disabled]//go;
- $short_with_num =~ s/[$short_disabled]//go;
-}
-$short_no_arg = "[$short_no_arg]" if length($short_no_arg) > 1;
-$short_with_num = "[$short_with_num]" if length($short_with_num) > 1;
-
-my $write_log = -f LOGFILE && open(LOG, '>>', LOGFILE);
-
-chdir($subdir) or die "$0: Unable to chdir to restricted dir: $!\n";
-
-my(@opts, @args);
-my $in_options = 1;
-my $last_opt = '';
-my $check_type;
-while ($command =~ /((?:[^\s\\]+|\\.[^\s\\]*)+)/g) {
- $_ = $1;
- if ($check_type) {
- push(@opts, check_arg($last_opt, $_, $check_type));
- $check_type = 0;
- } elsif ($in_options) {
- push(@opts, $_);
- if ($_ eq '.') {
- $in_options = 0;
- } else {
- die "$0: invalid option: '-'\n" if $_ eq '-';
- next if /^-$short_no_arg*(e\d*\.\w*)?$/o || /^-$short_with_num\d+$/o;
-
- my($opt,$arg) = /^--([^=]+)(?:=(.*))?$/;
- my $disabled;
- if (defined $opt) {
- my $ct = $long_opt{$opt};
- last unless defined $ct;
- next if $ct == 0;
- if ($ct > 0) {
- if (!defined $arg) {
- $check_type = $ct;
- $last_opt = $opt;
- next;
- }
- $arg = check_arg($opt, $arg, $ct);
- $opts[-1] =~ s/=.*/=$arg/;
- next;
- }
- $disabled = 1;
- $opt = "--$opt";
- } elsif ($short_disabled ne '') {
- $disabled = /^-$short_no_arg*([$short_disabled])/o;
- $opt = "-$1";
- }
-
- last unless $disabled; # Generate generic failure
- die "$0: option $opt has been disabled on this server.\n";
- }
- } else {
- if ($subdir ne '/') {
- # Validate args to ensure they don't try to leave our restricted dir.
- s{//+}{/}g;
- s{^/}{};
- s{^$}{.};
- }
- push(@args, bsd_glob($_, GLOB_LIMIT|GLOB_NOCHECK|GLOB_BRACE|GLOB_QUOTE));
- }
+long_opts = {
+ 'append': 0,
+ 'backup-dir': 2,
+ 'block-size': 1,
+ 'bwlimit': 1,
+ 'checksum-choice': 1,
+ 'checksum-seed': 1,
+ 'compare-dest': 2,
+ 'compress-choice': 1,
+ 'compress-level': 1,
+ 'copy-dest': 2,
+ 'copy-devices': -1,
+ 'copy-unsafe-links': 0,
+ 'daemon': -1,
+ 'debug': 1,
+ 'delay-updates': 0,
+ 'delete': 0,
+ 'delete-after': 0,
+ 'delete-before': 0,
+ 'delete-delay': 0,
+ 'delete-during': 0,
+ 'delete-excluded': 0,
+ 'delete-missing-args': 0,
+ 'existing': 0,
+ 'fake-super': 0,
+ 'files-from': 3,
+ 'force': 0,
+ 'from0': 0,
+ 'fsync': 0,
+ 'fuzzy': 0,
+ 'group': 0,
+ 'groupmap': 1,
+ 'hard-links': 0,
+ 'iconv': 1,
+ 'ignore-errors': 0,
+ 'ignore-existing': 0,
+ 'ignore-missing-args': 0,
+ 'ignore-times': 0,
+ 'info': 1,
+ 'inplace': 0,
+ 'link-dest': 2,
+ 'links': 0,
+ 'list-only': 0,
+ 'log-file': 3,
+ 'log-format': 1,
+ 'max-alloc': 1,
+ 'max-delete': 1,
+ 'max-size': 1,
+ 'min-size': 1,
+ 'mkpath': 0,
+ 'modify-window': 1,
+ 'msgs2stderr': 0,
+ 'munge-links': 0,
+ 'new-compress': 0,
+ 'no-W': 0,
+ 'no-implied-dirs': 0,
+ 'no-msgs2stderr': 0,
+ 'no-munge-links': -1,
+ 'no-r': 0,
+ 'no-relative': 0,
+ 'no-specials': 0,
+ 'numeric-ids': 0,
+ 'old-compress': 0,
+ 'one-file-system': 0,
+ 'only-write-batch': 1,
+ 'open-noatime': 0,
+ 'owner': 0,
+ 'partial': 0,
+ 'partial-dir': 2,
+ 'perms': 0,
+ 'preallocate': 0,
+ 'recursive': 0,
+ 'remove-sent-files': 0,
+ 'remove-source-files': 0,
+ 'safe-links': 0,
+ 'sender': 0,
+ 'server': 0,
+ 'size-only': 0,
+ 'skip-compress': 1,
+ 'specials': 0,
+ 'stats': 0,
+ 'stderr': 1,
+ 'suffix': 1,
+ 'super': 0,
+ 'temp-dir': 2,
+ 'timeout': 1,
+ 'times': 0,
+ 'use-qsort': 0,
+ 'usermap': 1,
+ 'write-devices': -1,
}
-die "$0: invalid rsync-command syntax or options\n" if $in_options;
-if ($subdir ne '/') {
- die "$0: do not use .. in any path!\n" if grep m{(^|/)\.\.(/|$)}, @args;
-}
+### END of options data produced by the cull-options script. ###
-@args = ( '.' ) if !@args;
+import os, sys, re, argparse, glob, socket, time, subprocess
+from argparse import RawTextHelpFormatter
-if ($write_log) {
- my ($mm,$hh) = (localtime)[1,2];
- my $host = $ENV{SSH_CONNECTION} || 'unknown';
- $host =~ s/ .*//; # Keep only the client's IP addr
- $host =~ s/^::ffff://;
- $host = gethostbyaddr(inet_aton($host),AF_INET) || $host;
- printf LOG "%02d:%02d %-13s [%s]\n", $hh, $mm, $host, "@opts @args";
- close LOG;
-}
+try:
+ from braceexpand import braceexpand
+except:
+ braceexpand = lambda x: [ DE_BACKSLASH_RE.sub(r'\1', x) ]
-# Note: This assumes that the rsync protocol will not be maliciously hijacked.
-exec(RSYNC, @opts, '--', @args) or die "exec(rsync @opts -- @args) failed: $? $!";
-
-sub check_arg
-{
- my($opt, $arg, $type) = @_;
- $arg =~ s/\\(.)/$1/g;
- if ($subdir ne '/' && ($type == 3 || ($type == 2 && !$am_sender))) {
- $arg =~ s{//}{/}g;
- die "Do not use .. in --$opt; anchor the path at the root of your restricted dir.\n"
- if $arg =~ m{(^|/)\.\.(/|$)};
- $arg =~ s{^/}{$subdir/};
- }
- $arg;
-}
+HAS_DOT_DOT_RE = re.compile(r'(^|/)\.\.(/|$)')
+LONG_OPT_RE = re.compile(r'^--([^=]+)(?:=(.*))?$')
+DE_BACKSLASH_RE = re.compile(r'\\(.)')
+
+def main():
+ if not os.path.isdir(args.dir):
+ die("Restricted directory does not exist!")
+
+ # The format of the environment variables set by sshd:
+ # SSH_ORIGINAL_COMMAND:
+ # rsync --server -vlogDtpre.iLsfxCIvu --etc . ARG # push
+ # rsync --server --sender -vlogDtpre.iLsfxCIvu --etc . ARGS # pull
+ # SSH_CONNECTION (client_ip client_port server_ip server_port):
+ # 192.168.1.100 64106 192.168.1.2 22
+
+ command = os.environ.get('SSH_ORIGINAL_COMMAND', None)
+ if not command:
+ die("Not invoked via sshd")
+ if command == 'true':
+ # Allow checking connectivity with "ssh true". (For example,
+ # rsbackup uses this.)
+ sys.exit(0)
+ command = command.split(' ', 2)
+ if command[0:1] != ['rsync']:
+ die("SSH_ORIGINAL_COMMAND does not run rsync")
+ if command[1:2] != ['--server']:
+ die("--server option is not the first arg")
+ command = '' if len(command) < 3 else command[2]
+
+ global am_sender
+ am_sender = command.startswith("--sender ") # Restrictive on purpose!
+ if args.ro and not am_sender:
+ die("sending to read-only server is not allowed")
+ if args.wo and am_sender:
+ die("reading from write-only server is not allowed")
+
+ if args.wo or not am_sender:
+ long_opts['sender'] = -1
+ if args.no_del:
+ for opt in long_opts:
+ if opt.startswith(('remove', 'delete')):
+ long_opts[opt] = -1
+ if args.ro:
+ long_opts['log-file'] = -1
+
+ if args.dir != '/':
+ global short_disabled
+ short_disabled += short_disabled_subdir
+
+ short_no_arg_re = short_no_arg
+ short_with_num_re = short_with_num
+ if short_disabled:
+ for ltr in short_disabled:
+ short_no_arg_re = short_no_arg_re.replace(ltr, '')
+ short_with_num_re = short_with_num_re.replace(ltr, '')
+ short_disabled_re = re.compile(r'^-[%s]*([%s])' % (short_no_arg_re, short_disabled))
+ short_no_arg_re = re.compile(r'^-(?=.)[%s]*(e\d*\.\w*)?$' % short_no_arg_re)
+ short_with_num_re = re.compile(r'^-[%s]\d+$' % short_with_num_re)
+
+ log_fh = open(LOGFILE, 'a') if os.path.isfile(LOGFILE) else None
+
+ try:
+ os.chdir(args.dir)
+ except OSError as e:
+ die('unable to chdir to restricted dir:', str(e))
+
+ rsync_opts = [ '--server' ]
+ rsync_args = [ ]
+ saw_the_dot_arg = False
+ last_opt = check_type = None
+
+ for arg in re.findall(r'(?:[^\s\\]+|\\.[^\s\\]*)+', command):
+ if check_type:
+ rsync_opts.append(validated_arg(last_opt, arg, check_type))
+ check_type = None
+ elif saw_the_dot_arg:
+ # NOTE: an arg that starts with a '-' is safe due to our use of "--" in the cmd tuple.
+ try:
+ b_e = braceexpand(arg) # Also removes backslashes
+ except: # Handle errors such as unbalanced braces by just de-backslashing the arg:
+ b_e = [ DE_BACKSLASH_RE.sub(r'\1', arg) ]
+ for xarg in b_e:
+ rsync_args += validated_arg('arg', xarg, wild=True)
+ else: # parsing the option args
+ if arg == '.':
+ saw_the_dot_arg = True
+ continue
+ rsync_opts.append(arg)
+ if short_no_arg_re.match(arg) or short_with_num_re.match(arg):
+ continue
+ disabled = False
+ m = LONG_OPT_RE.match(arg)
+ if m:
+ opt = m.group(1)
+ opt_arg = m.group(2)
+ ct = long_opts.get(opt, None)
+ if ct is None:
+ break # Generate generic failure due to unfinished arg parsing
+ if ct == 0:
+ continue
+ opt = '--' + opt
+ if ct > 0:
+ if opt_arg is not None:
+ rsync_opts[-1] = opt + '=' + validated_arg(opt, opt_arg, ct)
+ else:
+ check_type = ct
+ last_opt = opt
+ continue
+ disabled = True
+ elif short_disabled:
+ m = short_disabled_re.match(arg)
+ if m:
+ disabled = True
+ opt = '-' + m.group(1)
+
+ if disabled:
+ die("option", opt, "has been disabled on this server.")
+ break # Generate a generic failure
+
+ if not saw_the_dot_arg:
+ die("invalid rsync-command syntax or options")
+
+ if args.munge:
+ rsync_opts.append('--munge-links')
+
+ if args.no_overwrite:
+ rsync_opts.append('--ignore-existing')
+
+ if not rsync_args:
+ rsync_args = [ '.' ]
+
+ cmd = (RSYNC, *rsync_opts, '--', '.', *rsync_args)
+
+ if log_fh:
+ now = time.localtime()
+ host = os.environ.get('SSH_CONNECTION', 'unknown').split()[0] # Drop everything after the IP addr
+ if host.startswith('::ffff:'):
+ host = host[7:]
+ try:
+ host = socket.gethostbyaddr(socket.inet_aton(host))
+ except:
+ pass
+ log_fh.write("%02d:%02d:%02d %-16s %s\n" % (now.tm_hour, now.tm_min, now.tm_sec, host, str(cmd)))
+ log_fh.close()
+
+ # NOTE: This assumes that the rsync protocol will not be maliciously hijacked.
+ if args.no_lock:
+ os.execlp(RSYNC, *cmd)
+ die("execlp(", RSYNC, *cmd, ') failed')
+ child = subprocess.run(cmd)
+ if child.returncode != 0:
+ sys.exit(child.returncode)
+
+
+def validated_arg(opt, arg, typ=3, wild=False):
+ if opt != 'arg': # arg values already have their backslashes removed.
+ arg = DE_BACKSLASH_RE.sub(r'\1', arg)
+
+ orig_arg = arg
+ if arg.startswith('./'):
+ arg = arg[1:]
+ arg = arg.replace('//', '/')
+ if args.dir != '/':
+ if HAS_DOT_DOT_RE.search(arg):
+ die("do not use .. in", opt, "(anchor the path at the root of your restricted dir)")
+ if arg.startswith('/'):
+ arg = args.dir + arg
+
+ if wild:
+ got = glob.glob(arg)
+ if not got:
+ got = [ arg ]
+ else:
+ got = [ arg ]
+
+ ret = [ ]
+ for arg in got:
+ if args.dir != '/' and arg != '.' and (typ == 3 or (typ == 2 and not am_sender)):
+ arg_has_trailing_slash = arg.endswith('/')
+ if arg_has_trailing_slash:
+ arg = arg[:-1]
+ else:
+ arg_has_trailing_slash_dot = arg.endswith('/.')
+ if arg_has_trailing_slash_dot:
+ arg = arg[:-2]
+ real_arg = os.path.realpath(arg)
+ if arg != real_arg and not real_arg.startswith(args.dir_slash):
+ die('unsafe arg:', orig_arg, [arg, real_arg])
+ if arg_has_trailing_slash:
+ arg += '/'
+ elif arg_has_trailing_slash_dot:
+ arg += '/.'
+ if opt == 'arg' and arg.startswith(args.dir_slash):
+ arg = arg[args.dir_slash_len:]
+ if arg == '':
+ arg = '.'
+ ret.append(arg)
+
+ return ret if wild else ret[0]
+
+
+def lock_or_die(dirname):
+ import fcntl
+ global lock_handle
+ lock_handle = os.open(dirname, os.O_RDONLY)
+ try:
+ fcntl.flock(lock_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except:
+ die('Another instance of rrsync is already accessing this directory.')
+
+
+def die(*msg):
+ print(sys.argv[0], 'error:', *msg, file=sys.stderr)
+ if sys.stdin.isatty():
+ arg_parser.print_help(sys.stderr)
+ sys.exit(1)
+
+
+# This class displays the --help to the user on argparse error IFF they're running it interactively.
+class OurArgParser(argparse.ArgumentParser):
+ def error(self, msg):
+ die(msg)
+
+
+if __name__ == '__main__':
+ our_desc = """Use "man rrsync" to learn how to restrict ssh users to using a restricted rsync command."""
+ arg_parser = OurArgParser(description=our_desc, add_help=False)
+ only_group = arg_parser.add_mutually_exclusive_group()
+ only_group.add_argument('-ro', action='store_true', help="Allow only reading from the DIR. Implies -no-del and -no-lock.")
+ only_group.add_argument('-wo', action='store_true', help="Allow only writing to the DIR.")
+ arg_parser.add_argument('-munge', action='store_true', help="Enable rsync's --munge-links on the server side.")
+ arg_parser.add_argument('-no-del', action='store_true', help="Disable rsync's --delete* and --remove* options.")
+ arg_parser.add_argument('-no-lock', action='store_true', help="Avoid the single-run (per-user) lock check.")
+ arg_parser.add_argument('-no-overwrite', action='store_true', help="Prevent overwriting existing files by enforcing --ignore-existing")
+ arg_parser.add_argument('-help', '-h', action='help', help="Output this help message and exit.")
+ arg_parser.add_argument('dir', metavar='DIR', help="The restricted directory to use.")
+ args = arg_parser.parse_args()
+ args.dir = os.path.realpath(args.dir)
+ args.dir_slash = args.dir + '/'
+ args.dir_slash_len = len(args.dir_slash)
+ if args.ro:
+ args.no_del = True
+ elif not args.no_lock:
+ lock_or_die(args.dir)
+ main()
+
+# vim: sw=4 et
diff --git a/support/rrsync.1.md b/support/rrsync.1.md
new file mode 100644
index 000000000..09b2f2822
--- /dev/null
+++ b/support/rrsync.1.md
@@ -0,0 +1,171 @@
+## NAME
+
+rrsync - a script to setup restricted rsync users via ssh logins
+
+## SYNOPSIS
+
+```
+rrsync [-ro|-wo] [-munge] [-no-del] [-no-lock] [-no-overwrite] DIR
+```
+
+The single non-option argument specifies the restricted _DIR_ to use. It can be
+relative to the user's home directory or an absolute path.
+
+The online version of this manpage (that includes cross-linking of topics)
+is available at .
+
+## DESCRIPTION
+
+A user's ssh login can be restricted to only allow the running of an rsync
+transfer in one of two easy ways:
+
+* forcing the running of the rrsync script
+* forcing the running of an rsync daemon-over-ssh command.
+
+Both of these setups use a feature of ssh that allows a command to be forced to
+run instead of an interactive shell. However, if the user's home shell is bash,
+please see [BASH SECURITY ISSUE](#) for a potential issue.
+
+To use the rrsync script, edit the user's `~/.ssh/authorized_keys` file and add
+a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+
+> ```
+> command="rrsync DIR"
+> command="rrsync -ro DIR"
+> command="rrsync -munge -no-del DIR"
+> ```
+
+Then, ensure that the rrsync script has your desired option restrictions. You
+may want to copy the script to a local bin dir with a unique name if you want
+to have multiple configurations. One or more rrsync options can be specified
+prior to the _DIR_ if you want to further restrict the transfer.
+
+To use an rsync daemon setup, edit the user's `~/.ssh/authorized_keys` file and
+add a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+
+> ```
+> command="rsync --server --daemon ."
+> command="rsync --server --daemon --config=/PATH/TO/rsyncd.conf ."
+> ```
+
+Then, ensure that the rsyncd.conf file is created with one or more module names
+with the appropriate path and option restrictions. If rsync's
+[`--config`](rsync.1#dopt) option is omitted, it defaults to `~/rsyncd.conf`.
+See the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage for details of how to
+configure an rsync daemon.
+
+When using rrsync, there can be just one restricted dir per authorized key. A
+daemon setup, on the other hand, allows multiple module names inside the config
+file, each one with its own path setting.
+
+The remainder of this manpage is dedicated to using the rrsync script.
+
+## OPTIONS
+
+0. `-ro`
+
+ Allow only reading from the DIR. Implies [`-no-del`](#opt) and
+ [`-no-lock`](#opt).
+
+0. `-wo`
+
+ Allow only writing to the DIR.
+
+0. `-munge`
+
+ Enable rsync's [`--munge-links`](rsync.1#opt) on the server side.
+
+0. `-no-del`
+
+ Disable rsync's `--delete*` and `--remove*` options.
+
+0. `-no-lock`
+
+ Avoid the single-run (per-user) lock check. Useful with [`-munge`](#opt).
+
+0. `-no-overwrite`
+
+ Enforce `--ignore-existing` on the server. Prevents overwriting existing
+ files when the server is the receiver.
+
+0. `-help`, `-h`
+
+ Output this help message and exit.
+
+## SECURITY RESTRICTIONS
+
+The rrsync script validates the path arguments it is sent to try to restrict
+them to staying within the specified DIR.
+
+The rrsync script rejects rsync's [`--copy-links`](rsync.1#opt) option (by
+default) so that a copy cannot dereference a symlink within the DIR to get to a
+file outside the DIR.
+
+The rrsync script rejects rsync's [`--protect-args`](rsync.1#opt) (`-s`) option
+because it would allow options to be sent to the server-side that the script
+cannot check. If you want to support `--protect-args`, use a daemon-over-ssh
+setup.
+
+The rrsync script accepts just a subset of rsync's options that the real rsync
+uses when running the server command. A few extra convenience options are also
+included to help it to interact with BackupPC and accept some convenient user
+overrides.
+
+The script (or a copy of it) can be manually edited if you want it to customize
+the option handling.
+
+## BASH SECURITY ISSUE
+
+If your users have bash set as their home shell, bash may try to be overly
+helpful and ensure that the user's login bashrc files are run prior to
+executing the forced command. This can be a problem if the user can somehow
+update their home bashrc files, perhaps via the restricted copy, a shared home
+directory, or something similar.
+
+One simple way to avoid the issue is to switch the user to a simpler shell,
+such as dash. When choosing the new home shell, make sure that you're not
+choosing bash in disguise, as it is unclear if it avoids the security issue.
+
+Another potential fix is to ensure that the user's home directory is not a
+shared mount and that they have no means of copying files outside of their
+restricted directories. This may require you to force the enabling of symlink
+munging on the server side.
+
+A future version of openssh may have a change to the handling of forced
+commands that allows it to avoid using the user's home shell.
+
+## EXAMPLES
+
+The `~/.ssh/authorized_keys` file might have lines in it like this:
+
+> ```
+> command="rrsync client/logs" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzG...
+> command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmk...
+> ```
+
+## FILES
+
+~/.ssh/authorized_keys
+
+## SEE ALSO
+
+[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
+
+## VERSION
+
+This manpage is current for version @VERSION@ of rsync.
+
+## CREDITS
+
+rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
+
+An rsync web site is available at and its github
+project is .
+
+## AUTHOR
+
+The original rrsync perl script was written by Joe Smith. Many people have
+later contributed to it. The python version was created by Wayne Davison.
diff --git a/support/rsync-no-vanished b/support/rsync-no-vanished
index 0f0bb22f5..b31a5d210 100755
--- a/support/rsync-no-vanished
+++ b/support/rsync-no-vanished
@@ -1,12 +1,21 @@
#!/usr/bin/env bash
+REAL_RSYNC=/usr/bin/rsync
IGNOREEXIT=24
IGNOREOUT='^(file has vanished: |rsync warning: some files vanished before they could be transferred)'
+# If someone installs this as "rsync", make sure we don't affect a server run.
+for arg in "${@}"; do
+ if [[ "$arg" == --server ]]; then
+ exec $REAL_RSYNC "${@}"
+ exit $? # Not reached
+ fi
+done
+
set -o pipefail
-rsync "${@}" 2>&1 | (grep -E -v "$IGNOREOUT" || true)
-ret=$?
+# This filters stderr without merging it with stdout:
+{ $REAL_RSYNC "${@}" 2>&1 1>&3 3>&- | grep -E -v "$IGNOREOUT"; ret=${PIPESTATUS[0]}; } 3>&1 1>&2
if [[ $ret == $IGNOREEXIT ]]; then
ret=0
diff --git a/support/rsync-slash-strip b/support/rsync-slash-strip
index 2869e45cb..b57e61c53 100755
--- a/support/rsync-slash-strip
+++ b/support/rsync-slash-strip
@@ -6,12 +6,19 @@
#
# To use this, name it something like "rs", put it somewhere in your path, and
# then use "rs" in place of "rsync" when you are typing your copy commands.
+
+REAL_RSYNC=/usr/bin/rsync
+
args=()
for arg in "${@}"; do
+ if [[ "$arg" == --server ]]; then
+ exec $REAL_RSYNC "${@}"
+ exit $? # Not reached
+ fi
if [[ "$arg" == / ]]; then
args=("${args[@]}" /)
else
args=("${args[@]}" "${arg%/}")
fi
done
-exec /usr/bin/rsync "${args[@]}"
+exec $REAL_RSYNC "${args[@]}"
diff --git a/syscall.c b/syscall.c
index 56948a832..34a9bba08 100644
--- a/syscall.c
+++ b/syscall.c
@@ -4,7 +4,7 @@
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +33,8 @@
#include
#endif
+#include "ifuncs.h"
+
extern int dry_run;
extern int am_root;
extern int am_sender;
@@ -43,6 +45,8 @@ extern int preallocate_files;
extern int preserve_perms;
extern int preserve_executability;
extern int open_noatime;
+extern int copy_links;
+extern int copy_unsafe_links;
#ifndef S_BLKSIZE
# if defined hpux || defined __hpux__ || defined __hpux
@@ -247,7 +251,7 @@ int do_chmod(const char *path, mode_t mode)
else if (errno != ENOTSUP)
break;
#endif
-
+ /* FALLTHROUGH */
default:
if (S_ISLNK(mode)) {
# if defined HAVE_SETATTRLIST
@@ -257,7 +261,10 @@ int do_chmod(const char *path, mode_t mode)
memset(&attrList, 0, sizeof attrList);
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.commonattr = ATTR_CMN_ACCESSMASK;
- code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW);
+ if ((code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW)) == 0)
+ break;
+ if (errno == ENOTSUP)
+ code = 1;
# else
code = 1;
# endif
@@ -385,11 +392,6 @@ int do_fstat(int fd, STRUCT_STAT *st)
OFF_T do_lseek(int fd, OFF_T offset, int whence)
{
#ifdef HAVE_LSEEK64
-#if !SIZEOF_OFF64_T
- OFF_T lseek64();
-#else
- off64_t lseek64();
-#endif
return lseek64(fd, offset, whence);
#else
return lseek(fd, offset, whence);
@@ -709,3 +711,100 @@ int do_open_nofollow(const char *pathname, int flags)
return fd;
}
+
+/*
+ open a file relative to a base directory. The basedir can be NULL,
+ in which case the current working directory is used. The relpath
+ must be a relative path, and the relpath must not contain any
+ elements in the path which follow symlinks (ie. like O_NOFOLLOW, but
+ applies to all path components, not just the last component)
+
+ The relpath must also not contain any ../ elements in the path
+*/
+int secure_relative_open(const char *basedir, const char *relpath, int flags, mode_t mode)
+{
+ if (!relpath || relpath[0] == '/') {
+ // must be a relative path
+ errno = EINVAL;
+ return -1;
+ }
+ if (strncmp(relpath, "../", 3) == 0 || strstr(relpath, "/../")) {
+ // no ../ elements allowed in the relpath
+ errno = EINVAL;
+ return -1;
+ }
+
+#if !defined(O_NOFOLLOW) || !defined(O_DIRECTORY) || !defined(AT_FDCWD)
+ // really old system, all we can do is live with the risks
+ if (!basedir) {
+ return open(relpath, flags, mode);
+ }
+ char fullpath[MAXPATHLEN];
+ pathjoin(fullpath, sizeof fullpath, basedir, relpath);
+ return open(fullpath, flags, mode);
+#else
+ int dirfd = AT_FDCWD;
+ if (basedir != NULL) {
+ dirfd = openat(AT_FDCWD, basedir, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ return -1;
+ }
+ }
+ int retfd = -1;
+
+ char *path_copy = my_strdup(relpath, __FILE__, __LINE__);
+ if (!path_copy) {
+ return -1;
+ }
+
+ for (const char *part = strtok(path_copy, "/");
+ part != NULL;
+ part = strtok(NULL, "/"))
+ {
+ int next_fd = openat(dirfd, part, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (next_fd == -1 && errno == ENOTDIR) {
+ if (strtok(NULL, "/") != NULL) {
+ // this is not the last component of the path
+ errno = ELOOP;
+ goto cleanup;
+ }
+ // this could be the last component of the path, try as a file
+ retfd = openat(dirfd, part, flags | O_NOFOLLOW, mode);
+ goto cleanup;
+ }
+ if (next_fd == -1) {
+ goto cleanup;
+ }
+ if (dirfd != AT_FDCWD) close(dirfd);
+ dirfd = next_fd;
+ }
+
+ // the path must be a directory
+ errno = EINVAL;
+
+cleanup:
+ free(path_copy);
+ if (dirfd != AT_FDCWD) {
+ close(dirfd);
+ }
+ return retfd;
+#endif // O_NOFOLLOW, O_DIRECTORY
+}
+
+/*
+ varient of do_open/do_open_nofollow which does do_open() if the
+ copy_links or copy_unsafe_links options are set and does
+ do_open_nofollow() otherwise
+
+ This is used to prevent a race condition where an attacker could be
+ switching a file between being a symlink and being a normal file
+
+ The open is always done with O_RDONLY flags
+ */
+int do_open_checklinks(const char *pathname)
+{
+ if (copy_links || copy_unsafe_links) {
+ return do_open(pathname, O_RDONLY, 0);
+ }
+ return do_open_nofollow(pathname, O_RDONLY);
+}
diff --git a/t_stub.c b/t_stub.c
index 12f7b622c..eee927299 100644
--- a/t_stub.c
+++ b/t_stub.c
@@ -3,7 +3,7 @@
* functions, so that module test harnesses can run standalone.
*
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,8 +28,7 @@ int preallocate_files = 0;
int protect_args = 0;
int module_id = -1;
int relative_paths = 0;
-int module_dirlen = 0;
-int preserve_mtimes = 0;
+unsigned int module_dirlen = 0;
int preserve_xattrs = 0;
int preserve_perms = 0;
int preserve_executability = 0;
diff --git a/t_unsafe.c b/t_unsafe.c
index 010cac50d..e10619a2a 100644
--- a/t_unsafe.c
+++ b/t_unsafe.c
@@ -28,6 +28,9 @@ int am_root = 0;
int am_sender = 1;
int read_only = 0;
int list_only = 0;
+int copy_links = 0;
+int copy_unsafe_links = 0;
+
short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
int
diff --git a/testsuite/00-hello.test b/testsuite/00-hello.test
index ed72c0bdc..ebd068365 100644
--- a/testsuite/00-hello.test
+++ b/testsuite/00-hello.test
@@ -1,9 +1,11 @@
#!/bin/sh
-test_fail() {
- echo "$@" >&2
- exit 1
-}
+# Test some foundational things.
+
+. "$suitedir/rsync.fns"
+
+RSYNC_RSH="$scratchdir/src/support/lsh.sh"
+export RSYNC_RSH
echo $0 running
@@ -12,3 +14,48 @@ $RSYNC --version || test_fail '--version output failed'
$RSYNC --info=help || test_fail '--info=help output failed'
$RSYNC --debug=help || test_fail '--debug=help output failed'
+
+weird_name="A weird)name"
+
+mkdir "$fromdir"
+mkdir "$fromdir/$weird_name"
+
+append_line() {
+ echo "$1"
+ echo "$1" >>"$fromdir/$weird_name/file"
+}
+
+append_line test1
+checkit "$RSYNC -ai '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+copy_weird() {
+ checkit "$RSYNC $1 --rsync-path='$RSYNC' '$2$fromdir/$weird_name/' '$3$todir/$weird_name'" "$fromdir" "$todir"
+}
+
+append_line test2
+copy_weird '-ai' 'lh:' ''
+
+append_line test3
+copy_weird '-ai' '' 'lh:'
+
+append_line test4
+copy_weird '-ais' 'lh:' ''
+
+append_line test5
+copy_weird '-ais' '' 'lh:'
+
+echo test6
+
+touch "$fromdir/one" "$fromdir/two"
+(cd "$fromdir" && $RSYNC -ai --old-args --rsync-path="$RSYNC" lh:'one two' "$todir/")
+if [ ! -f "$todir/one" ] || [ ! -f "$todir/two" ]; then
+ test_fail "old-args copy of 'one two' failed"
+fi
+
+echo test7
+
+rm "$todir/one" "$todir/two"
+(cd "$fromdir" && RSYNC_OLD_ARGS=1 $RSYNC -ai --rsync-path="$RSYNC" lh:'one two' "$todir/")
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/acls-default.test b/testsuite/acls-default.test
index a0a482ced..d8fba7fee 100644
--- a/testsuite/acls-default.test
+++ b/testsuite/acls-default.test
@@ -7,7 +7,7 @@
. $suitedir/rsync.fns
-$RSYNC --version | grep "[, ] ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
+$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
case "$setfacl_nodef" in
true) test_skipped "I don't know how to use your setfacl command" ;;
diff --git a/testsuite/acls.test b/testsuite/acls.test
index 23449018a..693da6677 100644
--- a/testsuite/acls.test
+++ b/testsuite/acls.test
@@ -7,7 +7,7 @@
. $suitedir/rsync.fns
-$RSYNC --version | grep "[, ] ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
+$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
makepath "$fromdir/foo"
echo something >"$fromdir/file1"
diff --git a/testsuite/alt-dest.test b/testsuite/alt-dest.test
new file mode 100644
index 000000000..d2fb5a1bb
--- /dev/null
+++ b/testsuite/alt-dest.test
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of --compare-dest and similar options.
+
+. "$suitedir/rsync.fns"
+
+alt1dir="$tmpdir/alt1"
+alt2dir="$tmpdir/alt2"
+alt3dir="$tmpdir/alt3"
+
+SSH="$scratchdir/src/support/lsh.sh"
+
+# Build some files/dirs/links to copy
+
+hands_setup
+
+# Setup the alt and chk dirs
+$RSYNC -av --include=text --include='*/' --exclude='*' "$fromdir/" "$alt1dir/"
+$RSYNC -av --include=etc-ltr-list --include='*/' --exclude='*' "$fromdir/" "$alt2dir/"
+
+# Create a side dir where there is a candidate destfile of the same name as a sourcefile
+echo "This is a test file" >"$fromdir/likely"
+
+mkdir "$alt3dir"
+echo "This is a test file" >"$alt3dir/likely"
+
+sleep 1
+touch "$fromdir/dir/text" "$fromdir/likely"
+
+$RSYNC -av --exclude=/text --exclude=etc-ltr-list "$fromdir/" "$chkdir/"
+
+# Let's do it!
+checkit "$RSYNC -avv --no-whole-file \
+ --compare-dest='$alt1dir' --compare-dest='$alt2dir' \
+ '$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+rm -rf "$todir"
+checkit "$RSYNC -avv --no-whole-file \
+ --copy-dest='$alt1dir' --copy-dest='$alt2dir' \
+ '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+# Test that copy_file() works correctly with tmpfiles
+for maybe_inplace in '' --inplace; do
+ rm -rf "$todir"
+ checkit "$RSYNC -av $maybe_inplace --copy-dest='$alt3dir' \
+ '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+ for srchost in '' 'localhost:'; do
+ if [ -z "$srchost" ]; then
+ desthost='localhost:'
+ else
+ desthost=''
+ fi
+
+ rm -rf "$todir"
+ checkit "$RSYNC -ave '$SSH' --rsync-path='$RSYNC' $maybe_inplace \
+ --copy-dest='$alt3dir' '$srchost$fromdir/' '$desthost$todir/'" \
+ "$fromdir" "$todir"
+ done
+done
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/atimes.test b/testsuite/atimes.test
index 3bdb1d463..4d46eb057 100644
--- a/testsuite/atimes.test
+++ b/testsuite/atimes.test
@@ -4,7 +4,7 @@
. "$suitedir/rsync.fns"
-$RSYNC --version | grep "[, ] atimes" >/dev/null || test_skipped "Rsync is configured without atimes support"
+$RSYNC -VV | grep '"atimes": true' >/dev/null || test_skipped "Rsync is configured without atimes support"
mkdir "$fromdir"
diff --git a/testsuite/backup.test b/testsuite/backup.test
index 925c9fe0b..4de38674d 100644
--- a/testsuite/backup.test
+++ b/testsuite/backup.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2004-2020 Wayne Davison
+# Copyright (C) 2004-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
diff --git a/testsuite/chmod-temp-dir.test b/testsuite/chmod-temp-dir.test
index e5541e4c9..362d9d993 100644
--- a/testsuite/chmod-temp-dir.test
+++ b/testsuite/chmod-temp-dir.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2004-2020 Wayne Davison
+# Copyright (C) 2004-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
@@ -17,7 +17,7 @@ sdev=`$TOOLDIR/getfsdev $scratchdir`
tdev=$sdev
for tmpdir2 in "${RSYNC_TEST_TMP:-/override-tmp-not-specified}" /run/shm /var/tmp /tmp; do
- [ -d "$tmpdir2" -a -w "$tmpdir2" ] || continue
+ [ -d "$tmpdir2" ] && [ -w "$tmpdir2" ] || continue
tdev=`$TOOLDIR/getfsdev "$tmpdir2"`
[ x$sdev != x$tdev ] && break
done
diff --git a/testsuite/chmod.test b/testsuite/chmod.test
index 63258df7c..1646a9c81 100644
--- a/testsuite/chmod.test
+++ b/testsuite/chmod.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2004-2020 Wayne Davison
+# Copyright (C) 2004-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
diff --git a/testsuite/chown.test b/testsuite/chown.test
index 5dadb836c..b53413e1e 100644
--- a/testsuite/chown.test
+++ b/testsuite/chown.test
@@ -15,7 +15,7 @@
case $0 in
*fake*)
- $RSYNC --version | grep "[, ] xattrs" >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
+ $RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
RSYNC="$RSYNC --fake-super"
TLS_ARGS="$TLS_ARGS --fake-super"
case "$HOST_OS" in
diff --git a/testsuite/compare-dest.test b/testsuite/compare-dest.test
deleted file mode 100644
index 3c6348576..000000000
--- a/testsuite/compare-dest.test
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-
-# Copyright (C) 2004-2020 Wayne Davison
-
-# This program is distributable under the terms of the GNU GPL (see
-# COPYING).
-
-# Test rsync handling of the --compare-dest option.
-
-. "$suitedir/rsync.fns"
-
-alt1dir="$tmpdir/alt1"
-alt2dir="$tmpdir/alt2"
-
-# Build some files/dirs/links to copy
-
-hands_setup
-
-# Setup the alt and chk dirs
-$RSYNC -av --include=text --include='*/' --exclude='*' "$fromdir/" "$alt1dir/"
-$RSYNC -av --include=etc-ltr-list --include='*/' --exclude='*' "$fromdir/" "$alt2dir/"
-
-sleep 1
-touch "$fromdir/dir/text"
-
-$RSYNC -av --exclude=/text --exclude=etc-ltr-list "$fromdir/" "$chkdir/"
-
-# Let's do it!
-checkit "$RSYNC -avv --no-whole-file \
- --compare-dest='$alt1dir' --compare-dest='$alt2dir' \
- '$fromdir/' '$todir/'" "$chkdir" "$todir"
-checkit "$RSYNC -avv --no-whole-file \
- --copy-dest='$alt1dir' --copy-dest='$alt2dir' \
- '$fromdir/' '$todir/'" "$fromdir" "$todir"
-
-# The script would have aborted on error, so getting here means we've won.
-exit 0
diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
index 51bfe301c..456f0a5f1 100644
--- a/testsuite/crtimes.test
+++ b/testsuite/crtimes.test
@@ -4,13 +4,13 @@
. "$suitedir/rsync.fns"
-$RSYNC --version | grep "[, ] crtimes" >/dev/null || test_skipped "Rsync is configured without crtimes support"
+$RSYNC -VV | grep '"crtimes": true' >/dev/null || test_skipped "Rsync is configured without crtimes support"
# Setting an older time via touch sets the create time to the mtime.
# Setting it to a newer time affects just the mtime.
mkdir "$fromdir"
-echo hiho "$fromdir/foo"
+echo hiho >"$fromdir/foo"
touch -t 200101011111.11 "$fromdir"
touch -t 200202022222.22 "$fromdir"
diff --git a/testsuite/daemon.test b/testsuite/daemon.test
index 80d2baf65..60aa334b7 100644
--- a/testsuite/daemon.test
+++ b/testsuite/daemon.test
@@ -81,7 +81,7 @@ drwxr-xr-x DIR ####/##/## ##:##:## foo
EOT
diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
-if $RSYNC --version | grep "[, ] atimes" >/dev/null; then
+if $RSYNC -VV | grep '"atimes": true' >/dev/null; then
checkdiff "$RSYNC -rU localhost::test-from/f*" \
"sed -e '$FILE_REPL' -e '$DIR_REPL' -e '$LS_REPL'" </dev/null || test_skipped "Rsync needs xattrs for fake device tests"
+ $RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
RSYNC="$RSYNC --fake-super"
TLS_ARGS="$TLS_ARGS --fake-super"
case "$HOST_OS" in
@@ -94,7 +94,7 @@ esac
# TODO: Need to test whether hardlinks are possible on this OS/filesystem
-$RSYNC --version | grep "[, ] hardlink-special" >/dev/null && CAN_HLINK_SPECIAL=yes || CAN_HLINK_SPECIAL=no
+$RSYNC -VV | grep '"hardlink_specials": true' >/dev/null && CAN_HLINK_SPECIAL=yes || CAN_HLINK_SPECIAL=no
mkdir "$fromdir"
mkdir "$todir"
diff --git a/testsuite/exclude-lsh.test b/testsuite/exclude-lsh.test
new file mode 120000
index 000000000..84bc98ac2
--- /dev/null
+++ b/testsuite/exclude-lsh.test
@@ -0,0 +1 @@
+exclude.test
\ No newline at end of file
diff --git a/testsuite/exclude.test b/testsuite/exclude.test
index bd21c51cc..56b68b8ca 100644
--- a/testsuite/exclude.test
+++ b/testsuite/exclude.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2003-2020 Wayne Davison
+# Copyright (C) 2003-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
@@ -15,6 +15,19 @@
CVSIGNORE='*.junk'
export CVSIGNORE
+case $0 in
+*-lsh.*)
+ RSYNC_RSH="$scratchdir/src/support/lsh.sh"
+ export RSYNC_RSH
+ rpath=" --rsync-path='$RSYNC'"
+ host='lh:'
+ ;;
+*)
+ rpath=''
+ host=''
+ ;;
+esac
+
# Build some files/dirs/links to copy
makepath "$fromdir/foo/down/to/you"
@@ -106,8 +119,8 @@ home-cvs-exclude
EOF
# Start with a check of --prune-empty-dirs:
-$RSYNC -av -f -_foo/too/ -f -_foo/down/ -f -_foo/and/ -f -_new/ "$fromdir/" "$chkdir/"
-checkit "$RSYNC -av --prune-empty-dirs '$fromdir/' '$todir/'" "$chkdir" "$todir"
+$RSYNC -av --rsync-path="$RSYNC" -f -_foo/too/ -f -_foo/down/ -f -_foo/and/ -f -_new/ "$host$fromdir/" "$chkdir/"
+checkit "$RSYNC -av$rpath --prune-empty-dirs '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
rm -rf "$todir"
# Add a directory symlink.
@@ -120,7 +133,7 @@ touch "$scratchdir/up1/same-newness" "$scratchdir/up2/same-newness"
touch "$scratchdir/up1/extra-src" "$scratchdir/up2/extra-dest"
# Create chkdir with what we expect to be excluded.
-checkit "$RSYNC -avv '$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
+checkit "$RSYNC -avv$rpath '$host$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
sleep 1 # Ensures that the rm commands will tweak the directory times.
rm -r "$chkdir"/foo/down
rm -r "$chkdir"/mid/for/foo/and
@@ -135,12 +148,12 @@ touch "$scratchdir/up1/src-newness" "$scratchdir/up2/dst-newness"
# Un-tweak the directory times in our first (weak) exclude test (though
# it's a good test of the --existing option).
-$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
+$RSYNC -av --rsync-path="$RSYNC" --existing --include='*/' --exclude='*' "$host$fromdir/" "$chkdir/"
# Now, test if rsync excludes the same files.
-checkit "$RSYNC -avv --exclude-from='$excl' \
- --delete-during '$fromdir/' '$todir/'" "$chkdir" "$todir"
+checkit "$RSYNC -avv$rpath --exclude-from='$excl' \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
# Modify the chk dir by removing cvs-ignored files and then tweaking the dir times.
@@ -150,13 +163,15 @@ rm "$chkdir"/bar/down/to/foo/*.junk
rm "$chkdir"/bar/down/to/home-cvs-exclude
rm "$chkdir"/mid/one-in-one-out
-$RSYNC -av --existing --filter='exclude,! */' "$fromdir/" "$chkdir/"
+$RSYNC -av --rsync-path="$RSYNC" --existing --filter='exclude,! */' "$host$fromdir/" "$chkdir/"
# Now, test if rsync excludes the same files, this time with --cvs-exclude
# and --delete-excluded.
-checkit "$RSYNC -avvC --filter='merge $excl' --delete-excluded \
- --delete-during '$fromdir/' '$todir/'" "$chkdir" "$todir"
+# The -C option gets applied in a different order when pushing & pulling, so we instead
+# add the 2 --cvs-exclude filter rules (":C" & "-C") via -f to keep the order the same.
+checkit "$RSYNC -avv$rpath --filter='merge $excl' -f:C -f-C --delete-excluded \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
# Modify the chk dir for our merge-exclude test and then tweak the dir times.
@@ -165,19 +180,19 @@ rm "$chkdir"/bar/down/to/bar/baz/*.deep
cp_touch "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
cp_touch "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
-$RSYNC -av --existing -f 'show .filt*' -f 'hide,! */' --del "$fromdir/" "$todir/"
+$RSYNC -av --rsync-path="$RSYNC" --existing -f 'show .filt*' -f 'hide,! */' --del "$host$fromdir/" "$todir/"
echo retained >"$todir"/bar/down/to/bar/baz/nodel.deep
cp_touch "$todir"/bar/down/to/bar/baz/nodel.deep "$chkdir"/bar/down/to/bar/baz
-$RSYNC -av --existing --filter='-! */' "$fromdir/" "$chkdir/"
+$RSYNC -av --rsync-path="$RSYNC" --existing --filter='-! */' "$host$fromdir/" "$chkdir/"
# Now, test if rsync excludes the same files, this time with a merge-exclude
# file.
checkit "sed '/!/d' '$excl' |
- $RSYNC -avv -f dir-merge_.filt -f merge_- \
- --delete-during '$fromdir/' '$todir/'" "$chkdir" "$todir"
+ $RSYNC -avv$rpath -f dir-merge_.filt -f merge_- \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
# Remove the files that will be deleted.
@@ -188,14 +203,14 @@ rm "$chkdir"/bar/down/to/foo/.filt2
rm "$chkdir"/bar/down/to/bar/.filt2
rm "$chkdir"/mid/.filt
-$RSYNC -av --protocol=28 --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
+$RSYNC -av --rsync-path="$RSYNC" --existing --include='*/' --exclude='*' "$host$fromdir/" "$chkdir/"
# Now, try the prior command with --delete-before and some side-specific
# rules.
checkit "sed '/!/d' '$excl' |
- $RSYNC -avv -f :s_.filt -f .s_- -f P_nodel.deep \
- --delete-before '$fromdir/' '$todir/'" "$chkdir" "$todir"
+ $RSYNC -avv$rpath -f :s_.filt -f .s_- -f P_nodel.deep \
+ --delete-before '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
# Next, we'll test some rule-restricted filter files.
@@ -206,26 +221,26 @@ cat >"$fromdir/bar/down/to/foo/.excl" <f$all_plus extra-src
diff --git a/testsuite/fuzzy.test b/testsuite/fuzzy.test
index 2415173de..101ffd3c1 100644
--- a/testsuite/fuzzy.test
+++ b/testsuite/fuzzy.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2005-2020 Wayne Davison
+# Copyright (C) 2005-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
diff --git a/testsuite/hardlinks.test b/testsuite/hardlinks.test
index af2ea4088..68fd2701c 100644
--- a/testsuite/hardlinks.test
+++ b/testsuite/hardlinks.test
@@ -77,5 +77,11 @@ rm -rf "$todir"
$RSYNC -aHivv --debug=HLINK5 "$name1" "$todir/"
diff $diffopt "$name1" "$todir" || test_fail "solo copy of name1 failed"
+# Make sure there's nothing wrong with sending a single directory with -H
+# enabled (this has broken in 3.4.0 so far, so we need this test).
+rm -rf "$fromdir" "$todir"
+makepath "$fromdir/sym" "$todir"
+checkit "$RSYNC -aH '$fromdir/sym' '$todir'" "$fromdir" "$todir"
+
# The script would have aborted on error, so getting here means we've won.
exit 0
diff --git a/testsuite/itemize.test b/testsuite/itemize.test
index 923272389..c1c57c59a 100644
--- a/testsuite/itemize.test
+++ b/testsuite/itemize.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2005-2020 Wayne Davison
+# Copyright (C) 2005-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
@@ -25,7 +25,7 @@ ln "$fromdir/foo/config1" "$fromdir/foo/extra"
rm -f "$to2dir"
# Check if rsync is set to hard-link symlinks.
-if $RSYNC --version | grep "[, ] hardlink-symlinks" >/dev/null; then
+if $RSYNC -VV | grep '"hardlink_symlinks": true' >/dev/null; then
L=hL
sym_dots="$allspace"
L_sym_dots=".L$allspace"
@@ -45,7 +45,7 @@ case "$RSYNC" in
T=.T
;;
*)
- if $RSYNC --version | grep "[, ] symtimes" >/dev/null; then
+ if $RSYNC -VV | grep '"symtimes": true' >/dev/null; then
T=.t
else
T=.T
diff --git a/testsuite/merge.test b/testsuite/merge.test
index 5f9a6da66..17050a1d8 100644
--- a/testsuite/merge.test
+++ b/testsuite/merge.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2004-2020 Wayne Davison
+# Copyright (C) 2004-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
index 1e2b399f0..2ab97b69c 100644
--- a/testsuite/rsync.fns
+++ b/testsuite/rsync.fns
@@ -65,7 +65,7 @@ set_cp_destdir() {
# even if the copy rounded microsecond times on the destination file.
cp_touch() {
cp_p "${@}"
- if test $# -gt 2 -o -d "$2"; then
+ if test $# -gt 2 || test -d "$2"; then
set_cp_destdir "${@}" # sets destdir var
while test $# -gt 1; do
destname="$destdir/`basename $1`"
diff --git a/testsuite/safe-links.test b/testsuite/safe-links.test
new file mode 100644
index 000000000..6e95a4b93
--- /dev/null
+++ b/testsuite/safe-links.test
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+. "$suitedir/rsync.fns"
+
+test_symlink() {
+ is_a_link "$1" || test_fail "File $1 is not a symlink"
+}
+
+test_regular() {
+ if [ ! -f "$1" ]; then
+ test_fail "File $1 is not regular file or not exists"
+ fi
+}
+
+test_notexist() {
+ if [ -e "$1" ]; then
+ test_fail "File $1 exists"
+ fi
+ if [ -h "$1" ]; then
+ test_fail "File $1 exists as a symlink"
+ fi
+}
+
+cd "$tmpdir"
+
+mkdir from
+
+mkdir "from/safe"
+mkdir "from/unsafe"
+
+mkdir "from/safe/files"
+mkdir "from/safe/links"
+
+touch "from/safe/files/file1"
+touch "from/safe/files/file2"
+touch "from/unsafe/unsafefile"
+
+ln -s ../files/file1 "from/safe/links/"
+ln -s ../files/file2 "from/safe/links/"
+ln -s ../../unsafe/unsafefile "from/safe/links/"
+ln -s a/a/a/../../../unsafe2 "from/safe/links/"
+
+#echo "LISTING FROM"
+#ls -lR from
+
+echo "rsync with relative path and just -a"
+$RSYNC -avv --safe-links from/safe/ to
+
+#echo "LISTING TO"
+#ls -lR to
+
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_notexist to/links/unsafefile
+test_notexist to/links/unsafe2
diff --git a/testsuite/unsafe-byname.test b/testsuite/unsafe-byname.test
index 75e720145..d2e318ef4 100644
--- a/testsuite/unsafe-byname.test
+++ b/testsuite/unsafe-byname.test
@@ -40,7 +40,7 @@ test_unsafe ..//../dest from/dir unsafe
test_unsafe .. from/file safe
test_unsafe ../.. from/file unsafe
test_unsafe ..//.. from//file unsafe
-test_unsafe dir/.. from safe
+test_unsafe dir/.. from unsafe
test_unsafe dir/../.. from unsafe
test_unsafe dir/..//.. from unsafe
diff --git a/testsuite/wildmatch.test b/testsuite/wildmatch.test
index fa225d90f..cfe758412 100644
--- a/testsuite/wildmatch.test
+++ b/testsuite/wildmatch.test
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2003-2020 Wayne Davison
+# Copyright (C) 2003-2022 Wayne Davison
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
diff --git a/testsuite/xattrs.test b/testsuite/xattrs.test
index 455abef1b..d94d5f950 100644
--- a/testsuite/xattrs.test
+++ b/testsuite/xattrs.test
@@ -8,7 +8,7 @@
. $suitedir/rsync.fns
lnkdir="$tmpdir/lnk"
-$RSYNC --version | grep "[, ] xattrs" >/dev/null || test_skipped "Rsync is configured without xattr support"
+$RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync is configured without xattr support"
case "$HOST_OS" in
darwin*)
diff --git a/tls.c b/tls.c
index 966986745..858f8f10c 100644
--- a/tls.c
+++ b/tls.c
@@ -2,7 +2,7 @@
* Trivial ls for comparing two directories after running an rsync.
*
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,6 +49,9 @@ int list_only = 0;
int link_times = 0;
int link_owner = 0;
int nsec_times = 0;
+int safe_symlinks = 0;
+int copy_links = 0;
+int copy_unsafe_links = 0;
#ifdef SUPPORT_XATTRS
diff --git a/token.c b/token.c
index 3a6d069ed..c108b3af5 100644
--- a/token.c
+++ b/token.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +39,6 @@ extern char *skip_compress;
#define Z_INSERT_ONLY Z_SYNC_FLUSH
#endif
-static int compression_level; /* The compression level for the current file. */
static int skip_compression_level; /* The least possible compressing for handling skip-compress files. */
static int per_file_default_level; /* The default level that each new file gets prior to checking its suffix. */
@@ -224,9 +223,11 @@ static void init_set_compression(void)
/* determine the compression level based on a wildcard filename list */
void set_compression(const char *fname)
{
+#if 0 /* No compression algorithms currently allow mid-stream changing of the level. */
const struct suffix_tree *node;
const char *s;
char ltr;
+#endif
if (!do_compression)
return;
@@ -234,6 +235,7 @@ void set_compression(const char *fname)
if (!match_list)
init_set_compression();
+#if 0
compression_level = per_file_default_level;
if (!*match_list && !suftree)
@@ -270,6 +272,9 @@ void set_compression(const char *fname)
if (!(node = node->child))
return;
}
+#else
+ (void)fname;
+#endif
}
/* non-compressing recv token */
@@ -361,7 +366,7 @@ send_deflated_token(int f, int32 token, struct map_struct *buf, OFF_T offset, in
tx_strm.next_in = NULL;
tx_strm.zalloc = NULL;
tx_strm.zfree = NULL;
- if (deflateInit2(&tx_strm, compression_level,
+ if (deflateInit2(&tx_strm, per_file_default_level,
Z_DEFLATED, -15, 8,
Z_DEFAULT_STRATEGY) != Z_OK) {
rprintf(FERROR, "compression init failed\n");
diff --git a/trimslash.c b/trimslash.c
index 1ec928cac..f2774cd73 100644
--- a/trimslash.c
+++ b/trimslash.c
@@ -26,6 +26,8 @@ int am_root = 0;
int am_sender = 1;
int read_only = 1;
int list_only = 0;
+int copy_links = 0;
+int copy_unsafe_links = 0;
int
main(int argc, char **argv)
diff --git a/uidlist.c b/uidlist.c
index 6100b5035..99a34679a 100644
--- a/uidlist.c
+++ b/uidlist.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
- * Copyright (C) 2004-2020 Wayne Davison
+ * Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -210,7 +210,7 @@ static int is_in_group(gid_t gid)
ngroups = getgroups(ngroups, gidset);
/* The default gid might not be in the list on some systems. */
for (n = 0; n < ngroups; n++) {
- if (gidset[n] == our_gid)
+ if ((gid_t)gidset[n] == our_gid)
break;
}
if (n == ngroups)
@@ -229,7 +229,7 @@ static int is_in_group(gid_t gid)
last_in = gid;
for (n = 0; n < ngroups; n++) {
- if (gidset[n] == gid)
+ if ((gid_t)gidset[n] == gid)
return last_out = 1;
}
return last_out = 0;
diff --git a/usage.c b/usage.c
index db13535f4..f346385f4 100644
--- a/usage.c
+++ b/usage.c
@@ -1,7 +1,7 @@
/*
* Some usage & version related functions.
*
- * Copyright (C) 2002-2020 Wayne Davison
+ * Copyright (C) 2002-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,9 +22,9 @@
#include "latest-year.h"
#include "git-version.h"
#include "default-cvsignore.h"
+#include "itypes.h"
-extern struct name_num_obj valid_checksums;
-extern struct name_num_obj valid_compressions;
+extern struct name_num_obj valid_checksums, valid_compressions, valid_auth_checksums;
static char *istring(const char *fmt, int val)
{
@@ -37,7 +37,8 @@ static char *istring(const char *fmt, int val)
static void print_info_flags(enum logcode f)
{
STRUCT_STAT *dumstat;
- char line_buf[75];
+ BOOL as_json = f == FNONE ? 1 : 0; /* We use 1 == first attribute, 2 == need closing array */
+ char line_buf[75], item_buf[32];
int line_len, j;
char *info_flags[] = {
@@ -110,12 +111,12 @@ static void print_info_flags(enum logcode f)
#endif
"xattrs",
-#ifdef RSYNC_USE_PROTECTED_ARGS
+#ifdef RSYNC_USE_SECLUDED_ARGS
"default "
#else
"optional "
#endif
- "protect-args",
+ "secluded-args",
#ifndef ICONV_OPTION
"no "
@@ -139,64 +140,162 @@ static void print_info_flags(enum logcode f)
"*Optimizations",
-#ifndef HAVE_SIMD
+#ifndef USE_ROLL_SIMD
"no "
#endif
- "SIMD",
+ "SIMD-roll",
-#ifndef HAVE_ASM
+#ifndef USE_ROLL_ASM
"no "
#endif
- "asm",
+ "asm-roll",
#ifndef USE_OPENSSL
"no "
#endif
"openssl-crypto",
+#ifndef USE_MD5_ASM
+ "no "
+#endif
+ "asm-MD5",
+
NULL
};
for (line_len = 0, j = 0; ; j++) {
char *str = info_flags[j], *next_nfo = str ? info_flags[j+1] : NULL;
- int str_len = str && *str != '*' ? strlen(str) : 1000;
int need_comma = next_nfo && *next_nfo != '*' ? 1 : 0;
- if (line_len && line_len + 1 + str_len + need_comma >= (int)sizeof line_buf) {
- rprintf(f, " %s\n", line_buf);
+ int item_len;
+ if (!str || *str == '*')
+ item_len = 1000;
+ else if (as_json) {
+ char *space = strchr(str, ' ');
+ int is_no = space && strncmp(str, "no ", 3) == 0;
+ int is_bits = space && isDigit(str);
+ char *quot = space && !is_no && !is_bits ? "\"" : "";
+ char *item = space ? space + 1 : str;
+ char *val = !space ? "true" : is_no ? "false" : str;
+ int val_len = !space ? 4 : is_no ? 5 : space - str;
+ if (is_bits && (space = strchr(val, '-')) != NULL)
+ val_len = space - str;
+ item_len = snprintf(item_buf, sizeof item_buf,
+ " \"%s%s\": %s%.*s%s%s", item, is_bits ? "bits" : "",
+ quot, val_len, val, quot, need_comma ? "," : "");
+ if (is_bits)
+ item_buf[strlen(item)+2-1] = '_'; /* Turn the 's' into a '_' */
+ for (space = item; (space = strpbrk(space, " -")) != NULL; space++)
+ item_buf[space - item + 2] = '_';
+ } else
+ item_len = snprintf(item_buf, sizeof item_buf, " %s%s", str, need_comma ? "," : "");
+ if (line_len && line_len + item_len >= (int)sizeof line_buf) {
+ if (as_json)
+ printf(" %s\n", line_buf);
+ else
+ rprintf(f, " %s\n", line_buf);
line_len = 0;
}
if (!str)
break;
if (*str == '*') {
- rprintf(f, "%s:\n", str+1);
- continue;
+ if (as_json) {
+ if (as_json == 2)
+ printf(" }");
+ else
+ as_json = 2;
+ printf(",\n \"%c%s\": {\n", toLower(str+1), str+2);
+ } else
+ rprintf(f, "%s:\n", str+1);
+ } else {
+ strlcpy(line_buf + line_len, item_buf, sizeof line_buf - line_len);
+ line_len += item_len;
}
- line_len += snprintf(line_buf+line_len, sizeof line_buf - line_len, " %s%s", str, need_comma ? "," : "");
}
+ if (as_json == 2)
+ printf(" }");
}
-void print_rsync_version(enum logcode f)
+static void output_nno_list(enum logcode f, const char *name, struct name_num_obj *nno)
{
- char tmpbuf[256], *subprotocol = "";
+ char namebuf[64], tmpbuf[256];
+ char *tok, *next_tok, *comma = ",";
+ char *cp;
+
+ /* Using '(' ensures that we get a trailing "none" but also includes aliases. */
+ get_default_nno_list(nno, tmpbuf, sizeof tmpbuf - 1, '(');
+ if (f != FNONE) {
+ rprintf(f, "%s:\n", name);
+ rprintf(f, " %s\n", tmpbuf);
+ return;
+ }
+
+ strlcpy(namebuf, name, sizeof namebuf);
+ for (cp = namebuf; *cp; cp++) {
+ if (*cp == ' ')
+ *cp = '_';
+ else if (isUpper(cp))
+ *cp = toLower(cp);
+ }
+
+ printf(",\n \"%s\": [\n ", namebuf);
+
+ for (tok = strtok(tmpbuf, " "); tok; tok = next_tok) {
+ next_tok = strtok(NULL, " ");
+ if (*tok != '(') /* Ignore the alises in the JSON output */
+ printf(" \"%s\"%s", tok, comma + (next_tok ? 0 : 1));
+ }
+ printf("\n ]");
+}
+
+/* A request of f == FNONE wants json on stdout. */
+void print_rsync_version(enum logcode f)
+{
+ char copyright[] = "(C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.";
+ char url[] = "https://rsync.samba.org/";
+ BOOL first_line = 1;
+
+#define json_line(name, value) \
+ do { \
+ printf("%c\n \"%s\": \"%s\"", first_line ? '{' : ',', name, value); \
+ first_line = 0; \
+ } while (0)
+
+ if (f == FNONE) {
+ char verbuf[32];
+ json_line("program", RSYNC_NAME);
+ json_line("version", rsync_version());
+ (void)snprintf(verbuf, sizeof verbuf, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ json_line("protocol", verbuf);
+ json_line("copyright", copyright);
+ json_line("url", url);
+ } else {
#if SUBPROTOCOL_VERSION != 0
- subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
+ char *subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
+#else
+ char *subprotocol = "";
#endif
- rprintf(f, "%s version %s protocol version %d%s\n",
- RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
-
- rprintf(f, "Copyright (C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.\n");
- rprintf(f, "Web site: https://rsync.samba.org/\n");
+ rprintf(f, "%s version %s protocol version %d%s\n",
+ RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
+ rprintf(f, "Copyright %s\n", copyright);
+ rprintf(f, "Web site: %s\n", url);
+ }
print_info_flags(f);
- rprintf(f, "Checksum list:\n");
- get_default_nno_list(&valid_checksums, tmpbuf, sizeof tmpbuf, '(');
- rprintf(f, " %s\n", tmpbuf);
+ init_checksum_choices();
- rprintf(f, "Compress list:\n");
- get_default_nno_list(&valid_compressions, tmpbuf, sizeof tmpbuf, '(');
- rprintf(f, " %s\n", tmpbuf);
+ output_nno_list(f, "Checksum list", &valid_checksums);
+ output_nno_list(f, "Compress list", &valid_compressions);
+ output_nno_list(f, "Daemon auth list", &valid_auth_checksums);
+
+ if (f == FNONE) {
+ json_line("license", "GPLv3");
+ json_line("caveat", "rsync comes with ABSOLUTELY NO WARRANTY");
+ printf("\n}\n");
+ fflush(stdout);
+ return;
+ }
#ifdef MAINTAINER_MODE
rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
@@ -240,7 +339,7 @@ void usage(enum logcode F)
#include "help-rsync.h"
rprintf(F,"\n");
rprintf(F,"Use \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
- rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.\n");
+ rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) manpages for full documentation.\n");
rprintf(F,"See https://rsync.samba.org/ for updates, bug reports, and answers\n");
}
@@ -253,16 +352,18 @@ void daemon_usage(enum logcode F)
#include "help-rsyncd.h"
rprintf(F,"\n");
rprintf(F,"If you were not trying to invoke rsync as a daemon, avoid using any of the\n");
- rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) man page.\n");
+ rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) manpage.\n");
}
const char *rsync_version(void)
{
+ char *ver;
#ifdef RSYNC_GITVER
- return RSYNC_GITVER;
+ ver = RSYNC_GITVER;
#else
- return RSYNC_VERSION;
+ ver = RSYNC_VERSION;
#endif
+ return *ver == 'v' ? ver+1 : ver;
}
const char *default_cvsignore(void)
diff --git a/util1.c b/util1.c
index 3f337d077..d84bc4140 100644
--- a/util1.c
+++ b/util1.c
@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,7 +31,6 @@ extern int do_fsync;
extern int protect_args;
extern int modify_window;
extern int relative_paths;
-extern int preserve_mtimes;
extern int preserve_xattrs;
extern int omit_link_times;
extern int preallocate_files;
@@ -320,44 +319,65 @@ static int safe_read(int desc, char *ptr, size_t len)
return n_chars;
}
-/* Copy a file. If ofd < 0, copy_file unlinks and opens the "dest" file.
- * Otherwise, it just writes to and closes the provided file descriptor.
+/* Remove existing file @dest and reopen, creating a new file with @mode */
+static int unlink_and_reopen(const char *dest, mode_t mode)
+{
+ int ofd;
+
+ if (robust_unlink(dest) && errno != ENOENT) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));
+ errno = save_errno;
+ return -1;
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ mode |= S_IWUSR;
+#endif
+ mode &= INITACCESSPERMS;
+ if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
+ errno = save_errno;
+ return -1;
+ }
+ return ofd;
+}
+
+/* Copy contents of file @source to file @dest with mode @mode.
+ *
+ * If @tmpfilefd is < 0, copy_file unlinks @dest and then opens a new
+ * file with name @dest.
+ *
+ * Otherwise, copy_file writes to and closes the provided file
+ * descriptor.
+ *
* In either case, if --xattrs are being preserved, the dest file will
* have its xattrs set from the source file.
*
* This is used in conjunction with the --temp-dir, --backup, and
* --copy-dest options. */
-int copy_file(const char *source, const char *dest, int ofd, mode_t mode)
+int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode)
{
- int ifd;
+ int ifd, ofd;
char buf[1024 * 8];
int len; /* Number of bytes read into `buf'. */
OFF_T prealloc_len = 0, offset = 0;
- if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
+ if ((ifd = do_open_nofollow(source, O_RDONLY)) < 0) {
int save_errno = errno;
rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
errno = save_errno;
return -1;
}
- if (ofd < 0) {
- if (robust_unlink(dest) && errno != ENOENT) {
- int save_errno = errno;
- rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));
- close(ifd);
- errno = save_errno;
- return -1;
- }
-
-#ifdef SUPPORT_XATTRS
- if (preserve_xattrs)
- mode |= S_IWUSR;
-#endif
- mode &= INITACCESSPERMS;
- if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+ if (tmpfilefd >= 0) {
+ ofd = tmpfilefd;
+ } else {
+ ofd = unlink_and_reopen(dest, mode);
+ if (ofd < 0) {
int save_errno = errno;
- rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
close(ifd);
errno = save_errno;
return -1;
@@ -1298,7 +1318,14 @@ int handle_partial_dir(const char *fname, int create)
*
* "src" is the top source directory currently applicable at the level
* of the referenced symlink. This is usually the symlink's full path
- * (including its name), as referenced from the root of the transfer. */
+ * (including its name), as referenced from the root of the transfer.
+ *
+ * NOTE: this also rejects dest names with a .. component in other
+ * than the first component of the name ie. it rejects names such as
+ * a/b/../x/y. This needs to be done as the leading subpaths 'a' or
+ * 'b' could later be replaced with symlinks such as a link to '.'
+ * resulting in the link being transferred now becoming unsafe
+ */
int unsafe_symlink(const char *dest, const char *src)
{
const char *name, *slash;
@@ -1308,6 +1335,23 @@ int unsafe_symlink(const char *dest, const char *src)
if (!dest || !*dest || *dest == '/')
return 1;
+ // reject destinations with /../ in the name other than at the start of the name
+ const char *dest2 = dest;
+ while (strncmp(dest2, "../", 3) == 0) {
+ dest2 += 3;
+ while (*dest2 == '/') {
+ // allow for ..//..///../foo
+ dest2++;
+ }
+ }
+ if (strstr(dest2, "/../"))
+ return 1;
+
+ // reject if the destination ends in /..
+ const size_t dlen = strlen(dest);
+ if (dlen > 3 && strcmp(&dest[dlen-3], "/..") == 0)
+ return 1;
+
/* find out what our safety margin is */
for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
/* ".." segment starts the count over. "." segment is ignored. */
@@ -1467,12 +1511,19 @@ const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
#define UNIT (1 << 16)
-uint32 fuzzy_distance(const char *s1, unsigned len1, const char *s2, unsigned len2)
+uint32 fuzzy_distance(const char *s1, unsigned len1, const char *s2, unsigned len2, uint32 upperlimit)
{
uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
int32 cost;
unsigned i1, i2;
+ /* Check to see if the Levenshtein distance must be greater than the
+ * upper limit defined by the previously found lowest distance using
+ * the heuristic that the Levenshtein distance is greater than the
+ * difference in length of the two strings */
+ if ((len1 > len2 ? len1 - len2 : len2 - len1) * UNIT > upperlimit)
+ return 0xFFFFU * UNIT + 1;
+
if (!len1 || !len2) {
if (!len1) {
s1 = s2;
diff --git a/util2.c b/util2.c
index a8609a5d5..b59bff0a0 100644
--- a/util2.c
+++ b/util2.c
@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool
- * Copyright (C) 2003-2020 Wayne Davison
+ * Copyright (C) 2003-2024 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,7 +72,7 @@ int msleep(int t)
void *my_alloc(void *ptr, size_t num, size_t size, const char *file, int line)
{
- if (max_alloc && num >= max_alloc/size) {
+ if (num >= max_alloc/size) {
if (!file)
return NULL;
rprintf(FERROR, "[%s] exceeded --max-alloc=%s setting (file=%s, line=%d)\n",
diff --git a/version.h b/version.h
index da21e0fdc..7f18570de 100644
--- a/version.h
+++ b/version.h
@@ -1 +1,2 @@
-#define RSYNC_VERSION "3.2.4dev"
+#define RSYNC_VERSION "3.4.2dev"
+#define MAINTAINER_TZ_OFFSET -7.0
diff --git a/xattrs.c b/xattrs.c
index 3c5491920..26e50a6f9 100644
--- a/xattrs.c
+++ b/xattrs.c
@@ -3,7 +3,7 @@
* Written by Jay Fenlason, vaguely based on the ACLs patch.
*
* Copyright (C) 2004 Red Hat, Inc.
- * Copyright (C) 2006-2020 Wayne Davison
+ * Copyright (C) 2006-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,9 +39,13 @@ extern int preserve_specials;
extern int checksum_seed;
extern int saw_xattr_filter;
+extern struct name_num_item *xattr_sum_nni;
+extern int xattr_sum_len;
+
#define RSYNC_XAL_INITIAL 5
#define RSYNC_XAL_LIST_INITIAL 100
+#define MAX_XATTR_DIGEST_LEN MD5_DIGEST_LEN
#define MAX_FULL_DATUM 32
#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
@@ -269,8 +273,8 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
if (datum_len > MAX_FULL_DATUM) {
/* For large datums, we store a flag and a checksum. */
- name_offset = 1 + MAX_DIGEST_LEN;
- sum_init(-1, checksum_seed);
+ name_offset = 1 + MAX_XATTR_DIGEST_LEN;
+ sum_init(xattr_sum_nni, checksum_seed);
sum_update(ptr, datum_len);
free(ptr);
@@ -377,20 +381,14 @@ static int64 xattr_lookup_hash(const item_list *xalp)
{
const rsync_xa *rxas = xalp->items;
size_t i;
- int64 key = hashlittle(&xalp->count, sizeof xalp->count);
+ int64 key = hashlittle2(&xalp->count, sizeof xalp->count);
for (i = 0; i < xalp->count; i++) {
- key += hashlittle(rxas[i].name, rxas[i].name_len);
+ key += hashlittle2(rxas[i].name, rxas[i].name_len);
if (rxas[i].datum_len > MAX_FULL_DATUM)
- key += hashlittle(rxas[i].datum, MAX_DIGEST_LEN);
+ key += hashlittle2(rxas[i].datum, xattr_sum_len);
else
- key += hashlittle(rxas[i].datum, rxas[i].datum_len);
- }
-
- if (key == 0) {
- /* This is very unlikely, but we should never
- * return 0 as hashtable_find() doesn't like it. */
- return 1;
+ key += hashlittle2(rxas[i].datum, rxas[i].datum_len);
}
return key;
@@ -435,7 +433,7 @@ static int find_matching_xattr(const item_list *xalp)
if (rxas1[j].datum_len > MAX_FULL_DATUM) {
if (memcmp(rxas1[j].datum + 1,
rxas2[j].datum + 1,
- MAX_DIGEST_LEN) != 0)
+ xattr_sum_len) != 0)
break;
} else {
if (memcmp(rxas1[j].datum, rxas2[j].datum,
@@ -471,8 +469,6 @@ static int rsync_xal_store(item_list *xalp)
if (rsync_xal_h == NULL)
rsync_xal_h = hashtable_create(512, HT_KEY64);
- if (rsync_xal_h == NULL)
- out_of_memory("rsync_xal_h hashtable_create()");
new_ref = new0(rsync_xa_list_ref);
new_ref->ndx = ndx;
@@ -535,7 +531,7 @@ int send_xattr(int f, stat_x *sxp)
#endif
write_buf(f, name, name_len);
if (rxa->datum_len > MAX_FULL_DATUM)
- write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
+ write_buf(f, rxa->datum + 1, xattr_sum_len);
else
write_bigbuf(f, rxa->datum, rxa->datum_len);
}
@@ -588,7 +584,7 @@ int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all)
else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
&& memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
- MAX_DIGEST_LEN) == 0;
+ xattr_sum_len) == 0;
/* Flag unrequested items that we need. */
if (!same && find_all && snd_rxa->datum[0] == XSTATE_ABBREV)
snd_rxa->datum[0] = XSTATE_TODO;
@@ -797,7 +793,7 @@ void receive_xattr(int f, struct file_struct *file)
rsync_xa *rxa;
size_t name_len = read_varint(f);
size_t datum_len = read_varint(f);
- size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
+ size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + (size_t)xattr_sum_len : datum_len;
size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
if (SIZE_MAX - dget_len < extra_len || SIZE_MAX - dget_len - extra_len < name_len)
overflow_exit("receive_xattr");
@@ -812,7 +808,7 @@ void receive_xattr(int f, struct file_struct *file)
read_buf(f, ptr, dget_len);
else {
*ptr = XSTATE_ABBREV;
- read_buf(f, ptr + 1, MAX_DIGEST_LEN);
+ read_buf(f, ptr + 1, xattr_sum_len);
}
if (saw_xattr_filter) {
@@ -943,7 +939,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
rsync_xa *rxas = xalp->items;
ssize_t list_len;
size_t i, len;
- char *name, *ptr, sum[MAX_DIGEST_LEN];
+ char *name, *ptr, sum[MAX_XATTR_DIGEST_LEN];
#ifdef HAVE_LINUX_XATTRS
int user_only = am_root <= 0;
#endif
@@ -958,7 +954,6 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
name = rxas[i].name;
if (XATTR_ABBREV(rxas[i])) {
- int sum_len;
/* See if the fnamecmp version is identical. */
len = name_len = rxas[i].name_len;
if ((ptr = get_xattr_data(fnamecmp, name, &len, 1)) == NULL) {
@@ -975,10 +970,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
goto still_abbrev;
}
- sum_init(-1, checksum_seed);
+ sum_init(xattr_sum_nni, checksum_seed);
sum_update(ptr, len);
- sum_len = sum_end(sum);
- if (memcmp(sum, rxas[i].datum + 1, sum_len) != 0) {
+ sum_end(sum);
+ if (memcmp(sum, rxas[i].datum + 1, xattr_sum_len) != 0) {
free(ptr);
goto still_abbrev;
}
diff --git a/zlib/deflate.c b/zlib/deflate.c
index 529c5e8d9..2cbc4fc2f 100644
--- a/zlib/deflate.c
+++ b/zlib/deflate.c
@@ -227,11 +227,6 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
int wrap = 1;
static const char my_version[] = ZLIB_VERSION;
- ushf *overlay;
- /* We overlay pending_buf and d_buf+l_buf. This works since the average
- * output size for (length,distance) codes is <= 24 bits.
- */
-
if (version == Z_NULL || version[0] != my_version[0] ||
stream_size != sizeof(z_stream)) {
return Z_VERSION_ERROR;
@@ -300,9 +295,47 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
- overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
- s->pending_buf = (uchf *) overlay;
- s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+ /* We overlay pending_buf and sym_buf. This works since the average size
+ * for length/distance pairs over any compressed block is assured to be 31
+ * bits or less.
+ *
+ * Analysis: The longest fixed codes are a length code of 8 bits plus 5
+ * extra bits, for lengths 131 to 257. The longest fixed distance codes are
+ * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest
+ * possible fixed-codes length/distance pair is then 31 bits total.
+ *
+ * sym_buf starts one-fourth of the way into pending_buf. So there are
+ * three bytes in sym_buf for every four bytes in pending_buf. Each symbol
+ * in sym_buf is three bytes -- two for the distance and one for the
+ * literal/length. As each symbol is consumed, the pointer to the next
+ * sym_buf value to read moves forward three bytes. From that symbol, up to
+ * 31 bits are written to pending_buf. The closest the written pending_buf
+ * bits gets to the next sym_buf symbol to read is just before the last
+ * code is written. At that time, 31*(n-2) bits have been written, just
+ * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
+ * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
+ * symbols are written.) The closest the writing gets to what is unread is
+ * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
+ * can range from 128 to 32768.
+ *
+ * Therefore, at a minimum, there are 142 bits of space between what is
+ * written and what is read in the overlain buffers, so the symbols cannot
+ * be overwritten by the compressed data. That space is actually 139 bits,
+ * due to the three-bit fixed-code block header.
+ *
+ * That covers the case where either Z_FIXED is specified, forcing fixed
+ * codes, or when the use of fixed codes is chosen, because that choice
+ * results in a smaller compressed block than dynamic codes. That latter
+ * condition then assures that the above analysis also covers all dynamic
+ * blocks. A dynamic-code block will only be chosen to be emitted if it has
+ * fewer bits than a fixed-code block would for the same set of symbols.
+ * Therefore its average symbol length is assured to be less than 31. So
+ * the compressed data for a dynamic block also cannot overwrite the
+ * symbols from which it is being constructed.
+ */
+
+ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4);
+ s->pending_buf_size = (ulg)s->lit_bufsize * 4;
if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
s->pending_buf == Z_NULL) {
@@ -311,8 +344,12 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
deflateEnd (strm);
return Z_MEM_ERROR;
}
- s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
- s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+ s->sym_buf = s->pending_buf + s->lit_bufsize;
+ s->sym_end = (s->lit_bufsize - 1) * 3;
+ /* We avoid equality with lit_bufsize*3 because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
s->level = level;
s->strategy = strategy;
@@ -473,7 +510,7 @@ int ZEXPORT deflatePrime (strm, bits, value)
if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
s = strm->state;
- if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
+ if (s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
return Z_BUF_ERROR;
do {
put = Buf_size - s->bi_valid;
@@ -1022,7 +1059,6 @@ int ZEXPORT deflateCopy (dest, source)
#else
deflate_state *ds;
deflate_state *ss;
- ushf *overlay;
if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
@@ -1042,8 +1078,7 @@ int ZEXPORT deflateCopy (dest, source)
ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
- overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
- ds->pending_buf = (uchf *) overlay;
+ ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4);
if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
ds->pending_buf == Z_NULL) {
@@ -1057,8 +1092,7 @@ int ZEXPORT deflateCopy (dest, source)
zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
- ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
- ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+ ds->sym_buf = ds->pending_buf + ds->lit_bufsize;
ds->l_desc.dyn_tree = ds->dyn_ltree;
ds->d_desc.dyn_tree = ds->dyn_dtree;
@@ -1737,7 +1771,7 @@ local block_state deflate_fast(s, flush)
FLUSH_BLOCK(s, 1);
return finish_done;
}
- if (s->last_lit)
+ if (s->sym_next)
FLUSH_BLOCK(s, 0);
return block_done;
}
@@ -1878,7 +1912,7 @@ local block_state deflate_slow(s, flush)
FLUSH_BLOCK(s, 1);
return finish_done;
}
- if (s->last_lit)
+ if (s->sym_next)
FLUSH_BLOCK(s, 0);
return block_done;
}
@@ -1953,7 +1987,7 @@ local block_state deflate_rle(s, flush)
FLUSH_BLOCK(s, 1);
return finish_done;
}
- if (s->last_lit)
+ if (s->sym_next)
FLUSH_BLOCK(s, 0);
return block_done;
}
@@ -1992,7 +2026,7 @@ local block_state deflate_huff(s, flush)
FLUSH_BLOCK(s, 1);
return finish_done;
}
- if (s->last_lit)
+ if (s->sym_next)
FLUSH_BLOCK(s, 0);
return block_done;
}
diff --git a/zlib/deflate.h b/zlib/deflate.h
index ce0299edd..a300a28d2 100644
--- a/zlib/deflate.h
+++ b/zlib/deflate.h
@@ -214,7 +214,7 @@ typedef struct internal_state {
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
- uchf *l_buf; /* buffer for literals or lengths */
+ uchf *sym_buf; /* buffer for distances and literals/lengths */
uInt lit_bufsize;
/* Size of match buffer for literals/lengths. There are 4 reasons for
@@ -236,13 +236,8 @@ typedef struct internal_state {
* - I can't count above 4
*/
- uInt last_lit; /* running index in l_buf */
-
- ushf *d_buf;
- /* Buffer for distances. To simplify the code, d_buf and l_buf have
- * the same number of elements. To use different lengths, an extra flag
- * array would be necessary.
- */
+ uInt sym_next; /* running index in sym_buf */
+ uInt sym_end; /* symbol table full when sym_next reaches this */
ulg opt_len; /* bit length of current block with optimal trees */
ulg static_len; /* bit length of current block with static trees */
@@ -322,20 +317,22 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
# define _tr_tally_lit(s, c, flush) \
{ uch cc = (c); \
- s->d_buf[s->last_lit] = 0; \
- s->l_buf[s->last_lit++] = cc; \
+ s->sym_buf[s->sym_next++] = 0; \
+ s->sym_buf[s->sym_next++] = 0; \
+ s->sym_buf[s->sym_next++] = cc; \
s->dyn_ltree[cc].Freq++; \
- flush = (s->last_lit == s->lit_bufsize-1); \
+ flush = (s->sym_next == s->sym_end); \
}
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (length); \
ush dist = (distance); \
- s->d_buf[s->last_lit] = dist; \
- s->l_buf[s->last_lit++] = len; \
+ s->sym_buf[s->sym_next++] = dist; \
+ s->sym_buf[s->sym_next++] = dist >> 8; \
+ s->sym_buf[s->sym_next++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
s->dyn_dtree[d_code(dist)].Freq++; \
- flush = (s->last_lit == s->lit_bufsize-1); \
+ flush = (s->sym_next == s->sym_end); \
}
#else
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
diff --git a/zlib/inflate.c b/zlib/inflate.c
index e43abd9e0..e9840b679 100644
--- a/zlib/inflate.c
+++ b/zlib/inflate.c
@@ -740,8 +740,9 @@ int flush;
if (copy > have) copy = have;
if (copy) {
if (state->head != Z_NULL &&
- state->head->extra != Z_NULL) {
- len = state->head->extra_len - state->length;
+ state->head->extra != Z_NULL &&
+ (len = state->head->extra_len - state->length) <
+ state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);
diff --git a/zlib/trees.c b/zlib/trees.c
index 1fd7759ef..9c667702c 100644
--- a/zlib/trees.c
+++ b/zlib/trees.c
@@ -418,7 +418,7 @@ local void init_block(s)
s->dyn_ltree[END_BLOCK].Freq = 1;
s->opt_len = s->static_len = 0L;
- s->last_lit = s->matches = 0;
+ s->sym_next = s->matches = 0;
}
#define SMALLEST 1
@@ -943,7 +943,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
- s->last_lit));
+ s->sym_next / 3));
if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
@@ -1012,8 +1012,9 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
unsigned dist; /* distance of matched string */
unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
- s->d_buf[s->last_lit] = (ush)dist;
- s->l_buf[s->last_lit++] = (uch)lc;
+ s->sym_buf[s->sym_next++] = dist;
+ s->sym_buf[s->sym_next++] = dist >> 8;
+ s->sym_buf[s->sym_next++] = lc;
if (dist == 0) {
/* lc is the unmatched char */
s->dyn_ltree[lc].Freq++;
@@ -1028,30 +1029,7 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
s->dyn_dtree[d_code(dist)].Freq++;
}
-
-#ifdef TRUNCATE_BLOCK
- /* Try to guess if it is profitable to stop the current block here */
- if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
- /* Compute an upper bound for the compressed length */
- ulg out_length = (ulg)s->last_lit*8L;
- ulg in_length = (ulg)((long)s->strstart - s->block_start);
- int dcode;
- for (dcode = 0; dcode < D_CODES; dcode++) {
- out_length += (ulg)s->dyn_dtree[dcode].Freq *
- (5L+extra_dbits[dcode]);
- }
- out_length >>= 3;
- Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
- s->last_lit, in_length, out_length,
- 100L - out_length*100L/in_length));
- if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
- }
-#endif
- return (s->last_lit == s->lit_bufsize-1);
- /* We avoid equality with lit_bufsize because of wraparound at 64K
- * on 16 bit machines and because stored blocks are restricted to
- * 64K-1 bytes.
- */
+ return (s->sym_next == s->sym_end);
}
/* ===========================================================================
@@ -1064,13 +1042,14 @@ local void compress_block(s, ltree, dtree)
{
unsigned dist; /* distance of matched string */
int lc; /* match length or unmatched char (if dist == 0) */
- unsigned lx = 0; /* running index in l_buf */
+ unsigned sx = 0; /* running index in sym_buf */
unsigned code; /* the code to send */
int extra; /* number of extra bits to send */
- if (s->last_lit != 0) do {
- dist = s->d_buf[lx];
- lc = s->l_buf[lx++];
+ if (s->sym_next != 0) do {
+ dist = s->sym_buf[sx++] & 0xff;
+ dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
+ lc = s->sym_buf[sx++];
if (dist == 0) {
send_code(s, lc, ltree); /* send a literal byte */
Tracecv(isgraph(lc), (stderr," '%c' ", lc));
@@ -1095,11 +1074,10 @@ local void compress_block(s, ltree, dtree)
}
} /* literal or match pair ? */
- /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
- Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
- "pendingBuf overflow");
+ /* Check that the overlay between pending_buf and sym_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
- } while (lx < s->last_lit);
+ } while (sx < s->sym_next);
send_code(s, END_BLOCK, ltree);
}