]> arthur.barton.de Git - netatalk.git/commitdiff
Merge branch 'v3-cleanup' into v3.1.3-alex v3.1.3-alex
authorAlexander Barton <alex@barton.de>
Thu, 10 Jul 2014 22:59:44 +0000 (00:59 +0200)
committerAlexander Barton <alex@barton.de>
Thu, 10 Jul 2014 23:00:00 +0000 (01:00 +0200)
* v3-cleanup:
  autoconf: Indentation fixes
  Spelling fixes
  Whitespace and exclamation mark fixes
  Reset version to 3.0.1 after merge
  sorry. revert...
  fix --enable-fhs

Conflicts:
VERSION
etc/afpd/main.c

Signed-off-by: Alexander Barton <alex@barton.de>
443 files changed:
.gitignore
Makefile.am
NEWS
VERSION
abigen.sh
bin/Makefile.am
bin/ad/Makefile.am
bin/ad/ad.c
bin/ad/ad.h
bin/ad/ad_cp.c
bin/ad/ad_find.c
bin/ad/ad_ls.c
bin/ad/ad_mv.c
bin/ad/ad_rm.c
bin/ad/ad_set.c
bin/ad/ad_util.c
bin/afppasswd/Makefile.am
bin/cnid/cnid2_create.in
bin/megatron/asingle.c
bin/megatron/asingle.h
bin/megatron/hqx.c
bin/megatron/hqx.h
bin/megatron/macbin.c
bin/megatron/macbin.h
bin/megatron/megatron.h
bin/megatron/nad.c
bin/megatron/nad.h
bin/megatron/updcrc.c
bin/megatron/updcrc.h
bin/misc/Makefile.am
bin/misc/fce.c
bin/misc/logger_test.c
bin/misc/netacnv.c
bin/misc/uuidtest.c
bin/uniconv/Makefile.am
bin/uniconv/iso8859_1_adapted.c
bin/uniconv/uniconv.c
bootstrap
config/.gitignore
config/Makefile.am
config/dbus-session.conf.tmpl [new file with mode: 0644]
config/extmap.conf [new file with mode: 0644]
config/netatalk-dbus.conf [new file with mode: 0644]
config/pam/.gitignore
config/pam/Makefile.am
config/pam/netatalk.pam.tmpl [deleted file]
config/pam/netatalk.tmpl [new file with mode: 0644]
configure.ac
contrib/macusers/macusers.in
contrib/misc/libevent.patch
contrib/shell_utils/Makefile.am
contrib/shell_utils/afpstats [new file with mode: 0755]
contrib/shell_utils/apple_dump.in
distrib/initscripts/Makefile.am
distrib/initscripts/rc.debian.tmpl
distrib/initscripts/rc.netbsd.tmpl
distrib/initscripts/rc.redhat.tmpl
distrib/initscripts/service.systemd.tmpl
doc/.gitignore
doc/DEVELOPER
doc/Makefile.am
doc/gfx_and_css/logo.ai [new file with mode: 0644]
doc/gfx_and_css/logo.pdf [new file with mode: 0644]
doc/gfx_and_css/logo.png [new file with mode: 0644]
doc/gfx_and_css/netatalk.css [new file with mode: 0644]
doc/html.xsl.in [new file with mode: 0644]
doc/man.xsl.in [new file with mode: 0644]
doc/manpages/Makefile.am [new file with mode: 0644]
doc/manpages/man1/.gitignore [new file with mode: 0644]
doc/manpages/man1/Makefile.am [new file with mode: 0644]
doc/manpages/man1/ad.1.xml [new file with mode: 0644]
doc/manpages/man1/afpldaptest.1.xml [new file with mode: 0644]
doc/manpages/man1/afppasswd.1.xml [new file with mode: 0644]
doc/manpages/man1/afpstats.1.xml [new file with mode: 0644]
doc/manpages/man1/apple_dump.1.xml [new file with mode: 0644]
doc/manpages/man1/asip-status.pl.1.xml [new file with mode: 0644]
doc/manpages/man1/dbd.1.xml [new file with mode: 0644]
doc/manpages/man1/macusers.1.xml [new file with mode: 0644]
doc/manpages/man1/megatron.1.xml [new file with mode: 0644]
doc/manpages/man1/netatalk-config.1.xml [new file with mode: 0644]
doc/manpages/man1/uniconv.1.xml [new file with mode: 0644]
doc/manpages/man5/.gitignore [new file with mode: 0644]
doc/manpages/man5/Makefile.am [new file with mode: 0644]
doc/manpages/man5/afp.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/afp_signature.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/afp_voluuid.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/extmap.conf.5.xml [new file with mode: 0644]
doc/manpages/man8/.gitignore [new file with mode: 0644]
doc/manpages/man8/Makefile.am [new file with mode: 0644]
doc/manpages/man8/afpd.8.xml [new file with mode: 0644]
doc/manpages/man8/cnid_dbd.8.xml [new file with mode: 0644]
doc/manpages/man8/cnid_metad.8.xml [new file with mode: 0644]
doc/manpages/man8/netatalk.8.xml [new file with mode: 0644]
doc/manual/.gitignore [new file with mode: 0644]
doc/manual/Makefile.am [new file with mode: 0644]
doc/manual/configuration.xml [new file with mode: 0644]
doc/manual/install.xml [new file with mode: 0644]
doc/manual/intro.xml [new file with mode: 0644]
doc/manual/manual.xml.in [new file with mode: 0644]
doc/manual/netatalk.html [new file with mode: 0644]
doc/manual/upgrade.xml [new file with mode: 0644]
doc/www/.gitignore [new file with mode: 0644]
doc/www/ReleaseNotes [new file with mode: 0644]
doc/www/asciidoc.conf [new file with mode: 0644]
doc/www/asciidoc.py [new file with mode: 0755]
doc/www/create-relnotes.sh [new file with mode: 0755]
doc/www/html5.conf [new file with mode: 0644]
doc/www/javascripts/asciidoc.js [new file with mode: 0644]
doc/www/lang-en.conf [new file with mode: 0644]
doc/www/netatalk-relnotes.conf [new file with mode: 0644]
doc/www/stylesheets/asciidoc.css [new file with mode: 0644]
etc/Makefile.am
etc/afpd/.gitignore
etc/afpd/Makefile.am
etc/afpd/acl_mappings.h
etc/afpd/acls.c
etc/afpd/acls.h
etc/afpd/afp_config.c
etc/afpd/afp_dsi.c
etc/afpd/afp_mdns.c
etc/afpd/afp_options.c
etc/afpd/afp_util.c
etc/afpd/afpstats-service.xml [new file with mode: 0644]
etc/afpd/afpstats.c [new file with mode: 0644]
etc/afpd/afpstats.h [new file with mode: 0644]
etc/afpd/afpstats_obj.c [new file with mode: 0644]
etc/afpd/afpstats_obj.h [new file with mode: 0644]
etc/afpd/afpstats_service_glue.h [new file with mode: 0644]
etc/afpd/afs.c
etc/afpd/appl.c
etc/afpd/auth.c
etc/afpd/auth.h
etc/afpd/catsearch.c
etc/afpd/desktop.c
etc/afpd/dircache.c
etc/afpd/directory.c
etc/afpd/directory.h
etc/afpd/enumerate.c
etc/afpd/extattrs.c
etc/afpd/extattrs.h
etc/afpd/fce_api.c
etc/afpd/fce_api_internal.h
etc/afpd/fce_util.c
etc/afpd/file.c
etc/afpd/file.h
etc/afpd/filedir.c
etc/afpd/filedir.h
etc/afpd/fork.c
etc/afpd/fork.h
etc/afpd/gettok.c [deleted file]
etc/afpd/hash.c
etc/afpd/hash.h
etc/afpd/main.c
etc/afpd/mangle.h
etc/afpd/messages.c
etc/afpd/nfsquota.c
etc/afpd/ofork.c
etc/afpd/quota.c
etc/afpd/spotlight-packet.bin [new file with mode: 0644]
etc/afpd/spotlight-packet2.bin [new file with mode: 0644]
etc/afpd/spotlight.c [new file with mode: 0644]
etc/afpd/spotlight_marshalling.c [new file with mode: 0644]
etc/afpd/status.c
etc/afpd/switch.h
etc/afpd/uam.c
etc/afpd/uam_auth.h
etc/afpd/uid.c
etc/afpd/uid.h
etc/afpd/unix.c
etc/afpd/unix.h
etc/afpd/volume.c
etc/afpd/volume.h
etc/cnid_dbd/Makefile.am
etc/cnid_dbd/cmd_dbd.c
etc/cnid_dbd/cmd_dbd.h
etc/cnid_dbd/cmd_dbd_scanvol.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/comm.c
etc/cnid_dbd/comm.h
etc/cnid_dbd/db_param.c
etc/cnid_dbd/dbd.h
etc/cnid_dbd/dbd_add.c
etc/cnid_dbd/dbd_dbcheck.c
etc/cnid_dbd/dbd_delete.c
etc/cnid_dbd/dbd_get.c
etc/cnid_dbd/dbd_getstamp.c
etc/cnid_dbd/dbd_lookup.c
etc/cnid_dbd/dbd_rebuild_add.c
etc/cnid_dbd/dbd_resolve.c
etc/cnid_dbd/dbd_search.c
etc/cnid_dbd/dbd_update.c
etc/cnid_dbd/dbif.c
etc/cnid_dbd/dbif.h
etc/cnid_dbd/main.c
etc/cnid_dbd/pack.c
etc/cnid_dbd/pack.h
etc/cnid_dbd/usockfd.h
etc/netatalk/Makefile.am
etc/netatalk/netatalk.c
etc/spotlight/.gitignore [new file with mode: 0644]
etc/spotlight/Makefile.am [new file with mode: 0644]
etc/spotlight/slmod_sparql.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_map.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_map.h [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.c [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.h [new file with mode: 0644]
etc/spotlight/slmod_sparql_parser.y [new file with mode: 0644]
etc/spotlight/spotlight_rawquery_lexer.c [new file with mode: 0644]
etc/spotlight/spotlight_rawquery_lexer.l [new file with mode: 0644]
etc/uams/Makefile.am
etc/uams/uams_dhx2_pam.c
etc/uams/uams_dhx2_passwd.c
etc/uams/uams_dhx_pam.c
etc/uams/uams_dhx_passwd.c
etc/uams/uams_gss.c
etc/uams/uams_guest.c
etc/uams/uams_pam.c
etc/uams/uams_passwd.c
etc/uams/uams_pgp.c
etc/uams/uams_randnum.c
include/atalk/.gitignore
include/atalk/Makefile.am
include/atalk/acl.h
include/atalk/adouble.h
include/atalk/afp.h
include/atalk/afp_dtrace.d [new file with mode: 0644]
include/atalk/byteorder.h [new file with mode: 0644]
include/atalk/cnid.h
include/atalk/cnid_bdb_private.h [new file with mode: 0644]
include/atalk/cnid_dbd_private.h [deleted file]
include/atalk/cnid_mysql_private.h [new file with mode: 0644]
include/atalk/cnid_private.h
include/atalk/compat.h
include/atalk/dalloc.h [new file with mode: 0644]
include/atalk/dictionary.h
include/atalk/directory.h
include/atalk/dsi.h
include/atalk/ea.h
include/atalk/errchk.h
include/atalk/fce_api.h
include/atalk/globals.h
include/atalk/hash.h
include/atalk/iniparser.h
include/atalk/ldapconfig.h
include/atalk/logger.h
include/atalk/netatalk_conf.h
include/atalk/paths.h
include/atalk/server_child.h
include/atalk/server_ipc.h
include/atalk/spotlight.h [new file with mode: 0644]
include/atalk/talloc.h [new file with mode: 0644]
include/atalk/tdb.h
include/atalk/uam.h
include/atalk/unicode.h
include/atalk/unix.h
include/atalk/util.h
include/atalk/vfs.h
include/atalk/volume.h
libatalk/.gitignore
libatalk/Makefile.am
libatalk/acl/Makefile.am
libatalk/acl/cache.c
libatalk/acl/cache.h
libatalk/acl/ldap.c
libatalk/acl/ldap_config.c
libatalk/acl/unix.c
libatalk/acl/uuid.c
libatalk/adouble/Makefile.am
libatalk/adouble/ad_attr.c
libatalk/adouble/ad_conv.c
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_lock.c
libatalk/adouble/ad_open.c
libatalk/adouble/ad_read.c
libatalk/adouble/ad_recvfile.c [new file with mode: 0644]
libatalk/adouble/ad_sendfile.c
libatalk/adouble/ad_size.c
libatalk/adouble/ad_write.c
libatalk/bstring/bstradd.c
libatalk/cnid/Makefile.am
libatalk/cnid/cdb/cnid_cdb_add.c
libatalk/cnid/cdb/cnid_cdb_close.c
libatalk/cnid/cdb/cnid_cdb_delete.c
libatalk/cnid/cdb/cnid_cdb_get.c
libatalk/cnid/cdb/cnid_cdb_lookup.c
libatalk/cnid/cdb/cnid_cdb_meta.c
libatalk/cnid/cdb/cnid_cdb_meta.h
libatalk/cnid/cdb/cnid_cdb_nextid.c
libatalk/cnid/cdb/cnid_cdb_open.c
libatalk/cnid/cdb/cnid_cdb_rebuild_add.c
libatalk/cnid/cdb/cnid_cdb_resolve.c
libatalk/cnid/cdb/cnid_cdb_update.c
libatalk/cnid/cnid.c
libatalk/cnid/cnid_init.c
libatalk/cnid/dbd/cnid_dbd.c
libatalk/cnid/dbd/cnid_dbd.h
libatalk/cnid/last/cnid_last.c
libatalk/cnid/mysql/Makefile.am [new file with mode: 0644]
libatalk/cnid/mysql/cnid_mysql.c [new file with mode: 0644]
libatalk/cnid/tdb/Makefile.am
libatalk/cnid/tdb/cnid_tdb_add.c
libatalk/cnid/tdb/cnid_tdb_close.c
libatalk/cnid/tdb/cnid_tdb_delete.c
libatalk/cnid/tdb/cnid_tdb_get.c
libatalk/cnid/tdb/cnid_tdb_lookup.c
libatalk/cnid/tdb/cnid_tdb_nextid.c
libatalk/cnid/tdb/cnid_tdb_open.c
libatalk/cnid/tdb/cnid_tdb_resolve.c
libatalk/cnid/tdb/cnid_tdb_update.c
libatalk/compat/getusershell.c
libatalk/compat/misc.c
libatalk/compat/mktemp.c
libatalk/compat/rquota_xdr.c
libatalk/dsi/dsi_attn.c
libatalk/dsi/dsi_close.c
libatalk/dsi/dsi_cmdreply.c
libatalk/dsi/dsi_getsess.c
libatalk/dsi/dsi_getstat.c
libatalk/dsi/dsi_opensess.c
libatalk/dsi/dsi_read.c
libatalk/dsi/dsi_stream.c
libatalk/dsi/dsi_tcp.c
libatalk/dsi/dsi_tickle.c
libatalk/dsi/dsi_write.c
libatalk/iniparser/dictionary.c
libatalk/iniparser/iniparser.c
libatalk/libatalk-3.0.1dev.abi [new file with mode: 0644]
libatalk/libatalk-3.0.2.abi [new file with mode: 0644]
libatalk/libatalk-3.0.3.abi [new file with mode: 0644]
libatalk/libatalk-3.0.4.abi [new file with mode: 0644]
libatalk/libatalk-3.0.5.abi [new file with mode: 0644]
libatalk/libatalk-3.0.6.abi [new file with mode: 0644]
libatalk/libatalk-3.1.0.abi [new file with mode: 0644]
libatalk/libatalk-3.1.1.abi [new file with mode: 0644]
libatalk/libatalk-3.1.2.abi [new file with mode: 0644]
libatalk/libatalk-3.1.3.abi [new file with mode: 0644]
libatalk/talloc/Makefile.am [new file with mode: 0644]
libatalk/talloc/dalloc.c [new file with mode: 0644]
libatalk/talloc/talloc.c [new file with mode: 0644]
libatalk/tdb/io.c
libatalk/tdb/tdb_private.h
libatalk/unicode/Makefile.am
libatalk/unicode/byteorder.h [deleted file]
libatalk/unicode/charcnv.c
libatalk/unicode/charsets/generic_cjk.h
libatalk/unicode/charsets/generic_mb.c
libatalk/unicode/charsets/mac_hebrew.c
libatalk/unicode/charsets/mac_roman.h
libatalk/unicode/iconv.c
libatalk/unicode/precompose.h
libatalk/unicode/utf16_case.c
libatalk/unicode/utf16_casetable.h
libatalk/unicode/utf8.c
libatalk/unicode/util_unistr.c
libatalk/util/Makefile.am
libatalk/util/cnid.c
libatalk/util/ftw.c
libatalk/util/gettok.c [new file with mode: 0644]
libatalk/util/locking.c
libatalk/util/logger.c
libatalk/util/module.c
libatalk/util/netatalk_conf.c
libatalk/util/server_child.c
libatalk/util/server_ipc.c
libatalk/util/server_lock.c
libatalk/util/socket.c
libatalk/util/unix.c
libatalk/vfs/acl.c
libatalk/vfs/ea_ad.c
libatalk/vfs/ea_sys.c
libatalk/vfs/extattr.c
libatalk/vfs/unix.c
libatalk/vfs/vfs.c
libevent/Makefile.am
macros/Makefile.am
macros/afs-check.m4
macros/ax_pthread.m4 [new file with mode: 0644]
macros/cnid-backend.m4
macros/db3-check.m4
macros/grep-check.m4
macros/netatalk.m4
macros/pam-check.m4
macros/perl-check.m4
macros/ps-check.m4
macros/quota-check.m4
macros/ssl-check.m4
macros/summary.m4
macros/tcp-wrappers.m4
man/.gitignore
man/man1/.gitignore
man/man1/Makefile.am
man/man1/ad.1 [deleted file]
man/man1/ad.1.in [new file with mode: 0644]
man/man1/afpldaptest.1.in [new file with mode: 0644]
man/man1/afpldaptest.1.tmpl [deleted file]
man/man1/afppasswd.1 [deleted file]
man/man1/afppasswd.1.in [new file with mode: 0644]
man/man1/afpstats.1.in [new file with mode: 0644]
man/man1/apple_dump.1 [deleted file]
man/man1/apple_dump.1.in [new file with mode: 0644]
man/man1/asip-status.pl.1.in [new file with mode: 0644]
man/man1/asip-status.pl.1.tmpl [deleted file]
man/man1/dbd.1 [deleted file]
man/man1/dbd.1.in [new file with mode: 0644]
man/man1/hqx2bin.1
man/man1/macbinary.1
man/man1/macusers.1 [deleted file]
man/man1/macusers.1.in [new file with mode: 0644]
man/man1/megatron.1 [deleted file]
man/man1/megatron.1.in [new file with mode: 0644]
man/man1/netatalk-config.1 [deleted file]
man/man1/netatalk-config.1.in [new file with mode: 0644]
man/man1/single2bin.1
man/man1/unbin.1
man/man1/unhex.1
man/man1/uniconv.1.in [new file with mode: 0644]
man/man1/uniconv.1.tmpl [deleted file]
man/man1/unsingle.1
man/man5/.gitignore
man/man5/Makefile.am
man/man5/afp.conf.5.in [new file with mode: 0644]
man/man5/afp.conf.5.tmpl [deleted file]
man/man5/afp_signature.conf.5.in [new file with mode: 0644]
man/man5/afp_signature.conf.5.tmpl [deleted file]
man/man5/afp_voluuid.conf.5.in [new file with mode: 0644]
man/man5/afp_voluuid.conf.5.tmpl [deleted file]
man/man5/extmap.conf.5.in [new file with mode: 0644]
man/man8/.gitignore
man/man8/Makefile.am
man/man8/afpd.8.in [new file with mode: 0644]
man/man8/afpd.8.tmpl [deleted file]
man/man8/cnid_dbd.8.in [new file with mode: 0644]
man/man8/cnid_dbd.8.tmpl [deleted file]
man/man8/cnid_metad.8.in [new file with mode: 0644]
man/man8/cnid_metad.8.tmpl [deleted file]
man/man8/netatalk.8.in [new file with mode: 0644]
man/man8/netatalk.8.tmpl [deleted file]
test/afpd/Makefile.am
test/afpd/afpfunc_helpers.c
test/afpd/afpfunc_helpers.h
test/afpd/subtests.h
test/afpd/test.c
test/afpd/test.h

index f9ba2b20ff909c898baa04bd4f33e589c40fde61..c96ca95db3415b8263724df6e73d020fbdc22a47 100644 (file)
@@ -19,6 +19,8 @@ ltconfig
 ltmain.sh
 autom4te.cache
 autoscan.log
+ylwrap
+test-driver
 *.rpm
 *.deb
 *.dsc
index 0100598597364a3d8483a2686979afb9e1ad1d29..4d714f2de666a4451205a3f4bf7657e29334a0bb 100644 (file)
@@ -1,9 +1,9 @@
 # Makefile.am for top level of netatalk package
 
 if USE_BUILTIN_LIBEVENT
-SUBDIRS = libevent libatalk bin config etc man contrib distrib include doc macros test
+SUBDIRS = libevent include libatalk bin config etc contrib distrib doc man macros test
 else
-SUBDIRS = libatalk bin config etc man contrib distrib include doc macros test
+SUBDIRS = include libatalk bin config etc contrib distrib doc man macros test
 endif
 
 EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS VERSION
diff --git a/NEWS b/NEWS
index 866ac8871b478afb263850cbd13976e718f39c30..56a0968eae1f1bfc7a711647819695c22ffcd083 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,234 @@
+Changes in 3.1.3
+================
+* UPD: Spotlight: more SPARQL query optimisations
+* UPD: Spotlight: new options "sparql results limit", "spotlight
+       attributes" and "spotlight expr"
+* FIX: afpd: Unarchiving certain ZIP archives fails, bug #569
+* UPD: Update Unicode support to version 7.0.0
+* FIX: Memory overflow caused by 'basedir regex', bug #567
+* NEW: afpd: delete empty resource forks, from FR #92
+* FIX: afpd: fix a crash when accessing ._ AppleDouble files created
+       by OS X via SMB, bug #564
+* FIX: afpd and dbd: Converting from AppleDouble v2 to ea may corrupt
+       the resource fork. In some circumstances an offset calculation
+       is wrong resulting in corrupt resource forks after the
+       conversion. Bug #568.
+* FIX: ad: fix for bug #563 broke ad file utilities, bug #570.
+* NEW: afpd: new advanced option controlling permissions and ACLs,
+       from FR #93
+
+Changes in 3.1.2
+================
+* FIX: Option "vol dbpath" was broken in 3.1.1
+* FIX: Spotlight: file modification date, bug #545
+* FIX: Improve reliability of afpd child handler
+* FIX: debian initscript: add 0 and 6 to Default-Stop. debian-bug#745520
+* FIX: put the Solaris share reservation after our locking stuff, bug #560.
+* UPD: Improve Linux quota behaviour
+* FIX: xattrs on *BSD, bug #562
+* NEW: afpd: support for using $u username variable in AFP volume
+       definitions. FR#90.
+* FIX: getvolbypath returns incorrect volume, bug #563
+* FIX: fd leak when using appledouble = v2, bug #554
+* UPD: New options that control whether dbus and Tracker are started:
+       'start dbus' and 'start tracker', both default to yes, FR#91
+* UPD: Spotlight: SPARQL query optimisations
+
+Changes in 3.1.1
+================
+* FIX: Add asprint() compatibility function for systems lacking it
+* FIX: Fix ressource fork name conversion. Bug #534.
+* FIX: Fix a bug where only the first configured UAM was loaded.
+       Bug #537.
+* UPD: Add support for AFP 3.4. From FR #85.
+* FIX: Registering with mDNS crashed. Bug #540
+* FIX: Saving from applications like Photoshop may fail, because
+       removing the ressource fork AppleDouble file failed. Bug #542.
+* FIX: dbd: remove orphaned ._ AppleDouble files. Bug #549.
+* NEW: afpd: Automatic conversion of ._ AppleDouble files
+       created by OS X. Bug #550.
+* FIX: afpd: Fix a crash in of_closefork(). Bug #551.
+* FIX: dbd: Don't print message "Ignoring ._file" for every ._ file.
+       Bug #552.
+* FIX: afpd: Don't flood log with failed sys_set_ea() messages.
+
+Changes in 3.1.0
+================
+* NEW: AFP Spotlight support with Gnome Tracker
+* NEW: New option "spotlight" (G/V)
+* NEW: Configure option --with-tracker-pkgconfig-version
+* NEW: Configure option --with-tracker-prefix
+* NEW: If Spotlight is enabled, launch our own dbus instance
+* NEW: New option "dbus daemon" (G)
+* UPD: Add configure option --with-afpstats for overriding the
+       result of autodetecting dbus-glib presence
+* NEW: Add recvfile support with splice() on Linux. New global options
+       "recvfile" (default: no) and "splice size" (default 64k).
+* NEW: CNID backend "mysql" for use with a MySQL server
+
+Changes in 3.0.7
+================
+* FIX: Build fixes for the Kerberos UAM
+* UPD: Use dedicated exit code for AFP connections that were dropped
+       by the client right after the TCP handshake
+* FIX: Workaround for a problem which cannot be advertized by Avahi. Bug #541.
+* FIX: Registering with mDNS crashed. Bug #540
+* FIX: Saving from applications like Photoshop may fail, because
+       removing the ressource fork AppleDouble file failed. Bug #542.
+* FIX: macusers showed root user. Bug #495.
+* UPD: Add file pathname to logmessage parse_entries: bogus eid. FR#87.
+
+Changes in 3.0.6
+================
+* FIX: charset conversion failed when copying from Mac OS 9. Bug #523.
+* UPD: Don't force S_ISGID for directories on FreeBSD. Bug #525.
+* NEW: Add support for ZFS ACLs on FreeBSD with libsunacl. From FR#83.
+* FIX: Active Directory LDAP queries for ACL support with new options
+       "ldap user filter" and "ldap group filter". Bug #526.
+* NEW: Option "vol dbnest", when set to true, the CNID database for
+       a volume is stored in the volume root of a share in a directory
+       .AppleDB like in Netatalk 2. Defaults to false. From FR#84.
+* FIX: Small fix in the DSI tickle handling. Bug #528.
+* UPD: Enhance handling of connection attempts when hitting the
+       connection limit. Bug #529.
+* FIX: Saving from Word to a folder that is a symlink to a folder on
+       another filesystem results in a crash of the afpd process and
+       the save to fail. This happens only if the option
+       "follow symlinks" is enabled. Bug #532.
+* FIX: Disable Kerberos UAM if AFP service principal name can't be
+       evaluated. Fixes bug #531.
+* FIX: Fix handling of large number of volumes. Bug #527.
+* NEW: Configure option --with-tbd which can be used to disable the
+       use of the bundled tdb and use a system installed version.
+
+Changes in 3.0.5
+================
+* FIX: Fix a crash when using pam_winbind. Fixes bug #516.
+* NEW: New global/volume option "ignored attributes"
+* FIX: "afp listen" option failed to take IPv6 addresses. Bug #515.
+* FIX: Fix a possible crash in set_groups. Bug #518.
+* NEW: Send optional AFP messages for vetoed files, new option
+       "veto message" can be used to enable sending messages.
+       Then whenever a client tries to access any file or directory
+       with a vetoed name, it will be sent an AFP message indicating
+       the name and the directory. From FR #81.
+* NEW: New boolean volume option "delete veto files". If this option is
+       set to yes, then Netatalk will attempt to recursively delete any
+       vetoed files and directories. FR #82.
+* UPD: systemd unit dir is /usr/lib/systemd/system .
+* FIX: Saving files from application like MS Word may result in the file
+       loosing metadata like the Finder label. Bug #521.
+
+Changes in 3.0.4
+================
+* FIX: Opening files without metadata EA may result in an invalid
+       metadata EA. Check for malformed metadata EAs and delete them.
+       Fixes bug #510.
+* FIX: Fix an issue with filenames containing non-ASCII characters that
+       lead to a failure setting the size of a files ressource fork.
+       This affected application like Adobe Photoshop where saving
+       files may fail. Fixes bug #511.
+* UPD: Enhance ACL mapping, change global ACL option 'map acls' to take
+       the following options: "none", "rights" (default), "mode".
+       none   = no mapping, this resembles the previous false/no setting
+       rights = map ACLs to Finder UARights, this resembles the previous
+                true/yes setting. This is the default.
+       mode   = map ACLs to Finder UARights and UNIX mode
+       From FR #73.
+* FIX: Fix a possible crash in cname() where cname_mtouname calls
+       dirlookup() where the curdir is freed because the dircache
+       detected a dev/inode cache difference and evicted the object
+       from the cache. Fixes bug #498.
+* FIX: Add missing include, fixes bug #512.
+* FIX: Change default FinderInfo for directories to be all 0, fixes
+       bug 514.
+* NEW: New option "afp interfaces" which allows specifying where
+       Netatalk listens for AFP connections by interface names.
+       From FR #79.
+
+Changes in 3.0.3
+================
+* UPD: afpd: Increase default DSI server quantum to 1 MB
+* UPD: bundled libevent2 is now static
+* NEW: --with-lockfile=PATH configure option for specifying an
+       alternative path for the netatalk lockfile.
+* UPD: systemd service file use PIDFile and ExecReload.
+       From FR #70.
+* UPD: RedHat sysvinit: rm graceful, reimplement reload, add condrestart
+* FIX: Couldn't create folders on FreeBSD 9.1 ZFS fileystems.
+       Fixed bug #491.
+* FIX: Fix an issue with user homes when user home directory has not the
+       same name as the username.
+       Fixes bug #497.
+* UPD: Fix PAM config install, new default installation dir is
+       $sysconfdir/pam.d/. Add configure option --with-pam-confdir
+       to specify alternative path.
+* NEW: AFP stats about active session via dbus IPC. Client side python
+       program `afpstats`. Requires dbus, dbus-glib any python-dbus.
+       configure option --dbus-sysconf-dir for specifying dbus
+       system security configuration files.
+       New option 'afpstats' (default: no) which determines whether
+       to enable the feature or not.
+* NEW: configure option --with-init-dir
+* NEW: dtrace probes, cf include/atalk/afp_dtrace.d for available
+       probes.
+* UPD: Reload groups when reloading volumes. FR #71.
+* FIX: Attempt to read read-only ._ rfork results in disconnect.
+       Fixes bug #502.
+* FIX: File's ressource fork can't be read if metadata EA is missing.
+       Fixes bug #501.
+* FIX: Conversion from adouble v2 to ea for directories.
+       Fixes bug #500.
+* FIX: Error messages when mounting read-only filesystems.
+       Fixes bug #504.
+* FIX: Permissions of ._ AppleDouble ressource fork after conversion
+       from v2 to ea.
+       Fixes bug #505.
+* UPD: Use FreeBSD sendfile() capability to send protocol header.
+       From FR #75.
+* UPD: Increase IO size when sendfile() is not used.
+       From FR #76.
+* FIX: Can't set Finder label on symlinked folder with "follow symlinks = yes".
+       Fixes bug #508.
+* FIX: Setting POSIX ACLs on Linux
+       Fixes bug #506.
+* FIX: "ad ls" segfault if requested object is not in an AFP volume.
+       Fixes bug #496.
+
+Changes in 3.0.2
+================
+* NEW: afpd: Put file extension type/creator mapping back in which had
+       been removed in 3.0.
+* NEW: afpd: new option 'ad domain'. From FR #66.
+* FIX: volumes and home share with symlinks in the path
+* FIX: Copying packages to a Netatalk share could fail, bug #469
+* FIX: Reloading volumes from config file was broken.  Fixes bug #474.
+* FIX: Fix _device-info service type registered with dns-sd API
+* FIX: Fix pathname bug for FCE modified event.
+* FIX: Remove length limitation of options like "valid users".
+       Fixes bug #473.
+* FIX: Dont copy our metadata EA in copyfile(). Fixes bug #452.
+* FIX: Fix an error where catalog search gave incomplete results.
+       Fixes bug #479.
+* REM: Remove TimeMachine volume used size FCE event.
+* UPD: Add quoting support to '[in]valid users' option. Fixes bug #472.
+* FIX: Install working PAM config on Solaris 11. Fixes bug #481.
+* FIX: Fix a race condition between dbd and the cnid_dbd daemon
+       which could result in users being disconnected from volumes
+       when dbd was scanning their volumes. Fixes bug #477.
+* FIX: Netatalk didn't start when the last line of the config file
+       afp.conf wasn't terminated by a newline. Fixes bug #476.
+* NEW: Add a new volumes option 'follow symlinks'. The default setting is
+       false, symlinks are not followed on the server. This is the same
+       behaviour as OS X's AFP server.
+       Setting the option to true causes afpd to follow symlinks on the
+       server. symlinks may point outside of the AFP volume, currently
+       afpd doesn't do any checks for "wide symlinks".
+* FIX: Automatic AppleDouble conversion to EAs failing for directories.
+       Fixes bug #486.
+* FIX: dbd failed to convert appledouble files of symlinks.
+       Fixes bug #490.
+
 Changes in 3.0.1
 ================
 * NEW: afpd: Optional "ldap uuid encoding = string | ms-guid" parameter to
diff --git a/VERSION b/VERSION
index 13d683ccbfeed4ecf19a8f76e016a4e0296ea4cf..781a07bb18b75fa611244d98824dd88729ee1ad3 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.1
\ No newline at end of file
+3.1.3-ab
index 3d3c33dcb4ca8776bbf03b1af19afeaf3d536782..d5a3a2ea964d9e0fb93490091309cd11931ac84f 100755 (executable)
--- a/abigen.sh
+++ b/abigen.sh
@@ -10,7 +10,7 @@ cat <<EOF
 set height 0
 set width 0
 EOF
-nm "$SHAREDLIB" | cut -d' ' -f2- | egrep '^[BDGTRVWS]' | grep -v @ | cut -c3- | sort | while read s; do
+nm "$SHAREDLIB" | cut -d' ' -f2- | egrep '^[BDGTRVWS]' | grep -v @ | cut -c3- | egrep -v '^[_]' | sort | while read s; do
     echo "echo $s: "
     echo p $s
 done
index f5d1448ae3621d70a8241af4864a925335693e68..9aedd4b694a2989e3c8a792205e297bbb0176c28 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile.am for bin/
 
-SUBDIRS = afppasswd cnid megatron uniconv misc
+SUBDIRS = afppasswd cnid megatron misc
 
 if HAVE_ATFUNCS
 SUBDIRS += ad
index 5c3f47b67f918c634dc9247d97d3c70e42896f5c..e7b2c147f53ff8bc738b61f912344335278c3974 100644 (file)
@@ -18,8 +18,7 @@ ad_SOURCES = \
 ad_CFLAGS = -D_PATH_AD=\"$(bindir)/ad\"
 
 ad_LDADD = \
-       $(top_builddir)/libatalk/cnid/libcnid.la \
        $(top_builddir)/libatalk/libatalk.la \
-       @ACL_LIBS@
+       @ACL_LIBS@ @MYSQL_LIBS@
 
 endif
index 7f5b3da6260e626ae210dfa71c82e13777a55240..fc8563c06f034a7c0a660ad0f3075f4946ef98b8 100644 (file)
@@ -58,7 +58,7 @@ int main(int argc, char **argv)
 
     setuplog("default:note", "/dev/tty");
 
-    if (load_volumes(&obj, NULL) != 0)
+    if (load_volumes(&obj, lv_none) != 0)
         return 1;
 
     if (STRCMP(argv[1], ==, "ls"))
index 3195641f7cb291c78e66d580a4d3a047f9d53997..9155900c990f3f9f02773a11ffc7b6f1ea3fcbb6 100644 (file)
@@ -69,7 +69,6 @@ extern int ad_find(int argc, char **argv, AFPObj *obj);
 /* ad_util.c */
 extern int openvol(AFPObj *obj, const char *path, afpvol_t *vol);
 extern void closevol(afpvol_t *vol);
-extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did);
 extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did);
 extern char *utompath(const struct vol *, const char *);
 extern int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen);
index ef995ce8cf3f159c6346a5e32a4858e9cfad8948..5cefe9648a30484cb007d99fd834eabd8bacd4bc 100644 (file)
@@ -95,36 +95,14 @@ static volatile sig_atomic_t alarmed;
 static int badcp, rval;
 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
 
-static char           *netatalk_dirs[] = {
-    ".AppleDouble",
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
-
 /* Forward declarations */
 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
 static int setfile(const struct stat *, int);
-static int preserve_dir_acls(const struct stat *, char *, char *);
+// static int preserve_dir_acls(const struct stat *, char *, char *);
 static int preserve_fd_acls(int, int);
 
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
-
 static void upfunc(void)
 {
     did = pdid;
@@ -531,7 +509,7 @@ static int copy(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             ppdid = pdid;
-            if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
+            if ((did = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &pdid)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", to.p_path);
                 badcp = rval = 1;
                 return -1;
@@ -599,7 +577,7 @@ static int copy(const char *path,
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
             cnid_t cnid;
-            if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(dvolume.vol->v_cdb, dvolume.vol->v_path, to.p_path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", to.p_path);
                 badcp = rval = 1;
                 return -1;
@@ -953,9 +931,9 @@ static int preserve_fd_acls(int source_fd, int dest_fd)
     return (0);
 }
 
+#if 0
 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
 {
-#if 0
     acl_t (*aclgetf)(const char *, acl_type_t);
     int (*aclsetf)(const char *, acl_type_t, acl_t);
     struct acl *aclp;
@@ -1037,6 +1015,6 @@ static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest
         return (1);
     }
     acl_free(acl);
-#endif
     return (0);
 }
+#endif
index 88a46752e2f1199b20f506c3a9d0034c912e645b..093414a1d5e104f5e224c3697dac1e7ef01a9ce8 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <atalk/adouble.h>
 #include <atalk/cnid.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/bstrlib.h>
 #include <atalk/bstradd.h>
 #include <atalk/directory.h>
index d8362595e4647a7ef54098d4f1711b5b45b5b2ce..b5947e17dd0b48c3fa7d12e477128989372fe38d 100644 (file)
@@ -227,7 +227,7 @@ static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
     if (S_ISDIR(st->st_mode))
         adflags = ADFLAGS_DIR;
 
-    if (vol->vol->v_path == NULL)
+    if (vol->vol == NULL || vol->vol->v_path == NULL)
         return;
 
     ad_init(&ad, vol->vol);
index 7f592dd27e1aaf0623bb2a2e34038e13a6468b33..3fdf1ea97ed052418913759c26b19168ad899be2 100644 (file)
@@ -50,31 +50,9 @@ static int fflg, iflg, nflg, vflg;
 static afpvol_t svolume, dvolume;
 static cnid_t did, pdid;
 static volatile sig_atomic_t alarmed;
-static char           *netatalk_dirs[] = {
-    ".AppleDouble",
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
 
 static int copy(const char *, const char *);
 static int do_move(const char *, const char *);
-static void preserve_fd_acls(int source_fd, int dest_fd, const char *source_path,
-                             const char *dest_path);
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
 
 /*
   SIGNAL handling:
@@ -242,7 +220,6 @@ int ad_mv(int argc, char *argv[], AFPObj *obj)
         }
     }
 
-exit:
     closevol(&dvolume);
     return rval;
 }
@@ -301,7 +278,7 @@ static int do_move(const char *from, const char *to)
     
     cnid_t cnid = 0;
     if (!mustcopy) {
-        if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) {
+        if ((cnid = cnid_for_path(svolume.vol->v_cdb, svolume.vol->v_path, from, &did)) == CNID_INVALID) {
             SLOG("Couldn't resolve CNID for %s", from);
             return -1;
         }
@@ -465,12 +442,3 @@ static int copy(const char *from, const char *to)
     }
     return 0;
 }
-
-static void
-preserve_fd_acls(int source_fd,
-                 int dest_fd,
-                 const char *source_path,
-                 const char *dest_path)
-{
-    ;
-}
index 195fee06979301eeef8e9e80d1c66c7d34f00904..b32e659fad5ff73a833a0c6de20575adcde66fb5 100644 (file)
@@ -211,7 +211,7 @@ static int rm(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
-            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
@@ -247,7 +247,7 @@ static int rm(const char *path,
             }
 
             /* Get CNID of Parent and add new childir to CNID database */
-            if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
+            if ((did = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &pdid)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
@@ -293,7 +293,7 @@ static int rm(const char *path,
 
             /* Get CNID of Parent and add new childir to CNID database */
             pdid = did;
-            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+            if ((cnid = cnid_for_path(volume.vol->v_cdb, volume.vol->v_path, path, &did)) == CNID_INVALID) {
                 SLOG("Error resolving CNID for %s", path);
                 return -1;
             }
index d41d5222040d8dce083e9e191d0a8807c71db990..af825093c1de8c2709955b4c9c57944db8124976 100644 (file)
@@ -140,7 +140,6 @@ static void change_label(char *path, afpvol_t *vol, const struct stat *st, struc
 
 static void change_attributes(char *path, afpvol_t *vol, const struct stat *st, struct adouble *ad, char *new_attributes)
 {
-    char *FinderInfo;
     uint16_t AFPattributes;
 
     ad_getattr(ad, &AFPattributes);
@@ -256,7 +255,7 @@ static void change_flags(char *path, afpvol_t *vol, const struct stat *st, struc
 
 int ad_set(int argc, char **argv, AFPObj *obj)
 {
-    int c, firstarg;
+    int c;
     afpvol_t vol;
     struct stat st;
     int adflags = 0;
index 897bdf3b1415c609efdae24f2104d019ef3464fc..27f42b052cec9faff446681358ede8d251d8c557 100644 (file)
@@ -51,6 +51,9 @@
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif  /* HAVE_SOLARIS_ACLS */
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
 
 #ifdef HAVE_POSIX_ACLS
 #include <sys/types.h>
@@ -121,12 +124,9 @@ int openvol(AFPObj *obj, const char *path, afpvol_t *vol)
     if ((vol->vol->v_flags & AFPVOL_NODEV))
         flags |= CNID_FLAG_NODEV;
 
-    if ((vol->vol->v_cdb = cnid_open(vol->vol->v_path,
-                                     0000,
+    if ((vol->vol->v_cdb = cnid_open(vol->vol,
                                      "dbd",
-                                     flags,
-                                     vol->vol->v_cnidserver,
-                                     vol->vol->v_cnidport)) == NULL)
+                                     flags)) == NULL)
         ERROR("Cant initialize CNID database connection for %s", vol->vol->v_path);
 
     cnid_getstamp(vol->vol->v_cdb,
@@ -138,9 +138,11 @@ int openvol(AFPObj *obj, const char *path, afpvol_t *vol)
 
 void closevol(afpvol_t *vol)
 {
-    if (vol->vol->v_cdb) {
-        cnid_close(vol->vol->v_cdb);
-        vol->vol->v_cdb = NULL;
+    if (vol->vol) {
+        if (vol->vol->v_cdb) {
+            cnid_close(vol->vol->v_cdb);
+            vol->vol->v_cdb = NULL;
+        }
     }
     memset(vol, 0, sizeof(afpvol_t));
 }
@@ -231,73 +233,6 @@ int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path
     return 0;
 }
 
-/*!
- * ResolvesCNID of a given paths
- *
- * path might be:
- * (a) relative:
- *     "dir/subdir" with cwd: "/afp_volume/topdir"
- * (b) absolute:
- *     "/afp_volume/dir/subdir"
- *
- * path MUST be pointing inside vol, this is usually the case as vol has been build from
- * path using loadvolinfo and friends.
- *
- * @param vol  (r) pointer to afpvol_t
- * @param path (r) path, see above
- * @param did  (rw) parent CNID of returned CNID
- *
- * @returns CNID of path
- */
-cnid_t cnid_for_path(const afpvol_t *vol,
-                     const char *path,
-                     cnid_t *did)
-{
-    EC_INIT;
-
-    cnid_t cnid;
-    bstring rpath = NULL;
-    bstring statpath = NULL;
-    struct bstrList *l = NULL;
-    struct stat st;
-
-    cnid = htonl(2);
-
-    EC_NULL(rpath = rel_path_in_vol(path, vol->vol->v_path));
-    EC_NULL(statpath = bfromcstr(vol->vol->v_path));
-    EC_ZERO(bcatcstr(statpath, "/"));
-
-    l = bsplit(rpath, '/');
-    for (int i = 0; i < l->qty ; i++) {
-        *did = cnid;
-
-        EC_ZERO(bconcat(statpath, l->entry[i]));
-        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
-                       "lstat(rpath: %s, elem: %s): %s: %s",
-                       cfrombstr(rpath), cfrombstr(l->entry[i]),
-                       cfrombstr(statpath), strerror(errno));
-
-        if ((cnid = cnid_add(vol->vol->v_cdb,
-                             &st,
-                             *did,
-                             cfrombstr(l->entry[i]),
-                             blength(l->entry[i]),
-                             0)) == CNID_INVALID) {
-            EC_FAIL;
-        }
-        EC_ZERO(bcatcstr(statpath, "/"));
-    }
-
-EC_CLEANUP:
-    bdestroy(rpath);
-    bstrListDestroy(l);
-    bdestroy(statpath);
-    if (ret != 0)
-        return CNID_INVALID;
-
-    return cnid;
-}
-
 /*!
  * Resolves CNID of a given paths parent directory
  *
index cab2df66125687dc2014435fd59fac1cd28fea43..578eac1b0a0970a85be4a12175afb65bf7fc35ae 100644 (file)
@@ -9,7 +9,7 @@ bin_PROGRAMS =
 endif
 
 afppasswd_SOURCES = afppasswd.c
-afppasswd_LDADD = $(top_builddir)/libatalk/libatalk.la @SSL_LIBS@
+afppasswd_LDADD = $(top_builddir)/libatalk/libatalk.la @SSL_LIBS@ @MYSQL_LIBS@
 
 AM_CFLAGS = @SSL_CFLAGS@ -I$(top_srcdir)/sys \
     -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\"
index e81ff1f3e320be0b965f29225b69b04fd4516967..c3e3ffd6c30a67e44a619c2d10ecf9529edc6ae5 100755 (executable)
@@ -3,7 +3,6 @@
 #
 # Upgrade version 1 CNID databases to version 2
 #
-# $Id: cnid2_create.in,v 1.2 2005-04-28 20:49:19 bfernhomberg Exp $
 #
 # Copyright (C) Joerg Lenneis 2003
 # All Rights Reserved.  See COPYING.
index 73e4ffaaff1022e6bc60a160ae2d550338b56d17..9ba3918b6a0a2dfa65b7d6de7b6e022003e74f50 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: asingle.c,v 1.14 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index c041ccaa99c9875844bdda6e98387563f4c0b9a4..769d58cd2ede563e086a409ce0ea999a2a88d432 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: asingle.h,v 1.4 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _ASINGLE_H
index da2f71cf58f04f7c25d6a0851fd8d8659ff78605..923ddbd1950131c30e98a87a5e078ff230639d7b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: hqx.c,v 1.18 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 38730d055ef4fbf96b4658279b35f04e8f65b6f8..b656f0b63e0c8f5ded4b9a9a0403c83f331a6f9d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: hqx.h,v 1.3 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _HQX_H
index 51b7ac5f616ede35aeb604892e423e3bbf4a35e6..986aac9247c91f6e7cb210030facfa4d566ef01d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: macbin.c,v 1.15 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index d216b1b9fcae4c5fa50c5f940007f42576218255..a1c5a3a1e83aac017b1e0cb904adb457293aba02 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: macbin.h,v 1.4 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _MACBIN_H
index c9aa8cee547ba75916a9b611e3fdb2c8aaa840d3..421a26afc31040c207c13f3d26137922e75dbdce 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: megatron.h,v 1.5 2009-10-14 01:38:28 didg Exp $
  */
 
 #ifndef _MEGATRON_H
index 9d0b64bf5627612a6bfeb1502d0512b6b8d27f1a..abe4541c71cc2f334fe38404ac308179e66610db 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nad.c,v 1.18 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 9fa8568442fc8a9680f353cf568d8aba3404df3c..f69e73231571d4b476e8cb69eeefe6416d8bfec6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nad.h,v 1.5 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _NAD_H
index d327571dc20d6cc41f9ca215082564403da036ae..ca199c8a8d26fe87c126bda0ab2eacd0d008150e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: updcrc.c,v 1.5 2009-10-13 22:55:36 didg Exp $
  *
  * updcrc(3), crc(1) - calculate crc polynomials
  *
index 0b6039e5ff7ccf45e9763b515cde05b4b890efbf..75c38388bd7d5dcfc522e429e8afe050c230dd34 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: updcrc.h,v 1.1 2009-10-13 22:55:36 didg Exp $
  */
 
 #ifndef _UPDCRC_H
index 5b2fd4e3ffc3154547b344dfaf152710a76f60e0..2cbb8d0a33acb37e625f9a1802c0de7fb2d72add 100644 (file)
@@ -6,18 +6,18 @@ bin_PROGRAMS =
 noinst_PROGRAMS = netacnv logger_test fce
 
 netacnv_SOURCES = netacnv.c
-netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
+netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
 
 logger_test_SOURCES = logger_test.c
-logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la
+logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
 
 fce_SOOURCE = fce.c
-fce_LDADD = $(top_builddir)/libatalk/libatalk.la
+fce_LDADD = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
 fce_CFLAGS = -I$(top_srcdir)/include
 
 bin_PROGRAMS += afpldaptest
 afpldaptest_SOURCES = uuidtest.c
 afpldaptest_CFLAGS = -D_PATH_CONFDIR=\"$(pkgconfdir)/\" @LDAP_CFLAGS@
 afpldaptest_LDFLAGS = @LDAP_LDFLAGS@
-afpldaptest_LDADD  = $(top_builddir)/libatalk/libatalk.la
+afpldaptest_LDADD  = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
 
index d69699079ff99c63df589f5450fbddc226f3ee3f..23101bfbb34ed7a1622c488f82a1fa4e58278c88 100644 (file)
@@ -27,20 +27,9 @@ static char *fce_ev_names[] = {
     "FCE_FILE_DELETE",
     "FCE_DIR_DELETE",
     "FCE_FILE_CREATE",
-    "FCE_DIR_CREATE",
-    "FCE_TM_SIZE"
+    "FCE_DIR_CREATE"
 };
 
-// get sockaddr, IPv4 or IPv6:
-static void *get_in_addr(struct sockaddr *sa)
-{
-    if (sa->sa_family == AF_INET) {
-        return &(((struct sockaddr_in*)sa)->sin_addr);
-    }
-
-    return &(((struct sockaddr_in6*)sa)->sin6_addr);
-}
-
 static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
 {
     unsigned char *p = buf;
@@ -63,6 +52,7 @@ static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
     packet->datalen = ntohs(packet->datalen);
 
     memcpy(&packet->data[0], p, packet->datalen);
+    packet->data[packet->datalen] = 0; /* 0 terminate strings */
     p += packet->datalen;
 
     return 0;
@@ -77,8 +67,6 @@ int main(void)
     struct sockaddr_storage their_addr;
     char buf[MAXBUFLEN];
     socklen_t addr_len;
-    char s[INET6_ADDRSTRLEN];
-    uint64_t tmsize;
 
     memset(&hints, 0, sizeof hints);
     hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
@@ -129,18 +117,11 @@ int main(void)
             exit(1);
         }
 
-        unpack_fce_packet(buf, &packet);
+        unpack_fce_packet((unsigned char *)buf, &packet);
 
         if (memcmp(packet.magic, FCE_PACKET_MAGIC, sizeof(packet.magic)) == 0) {
 
             switch (packet.mode) {
-            case FCE_TM_SIZE:
-                memcpy(&tmsize, packet.data, sizeof(uint64_t));
-                tmsize = ntoh64(tmsize);
-                printf("ID: %" PRIu32 ", Event: %s, Volume: %s, TM used size: %" PRIu64 " \n",
-                       packet.event_id, fce_ev_names[packet.mode], packet.data + sizeof(uint64_t), tmsize);
-                break;
-
             case FCE_CONN_START:
                 printf("FCE Start\n");
                 break;
index 585c15839158a61534c96aafb62258908308d4ef..cf9321142371817b50d47adab5e42f0542e377a2 100644 (file)
@@ -1,3 +1,21 @@
+/*
+  Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <stdio.h>
 #include <stdbool.h>
 
index e42c76b526b36013ace3c53f993ee2be8efb90f9..70c822d80419e97d342cc9ee6e0ae57719de8bf2 100644 (file)
@@ -21,7 +21,6 @@ struct flag_map {
 
 struct flag_map flag_map[] = {
     flag(CONV_ESCAPEHEX),
-    flag(CONV_ALLOW_COLON),
     flag(CONV_UNESCAPEHEX),    
     flag(CONV_ESCAPEDOTS),
     flag(CONV_IGNORE),
@@ -39,29 +38,32 @@ int main(int argc, char **argv)
 {
     int opt;
     uint16_t flags = 0;
-    char *string;
+    char *string, *macName = MACCHARSET;
     char *f = NULL, *t = NULL;
     charset_t from, to, mac;
 
-    while ((opt = getopt(argc, argv, ":o:f:t:")) != -1) {
+    while ((opt = getopt(argc, argv, "m:o:f:t:")) != -1) {
         switch(opt) {
+        case 'm':
+            macName = strdup(optarg);
+            break;
         case 'o':
             for (int i = 0; i < sizeof(flag_map)/sizeof(struct flag_map) - 1; i++)
                 if ((strcmp(flag_map[i].flagname, optarg)) == 0)
                     flags |= flag_map[i].flag;
             break;
         case 'f':
-            f = optarg;
+            f = strdup(optarg);
             break;
         case 't':
-            t = optarg;
+            t = strdup(optarg);
             break;
         }
     }
 
     if ((optind + 1) != argc) {
-        printf("Usage: test [-o <conversion option> [...]] [-f <from charset>] [-t <to charset>] <string>\n");
-        printf("Defaults: -f: UTF8-MAC , -t: UTF8 \n");
+        printf("Usage: test [-o <conversion option> [...]] [-f <from charset>] [-t <to charset>] [-m legacy Mac charset] <string>\n");
+        printf("Defaults: -f: UTF8-MAC, -t: UTF8, -m MAC_ROMAN\n");
         printf("Available conversion options:\n");
         for (int i = 0; i < (sizeof(flag_map)/sizeof(struct flag_map) - 1); i++) {
             printf("%s\n", flag_map[i].flagname);
@@ -70,6 +72,9 @@ int main(int argc, char **argv)
     }
     string = argv[optind];
 
+    set_charset_name(CH_UNIX, "UTF8");
+    set_charset_name(CH_MAC, macName);
+
     if ( (charset_t) -1 == (from = add_charset(f ? f : "UTF8-MAC")) ) {
         fprintf( stderr, "Setting codepage %s as from codepage failed\n", f ? f : "UTF8-MAC");
         return (-1);
@@ -80,7 +85,7 @@ int main(int argc, char **argv)
         return (-1);
     }
 
-    if ( (charset_t) -1 == (mac = add_charset(MACCHARSET)) ) {
+    if ( (charset_t) -1 == (mac = add_charset(macName)) ) {
         fprintf( stderr, "Setting codepage %s as Mac codepage failed\n", MACCHARSET);
         return (-1);
     }
index 030a4f522d7d3fabafddbcc601caf130123aff17..d2548921283eabf15cada777a749f61377de6f2b 100644 (file)
@@ -48,7 +48,7 @@ static void parse_ldapconf()
 #ifdef HAVE_LDAP
         /* Parse afp.conf */
         printf("Start parsing afp.conf\n");
-        iniconfig = iniparser_load(_PATH_CONFDIR "afp.conf");
+        iniconfig = atalk_iniparser_load(_PATH_CONFDIR "afp.conf");
         acl_ldap_readconfig(iniconfig);
         printf("Finished parsing afp.conf\n");
         if (ldap_config_valid) {
index 202e7227cfb9a8da2e2ce56253c4efd52afdfde2..58bbd0bee419f85dbe3fba634179f89e94be6562 100644 (file)
@@ -5,4 +5,4 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/sys
 bin_PROGRAMS = uniconv
 
 uniconv_SOURCES = uniconv.c iso8859_1_adapted.c
-uniconv_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
+uniconv_LDADD = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
index 7cdd43c3995d31ee455910d872f9071d589085b6..96cfcf5840eb5e3137178abe2345c6233ccaa0e7 100644 (file)
@@ -27,8 +27,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
-
-#include "../../libatalk/unicode/byteorder.h"
+#include <atalk/byteorder.h>
 
 static size_t   iso8859_adapted_pull(void *,char **, size_t *, char **, size_t *);
 static size_t   iso8859_adapted_push(void *,char **, size_t *, char **, size_t *);
index c8f80f52699e53ac56d39c8d20e548870f41a6d9..b9a2fc45c87b56e4fdccd2954cd34d32d36ed626 100644 (file)
@@ -389,7 +389,7 @@ static int init(char* path)
 {
        DIR* startdir;
 
-    if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0, "localhost", "4700")) ) {
+    if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0, "localhost", "4700", NULL, NULL)) ) {
                 fprintf (stderr, "ERROR: cannot open CNID database in '%s'\n", path);
                 fprintf (stderr, "ERROR: check the logs for reasons, aborting\n");
                return -1;
index 76b98f3b45993438dce5160bcf203d46c4541953..b847ba65e9f23fd8d3a8fd26c25a6db43f15ca7f 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -1,5 +1,50 @@
 #!/bin/sh
 
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`autoconf' installed to."
+  echo "Download the appropriate package for your distribution,"
+  echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+}
+
+(libtool --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`libtool' installed."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`automake' installed."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(aclocal --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: Missing \`aclocal'.  The version of \`automake'"
+  echo "installed doesn't appear recent enough."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(pkg-config --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`pkg-config' installed."
+  DIE=1
+}
+
+if test "$DIE" -eq 1; then
+  exit 1
+fi
+
 set -x
 
 rm -rf autom4te*.cache
index ec0b4097416de6cd166138540731526921984658..f6ebd89c41505fa9e57e357cdf4895758e34414d 100644 (file)
@@ -2,3 +2,4 @@ Makefile
 Makefile.in
 *.o
 afp.conf
+dbus-session.conf
index 4763046063a2ce5cd491e74dd8f0c46a831fa96c..5003de1b8b9fab7623558883244091a74f463073 100644 (file)
@@ -3,16 +3,22 @@
 SUBDIRS = pam
 SUFFIXES = .tmpl .
 
-TMPLFILES = afp.conf.tmpl
-GENFILES = afp.conf
+TMPLFILES = afp.conf.tmpl dbus-session.conf.tmpl
+GENFILES = afp.conf dbus-session.conf
 CLEANFILES = $(GENFILES)
-EXTRA_DIST = afp.conf.tmpl
+EXTRA_DIST = $(TMPLFILES) extmap.conf netatalk-dbus.conf
 
 OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
 
-CONFFILES = 
+CONFFILES = extmap.conf
 
 pkgconfdir = @PKGCONFDIR@
+
+if HAVE_DBUS_GLIB
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = netatalk-dbus.conf
+endif
+
 #
 # rule to parse template files
 #
@@ -22,6 +28,7 @@ pkgconfdir = @PKGCONFDIR@
            -e s@:ETCDIR:@${pkgconfdir}@ \
            -e s@:COMPILED_BACKENDS:@"$(compiled_backends)"@ \
            -e s@:DEFAULT_CNID_SCHEME:@$(DEFAULT_CNID_SCHEME)@ \
+               -e s@:LOCALSTATEDIR:@"$(localstatedir)"@ \
            <$< >$@
 
 #
@@ -35,10 +42,11 @@ install-data-local: install-config-files
        $(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(localstatedir)/netatalk/CNID/
 
 uninstall-local:
-       for f in $(CONFFILES) $(GENFILES); do \
+       @for f in $(CONFFILES) $(GENFILES); do \
                echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
                rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
        done
+       rm -f $(DESTDIR)$(pkgconfdir)/dbus-session.conf
        rm -f $(DESTDIR)$(localstatedir)/netatalk/README
        rm -f $(DESTDIR)$(localstatedir)/netatalk/CNID/README
 if USE_DEBIAN
@@ -47,7 +55,7 @@ endif
 
 install-config-files: $(CONFFILES) $(GENFILES)
        $(mkinstalldirs) $(DESTDIR)$(pkgconfdir)
-       for f in $(CONFFILES) ; do \
+       @for f in $(CONFFILES) ; do \
                if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \
                        echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \
                        $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgconfdir); \
@@ -55,7 +63,7 @@ install-config-files: $(CONFFILES) $(GENFILES)
                        echo "not overwriting $$f"; \
                fi; \
        done
-       for f in $(GENFILES); do \
+       @for f in $(GENFILES); do \
                if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pkgconfdir)/$$f; then \
                        echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir)"; \
                        $(INSTALL_DATA) $$f $(DESTDIR)$(pkgconfdir); \
diff --git a/config/dbus-session.conf.tmpl b/config/dbus-session.conf.tmpl
new file mode 100644 (file)
index 0000000..f84b923
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <!-- Our well-known bus type, don't change this -->
+  <type>session</type>
+
+  <!-- If we fork, keep the user's original umask to avoid affecting
+       the behavior of child processes. -->
+  <keep_umask/>
+
+  <listen>unix:path=:LOCALSTATEDIR:/netatalk/spotlight.ipc</listen>
+
+  <standard_session_servicedirs />
+
+  <policy context="default">
+    <allow user="*"/>
+    <allow own="*"/>
+    <allow send_destination="*" eavesdrop="true"/>
+    <allow receive_sender="*"/>
+    <allow eavesdrop="true"/>
+  </policy>
+
+  <!-- For the session bus, override the default relatively-low limits 
+       with essentially infinite limits, since the bus is just running 
+       as the user anyway, using up bus resources is not something we need 
+       to worry about. In some cases, we do set the limits lower than 
+       "all available memory" if exceeding the limit is almost certainly a bug, 
+       having the bus enforce a limit is nicer than a huge memory leak. But the 
+       intent is that these limits should never be hit. -->
+
+  <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max -->
+  <limit name="max_incoming_bytes">1000000000</limit>
+  <limit name="max_outgoing_bytes">1000000000</limit>
+  <limit name="max_message_size">1000000000</limit>
+  <limit name="service_start_timeout">120000</limit>  
+  <limit name="auth_timeout">240000</limit>
+  <limit name="max_completed_connections">100000</limit>  
+  <limit name="max_incomplete_connections">10000</limit>
+  <limit name="max_connections_per_user">100000</limit>
+  <limit name="max_pending_service_starts">10000</limit>
+  <limit name="max_names_per_connection">50000</limit>
+  <limit name="max_match_rules_per_connection">50000</limit>
+  <limit name="max_replies_per_connection">50000</limit>
+
+</busconfig>
diff --git a/config/extmap.conf b/config/extmap.conf
new file mode 100644 (file)
index 0000000..44ccec5
--- /dev/null
@@ -0,0 +1,308 @@
+# Netatalk file extension -> OS X type/creator mapping configuration file
+#
+# Delete a '#' character at the head of line to uncomment and enable a mapping
+#
+### Default translation:
+#.         "????"  "????"      Unix Binary                    Unix                      application/octet-stream
+#.         "BINA"  "UNIX"      Unix Binary                    Unix                      application/octet-stream
+#.         "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+
+#.1st      "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+#.669      "6669"  "SNPL"      669 MOD Music                  PlayerPro
+#.8med     "STrk"  "SCPL"      Amiga OctaMed music            SoundApp
+#.8svx     "8SVX"  "SCPL"      Amiga 8-bit sound              SoundApp
+#.a        "TEXT"  "ttxt"      Assembly Source                SimpleText
+#.aif      "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+#.aifc     "AIFC"  "SCPL"      AIFF Sound Compressed          SoundApp                  audio/x-aiff
+#.aiff     "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+#.al       "ALAW"  "SCPL"      ALAW Sound                     SoundApp
+#.ani      "ANIi"  "GKON"      Animated NeoChrome             GraphicConverter
+#.apd      "TEXT"  "ALD3"      Aldus Printer Description      Aldus PageMaker
+#.arc      "mArc"  "SITx"      PC ARChive                     StuffIt Expander
+#.arj      "BINA"  "DArj"      ARJ Archive                    DeArj
+#.arr      "ARR "  "GKON"      Amber ARR image                GraphicConverter
+#.art      "ART "  "GKON"      First Publisher                GraphicConverter
+#.asc      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.ascii    "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.asf      "ASF_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+#.asm      "TEXT"  "ttxt"      Assembly Source                SimpleText
+#.asx      "ASX_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+#.au       "ULAW"  "TVOD"      Sun Sound                      QuickTime Player          audio/basic
+#.avi      "VfW "  "TVOD"      AVI Movie                      QuickTime Player          video/avi
+#.bar      "BARF"  "S691"      Unix BAR Archive               SunTar
+#.bas      "TEXT"  "ttxt"      BASIC Source                   SimpleText
+#.bat      "TEXT"  "ttxt"      MS-DOS Batch File              SimpleText
+#.bga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+#.bib      "TEXT"  "ttxt"      BibTex Bibliography            SimpleText
+#.bin      "SIT!"  "SITx"      MacBinary                      StuffIt Expander          application/macbinary
+#.binary   "BINA"  "hDmp"      Untyped Binary Data            HexEdit                   application/octet-stream
+#.bmp      "BMPp"  "ogle"      Windows Bitmap                 PictureViewer
+#.boo      "TEXT"  "ttxt"      BOO encoded                    SimpleText
+#.bst      "TEXT"  "ttxt"      BibTex Style                   SimpleText
+#.bw       "SGI "  "GKON"      SGI Image                      GraphicConverter
+#.c        "TEXT"  "CWIE"      C Source                       CodeWarrior
+#.cgm      "CGMm"  "GKON"      Computer Graphics Meta         GraphicConverter
+#.class    "Clss"  "CWIE"      Java Class File                CodeWarrior
+#.clp      "CLPp"  "GKON"      Windows Clipboard              GraphicConverter
+#.cmd      "TEXT"  "ttxt"      OS/2 Batch File                SimpleText
+#.com      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+#.cp       "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+#.cpp      "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+#.cpt      "PACT"  "SITx"      Compact Pro Archive            StuffIt Expander
+#.csv      "TEXT"  "XCEL"      Comma Separated Vars           Excel
+#.ct       "..CT"  "GKON"      Scitex-CT                      GraphicConverter
+#.cut      "Halo"  "GKON"      Dr Halo Image                  GraphicConverter
+#.cvs      "drw2"  "DAD2"      Canvas Drawing                 Canvas
+#.dbf      "COMP"  "FOX+"      DBase Document                 FoxBase+
+#.dcx      "DCXx"  "GKON"      Some PCX Images                GraphicConverter
+#.dif      "TEXT"  "XCEL"      Data Interchange Format        Excel
+#.diz      "TEXT"  "R*Ch"      BBS Descriptive Text           BBEdit
+#.dl       "DL  "  "AnVw"      DL Animation                   MacAnim Viewer
+#.dll      "PCFL"  "SWIN"      Windows DLL                    SoftWindows
+#.doc      "WDBN"  "MSWD"      Word Document                  Microsoft Word            application/msword
+#.dot      "sDBN"  "MSWD"      Word for Windows Template      Microsoft Word
+#.dvi      "ODVI"  "xdvi"      TeX DVI Document               xdvi                      application/x-dvi
+#.dwt      "TEXT"  "DmWr"      Dreamweaver Template           Dreamweaver
+#.dxf      "TEXT"  "SWVL"      AutoCAD 3D Data                Swivel Pro
+#.eps      "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+#.epsf     "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+#.etx      "TEXT"  "ezVu"      SEText                         Easy View                 text/x-setext
+#.evy      "EVYD"  "ENVY"      Envoy Document                 Envoy
+#.exe      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+#.faq      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/x-usenet-faq
+#.fit      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter          image/x-fits
+#.fla      "SPA "  "MFL2"      Flash source                   Macromedia Flash
+#.flc      "FLI "  "TVOD"      FLIC Animation                 QuickTime Player
+#.fli      "FLI "  "TVOD"      FLI Animation                  QuickTime Player
+#.fm       "FMPR"  "FMPR"      FileMaker Pro Database         FileMaker Pro
+#.for      "TEXT"  "MPS "      Fortran Source                 MPW Shell
+#.fts      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter
+#.gem      "GEM-"  "GKON"      GEM Metafile                   GraphicConverter
+#.gif      "GIFf"  "ogle"      GIF Picture                    PictureViewer             image/gif
+#.gl       "GL  "  "AnVw"      GL Animation                   MacAnim Viewer
+#.grp      "GRPp"  "GKON"      GRP Image                      GraphicConverter
+#.gz       "SIT!"  "SITx"      Gnu ZIP Archive                StuffIt Expander          application/x-gzip
+#.h        "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hcom     "FSSD"  "SCPL"      SoundEdit Sound ex SOX         SoundApp
+#.hp       "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hpgl     "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+#.hpp      "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hqx      "TEXT"  "SITx"      BinHex                         StuffIt Expander          application/mac-binhex40
+#.htm      "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+#.html     "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+#.i3       "TEXT"  "R*ch"      Modula 3 Interface             BBEdit
+#.ic1      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.ic2      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.ic3      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.icn      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+#.ico      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+#.ief      "IEF "  "GKON"      IEF image                      GraphicConverter          image/ief
+#.iff      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+#.ilbm     "ILBM"  "GKON"      Amiga ILBM Image               GraphicConverter
+#.image    "dImg"  "ddsk"      Apple DiskCopy Image           Disk Copy
+#.img      "IMGg"  "GKON"      GEM bit image/XIMG             GraphicConverter
+#.ini      "TEXT"  "ttxt"      Windows INI File               SimpleText
+#.java     "TEXT"  "CWIE"      Java Source File               CodeWarrior
+#.jfif     "JPEG"  "ogle"      JFIF Image                     PictureViewer
+#.jpe      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.jpeg     "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.jpg      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.latex    "TEXT"  "OTEX"      Latex                          OzTex                     application/x-latex
+#.lbm      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+#.lha      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+#.lzh      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+#.m1a      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.m1s      "MPEG"  "TVOD"      MPEG-1 systemstream            MoviePlayer
+#.m1v      "M1V "  "TVOD"      MPEG-1 IPB videostream         MoviePlayer               video/mpeg
+#.m2       "TEXT"  "R*ch"      Modula 2 Source                BBEdit
+#.m2v      "MPG2"  "MPG2"      MPEG-2 IPB videostream         MPEG2decoder
+#.m3       "TEXT"  "R*ch"      Modula 3 Source                BBEdit
+#.mac      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.mak      "TEXT"  "R*ch"      Makefile                       BBEdit
+#.mcw      "WDBN"  "MSWD"      Mac Word Document              Microsoft Word
+#.me       "TEXT"  "ttxt"      Text Readme                    SimpleText
+#.med      "STrk"  "SCPL"      Amiga MED Sound                SoundApp
+#.mf       "TEXT"  "*MF*"      Metafont                       Metafont
+#.mid      "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+#.midi     "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+#.mif      "TEXT"  "Fram"      FrameMaker MIF                 FrameMaker                application/x-framemaker
+#.mime     "TEXT"  "SITx"      MIME Message                   StuffIt Expander          message/rfc822
+#.ml       "TEXT"  "R*ch"      ML Source                      BBEdit
+#.mod      "STrk"  "SCPL"      MOD Music                      SoundApp
+#.mol      "TEXT"  "RSML"      MDL Molfile                    RasMac
+#.moov     "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.mov      "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.mp2      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.mp3      "MPG3"  "TVOD"      MPEG-3 audiostream             MoviePlayer               audio/x-mpeg
+#.mpa      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.mpe      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.mpeg     "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.mpg      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.msp      "MSPp"  "GKON"      Microsoft Paint                GraphicConverter
+#.mtm      "MTM "  "SNPL"      MultiMOD Music                 PlayerPro
+#.mw       "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+#.mwii     "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+#.neo      "NeoC"  "GKON"      Atari NeoChrome                GraphicConverter
+#.nfo      "TEXT"  "ttxt"      Info Text                      SimpleText                application/text
+#.nst      "STrk"  "SCPL"      MOD Music                      SoundApp
+#.obj      "PCFL"  "SWIN"      Object (DOS/Windows)           SoftWindows
+#.oda      "ODIF"  "ODA "      ODA Document                   MacODA XTND Translator    application/oda
+#.okt      "OKTA"  "SCPL"      Oktalyser MOD Music            SoundApp
+#.out      "BINA"  "hDmp"      Output File                    HexEdit
+#.ovl      "PCFL"  "SWIN"      Overlay (DOS/Windows)          SoftWindows
+#.p        "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+#.pac      "STAD"  "GKON"      Atari STAD Image               GraphicConverter
+#.pas      "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+#.pbm      "PPGM"  "GKON"      Portable Bitmap                GraphicConverter          image/x-portable-bitmap
+#.pc1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pc2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pc3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pcs      "PICS"  "GKON"      Animated PICTs                 GraphicConverter
+#.pct      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.pcx      "PCXx"  "GKON"      PC PaintBrush                  GraphicConverter
+#.pdb      "TEXT"  "RSML"      Brookhaven PDB file            RasMac
+#.pdf      "PDF "  "CARO"      Portable Document Format       Acrobat Reader            application/pdf
+#.pdx      "TEXT"  "ALD5"      Printer Description            PageMaker
+#.pf       "CSIT"  "SITx"      Private File                   StuffIt Expander 
+#.pgm      "PPGM"  "GKON"      Portable Graymap               GraphicConverter          image/x-portable-graymap
+#.pi1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pi2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pi3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pic      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.pict     "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-macpict
+#.pit      "PIT "  "SITx"      PackIt Archive                 StuffIt Expander
+#.pkg      "HBSF"  "SITx"      AppleLink Package              StuffIt Expander
+#.pl       "TEXT"  "McPL"      Perl Source                    MacPerl
+#.plt      "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+#.pm       "PMpm"  "GKON"      Bitmap from xv                 GraphicConverter
+#.pm3      "ALB3"  "ALD3"      PageMaker 3 Document           PageMaker
+#.pm4      "ALB4"  "ALD4"      PageMaker 4 Document           PageMaker
+#.pm5      "ALB5"  "ALD5"      PageMaker 5 Document           PageMaker
+#.png      "PNG "  "ogle"      Portable Network Graphic       PictureViewer
+#.pntg     "PNTG"  "ogle"      Macintosh Painting             PictureViewer
+#.ppd      "TEXT"  "ALD5"      Printer Description            PageMaker
+#.ppm      "PPGM"  "GKON"      Portable Pixmap                GraphicConverter          image/x-portable-pixmap
+#.prn      "TEXT"  "R*ch"      Printer Output File            BBEdit
+#.ps       "TEXT"  "vgrd"      PostScript                     LaserWriter 8             application/postscript
+#.psd      "8BPS"  "8BIM"      PhotoShop Document             Photoshop
+#.pt4      "ALT4"  "ALD4"      PageMaker 4 Template           PageMaker
+#.pt5      "ALT5"  "ALD5"      PageMaker 5 Template           PageMaker
+#.pxr      "PXR "  "8BIM"      Pixar Image                    Photoshop
+#.qdv      "QDVf"  "GKON"      QDV image                      GraphicConverter
+#.qt       "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.qxd      "XDOC"  "XPR3"      QuarkXpress Document           QuarkXpress
+#.qxt      "XTMP"  "XPR3"      QuarkXpress Template           QuarkXpress
+#.raw      "BINA"  "GKON"      Raw Image                      GraphicConverter
+#.readme   "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+#.rgb      "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+#.rgba     "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+#.rib      "TEXT"  "RINI"      Renderman 3D Data              Renderman
+#.rif      "RIFF"  "GKON"      RIFF Graphic                   GraphicConverter
+#.rle      "RLE "  "GKON"      RLE image                      GraphicConverter
+#.rme      "TEXT"  "ttxt"      Text Readme                    SimpleText
+#.rpl      "FRL!"  "REP!"      Replica Document               Replica
+#.rsc      "rsrc"  "RSED"      Resource File                  ResEdit
+#.rsrc     "rsrc"  "RSED"      Resource File                  ResEdit
+#.rtf      "TEXT"  "MSWD"      Rich Text Format               Microsoft Word            application/rtf
+#.rtx      "TEXT"  "R*ch"      Rich Text                      BBEdit                    text/richtext
+#.s3m      "S3M "  "SNPL"      ScreamTracker 3 MOD            PlayerPro
+#.scc      "MSX "  "GKON"      MSX pitcure                    GraphicConverter
+#.scg      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.sci      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scp      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scr      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scu      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.sea      "APPL"  "????"      Self-Extracting Archive        Self Extracting Archive
+#.sf       "IRCM"  "SDHK"      IRCAM Sound                    SoundHack
+#.sgi      ".SGI"  "ogle"      SGI Image                      PictureViewer
+#.sha      "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+#.shar     "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+#.shp      "SHPp"  "GKON"      Printmaster Icon Library       GraphicConverter
+#.sit      "SIT!"  "SITx"      StuffIt 1.5.1 Archive          StuffIt Expander          application/x-stuffit
+#.sithqx   "TEXT"  "SITx"      BinHexed StuffIt Archive       StuffIt Expander          application/mac-binhex40
+#.six      "SIXE"  "GKON"      SIXEL image                    GraphicConverter
+#.slk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.snd      "BINA"  "SCPL"      Sound of various types         SoundApp
+#.spc      "Spec"  "GKON"      Atari Spectrum 512             GraphicConverter
+#.sr       "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+#.sty      "TEXT"  "*TEX"      TeX Style                      Textures
+#.sun      "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+#.sup      "SCRN"  "GKON"      StartupScreen                  GraphicConverter
+#.svx      "8SVX"  "SCPL"      Amiga IFF Sound                SoundApp
+#.syk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.sylk     "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.tar      "TARF"  "SITx"      Unix Tape ARchive              StuffIt Expander          application/x-tar
+#.targa    "TPIC"  "GKON"      Truevision Image               GraphicConverter
+#.taz      "ZIVU"  "SITx"      Compressed Tape ARchive        StuffIt Expander          application/x-compress
+#.tex      "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-tex
+#.texi     "TEXT"  "OTEX"      TeX Document                   OzTeX
+#.texinfo  "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-texinfo
+#.text     "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.tga      "TPIC"  "GKON"      Truevision Image               GraphicConverter
+#.tgz      "Gzip"  "SITx"      Gnu ZIPed Tape ARchive         StuffIt Expander          application/x-gzip
+#.tif      "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+#.tiff     "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+#.tny      "TINY"  "GKON"      Atari TINY Bitmap              GraphicConverter
+#.tsv      "TEXT"  "XCEL"      Tab Separated Values           Excel                     text/tab-separated-values
+#.tx8      "TEXT"  "ttxt"      8-bit ASCII Text               SimpleText
+#.txt      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.ul       "ULAW"  "TVOD"      Mu-Law Sound                   MoviePlayer               audio/basic
+#.url      "AURL"  "Arch"      URL Bookmark                   Anarchie                  message/external-body
+#.uu       "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+#.uue      "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+#.vff      "VFFf"  "GKON"      DESR VFF Greyscale Image       GraphicConverter
+#.vga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+#.voc      "VOC "  "SCPL"      VOC Sound                      SoundApp
+#.w51      ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+#.wav      "WAVE"  "TVOD"      Windows WAV Sound              MoviePlayer               audio/x-wav
+#.wk1      "XLBN"  "XCEL"      Lotus Spreadsheet r2.1         Excel
+#.wks      "XLBN"  "XCEL"      Lotus Spreadsheet r1.x         Excel
+#.wmf      "WMF "  "GKON"      Windows Metafile               GraphicConverter
+#.wp       ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+#.wp4      ".WP4"  "WPC2"      WordPerfect PC 4.2 Doc         WordPerfect
+#.wp5      ".WP5"  "WPC2"      WordPerfect PC 5.x Doc         WordPerfect               application/wordperfect5.1
+#.wp6      ".WP6"  "WPC2"      WordPerfect PC 6.x Doc         WordPerfect
+#.wpg      "WPGf"  "GKON"      WordPerfect Graphic            GraphicConverter
+#.wpm      "WPD1"  "WPC2"      WordPerfect Mac                WordPerfect
+#.wri      "WDBN"  "MSWD"      MS Write/Windows               Microsoft Word
+#.wve      "BINA"  "SCPL"      PSION sound                    SoundApp
+#.x10      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.x11      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.xbm      "XBM "  "GKON"      X-Windows Bitmap               GraphicConverter          image/x-xbm
+#.xl       "XLS "  "XCEL"      Excel Spreadsheet              Excel
+#.xlc      "XLC "  "XCEL"      Excel Chart                    Excel
+#.xlm      "XLM "  "XCEL"      Excel Macro                    Excel
+#.xls      "XLS "  "XCEL"      Excel Spreadsheet              Excel
+#.xlw      "XLW "  "XCEL"      Excel Workspace                Excel
+#.xm       "XM  "  "SNPL"      FastTracker MOD Music          PlayerPro
+#.xpm      "XPM "  "GKON"      X-Windows Pixmap               GraphicConverter          image/x-xpm
+#.xwd      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.Z        "ZIVU"  "SITx"      Unix Compress Archive          StuffIt Expander          application/x-compress
+#.zip      "ZIP "  "SITx"      PC ZIP Archive                 StuffIt Expander          application/zip
+#.zoo      "Zoo "  "Booz"      Zoo Archive                    MacBooz
+
+# I'd like to dedicate this as follows code to Miss.Tamaki Imazu
+#
+# Kazuhiko Okudaira the Nursery Teacher
+# kokudaira@hotmail.com
+
+#.bld  "BLD "  "GKON"  BLD                            GraphicConverter
+#.bum  ".bMp"  "GKON"  QuickTime Importer(QuickDraw)  GraphicConverter
+#.cel  "CEL "  "GKON"  KISS CEL                       GraphicConverter
+#.cur  "CUR "  "GKON"  Windows Cursor                 GraphicConverter
+#.cwj  "CWSS"  "cwkj"  ClarisWorks Document           ClarisWorks 4.0
+#.dat  "TCLl"  "GKON"  TCL image                      GraphicConverter
+#.hr   "TR80"  "GKON"  TSR-80 HR                      GraphicConverter
+#.iss  "ISS "  "GKON"  ISS                            GraphicConverter
+#.jif  "JIFf"  "GKON"  JIF99a                         GraphicConverter
+#.lwf  "lwfF"  "GKON"  LuraWave(LWF)                  GraphicConverter
+#.mbm  "MBM "  "GKON"  PSION 5(MBM)                   GraphicConverter
+#.ngg  "NGGC"  "GKON"  Mobile Phone(Nokia)Format      GraphicConverter
+#.nol  "NOL "  "GKON"  Mobile Phone(Nokia)Format      GraphicConverter
+#.pal  "8BCT"  "8BIM"  Color Table                    GraphicConverter
+#.pgc  "PGCF"  "GKON"  PGC/PGF  Atari Portfolio PCG   GraphicConverter
+#.pics "PICS"  "GKON"  PICS-PICT Sequence             GraphicConverter
+#.swf  "SWFL"  "SWF2"  Flash                          Macromedia Flash
+#.vpb  "VPB "  "GKON"  VPB QUANTEL                    GraphicConverter
+#.wbmp "WBMP"  "GKON"  WBMP                           GraphicConverter
+#.x-face  "TEXT"  "GKON"  X-Face                      GraphicConverter
diff --git a/config/netatalk-dbus.conf b/config/netatalk-dbus.conf
new file mode 100644 (file)
index 0000000..1646efd
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE busconfig PUBLIC
+          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- Only root can own AFP stats service -->
+  <policy user="root">
+    <allow own="org.netatalk.AFPStats"/>
+  </policy>
+
+  <!-- Allow anyone to invoke methods on time-sliderd -->
+  <policy context="default">
+    <allow send_destination="org.netatalk.AFPStats"/>
+    <allow receive_sender="org.netatalk.AFPStats"/>
+  </policy>
+
+</busconfig>
index dbbab4b2b68881f9239845ac8595eec98f1272d7..d209ccf96dcf4b7da7c1d8ae5208136bbe3569a7 100644 (file)
@@ -1,5 +1,5 @@
 Makefile
 Makefile.in
 netatalk.pam
-.gitignore
+netatalk
 *.o
index e01d784c675d54261bdba14f05a5d0321d21c7ad..62affbdac21e5b18603b6cb3879352a3ebab74aa 100644 (file)
@@ -1,10 +1,9 @@
 ## Makefile for distrib/pam/
 
 SUFFIXES = .tmpl .
-pamdir = @PAMDIR@/etc/pam.d
-EXTRA_DIST = netatalk.pam.tmpl
-noinst_SCRIPTS = netatalk.pam
-CLEANFILES = netatalk.pam
+EXTRA_DIST = netatalk.tmpl
+noinst_SCRIPTS = netatalk
+CLEANFILES = netatalk
 
 .tmpl:
        sed -e "s,[@]PAM_DIRECTIVE[@],${PAM_DIRECTIVE},g" \
@@ -15,23 +14,6 @@ CLEANFILES = netatalk.pam
            <$< >$@
 
 if USE_PAM
-install-data-local: netatalk.pam
-       $(mkinstalldirs) $(DESTDIR)$(pamdir)
-       if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pamdir)/netatalk; then \
-           echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pamdir)/netatalk"; \
-           $(INSTALL_DATA) netatalk.pam $(DESTDIR)$(pamdir)/netatalk || echo "WARNING: Can't install PAM files";               \
-       else \
-           echo "not overwriting $(DESTDIR)$(pamdir)/netatalk"; \
-       fi; 
-
-uninstall-local:
-       echo rm -f $(DESTDIR)$(pamdir)/netatalk; \
-       rm -f $(DESTDIR)$(pamdir)/netatalk; \
-       for f in $(CONFFILES) $(GENFILES); do \
-               echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
-               rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
-       done
-else
-install-data-local:
-uninstall-local:
+pamdir = $(PAMDIR)
+pam_DATA = netatalk
 endif
diff --git a/config/pam/netatalk.pam.tmpl b/config/pam/netatalk.pam.tmpl
deleted file mode 100644 (file)
index 1eceba3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#%PAM-1.0
-auth     @PAM_DIRECTIVE@ @PAM_AUTH@
-account  @PAM_DIRECTIVE@ @PAM_ACCOUNT@
-password @PAM_DIRECTIVE@ @PAM_PASSWORD@
-session  @PAM_DIRECTIVE@ @PAM_SESSION@
diff --git a/config/pam/netatalk.tmpl b/config/pam/netatalk.tmpl
new file mode 100644 (file)
index 0000000..1eceba3
--- /dev/null
@@ -0,0 +1,5 @@
+#%PAM-1.0
+auth     @PAM_DIRECTIVE@ @PAM_AUTH@
+account  @PAM_DIRECTIVE@ @PAM_ACCOUNT@
+password @PAM_DIRECTIVE@ @PAM_PASSWORD@
+session  @PAM_DIRECTIVE@ @PAM_SESSION@
index 49f70c603f2f4946763177f11df9aa772d559778..6f54a896ce37402f8edd86146de575e7b1f25069 100644 (file)
@@ -25,6 +25,8 @@ AC_PROG_PS
 AM_PROG_CC_C_O
 AC_C_BIGENDIAN
 AC_C_INLINE
+AC_PROG_LEX
+AC_PROG_YACC
 
 dnl Check if we can use attribute unused (gcc only) from ethereal
 AC_MSG_CHECKING(to see if we can add '__attribute__((unused))' to CFLAGS)
@@ -74,17 +76,15 @@ AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
 
 dnl these tests have been comfirmed to be needed in 2011
 AC_CHECK_FUNCS(backtrace_symbols dirfd getusershell pread pwrite pselect)
-AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen)
+AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen mempcpy vasprintf asprintf)
 AC_CHECK_FUNCS(mmap utime getpagesize) dnl needed by tbd
 
 dnl search for necessary libraries
 AC_SEARCH_LIBS(gethostbyname, nsl)
 AC_SEARCH_LIBS(connect, socket)
-AC_SEARCH_LIBS(pthread_sigmask, pthread,,[AC_MSG_ERROR([missing pthread_sigmask])])
-if test x"$ac_cv_search_pthread_sigmask" != x"none required" ; then
-   PTHREAD_LIBS=$ac_cv_search_pthread_sigmask
-fi
-AC_SUBST(PTHREAD_LIBS)
+AC_CHECK_FUNCS(getifaddrs) dnl comes after gethostbyname and connect so it picks up the libs
+
+AX_PTHREAD(, [AC_MSG_ERROR([missing pthread_sigmask])])
 
 AC_DEFINE(OPEN_NOFOLLOW_ERRNO, ELOOP, errno returned by open with O_NOFOLLOW)
 
@@ -180,16 +180,36 @@ AC_NETATALK_REALPATH
 
 dnl Check for sendfile()
 AC_NETATALK_SENDFILE
+AC_NETATALK_RECVFILE
 
 dnl Check whether bundled libevent shall not be used
 AC_NETATALK_LIBEVENT
 
+dnl Check whether bundled tdb shall be used
+AC_NETATALK_TDB
+
+dnl Check for Tracker
+AC_NETATALK_SPOTLIGHT
+
+dnl libatalk API checks
+AC_DEVELOPER
+
+dnl Check for dtrace
+AC_NETATALK_DTRACE
+
+dnl Check for dbus-glib, for AFP stats on dbus
+AC_NETATALK_DBUS_GLIB
+
 dnl FHS stuff has to be done last because it overrides other defaults
 AC_NETATALK_FHS
 
-AC_DEVELOPER
+dnl netatalk lockfile path, must come after AC_NETATALK_FHS
+AC_NETATALK_LOCKFILE
+
+dnl Check for Docbook and build documentation if found
+AX_CHECK_DOCBOOK
 
-CFLAGS="-I\$(top_srcdir)/include -I\$(top_srcdir)/sys $CFLAGS"
+CPPFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/include $CPPFLAGS"
 UAMS_PATH="${uams_path}"
 
 AC_SUBST(LIBS)
@@ -199,7 +219,7 @@ AC_SUBST(UAMS_PATH)
 AM_CONDITIONAL(SOLARIS_MODULE, test x$solaris_module = xyes)
 AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
 AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
-AM_CONDITIONAL(HAVE_ACLS, test x"$with_acl_support" = x"yes")
+AM_CONDITIONAL(HAVE_ACLS, test x"$ac_cv_have_acls" = x"yes")
 AM_CONDITIONAL(HAVE_LDAP, test x"$netatalk_cv_ldap" = x"yes")
 AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
 AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
@@ -225,7 +245,6 @@ AC_OUTPUT([Makefile
        bin/cnid/cnid2_create
        bin/megatron/Makefile
        bin/misc/Makefile
-       bin/uniconv/Makefile
        config/Makefile
        config/pam/Makefile
        contrib/Makefile
@@ -240,10 +259,19 @@ AC_OUTPUT([Makefile
        distrib/initscripts/Makefile
        distrib/m4/Makefile
        doc/Makefile
+    doc/html.xsl
+    doc/man.xsl
+    doc/manual/Makefile
+    doc/manual/manual.xml
+    doc/manpages/Makefile
+    doc/manpages/man1/Makefile
+    doc/manpages/man5/Makefile
+    doc/manpages/man8/Makefile
        etc/Makefile
        etc/afpd/Makefile
        etc/cnid_dbd/Makefile
        etc/netatalk/Makefile
+       etc/spotlight/Makefile
        etc/uams/Makefile
        include/Makefile
        include/atalk/Makefile
@@ -256,9 +284,11 @@ AC_OUTPUT([Makefile
        libatalk/cnid/last/Makefile
        libatalk/cnid/dbd/Makefile
        libatalk/cnid/tdb/Makefile
+       libatalk/cnid/mysql/Makefile
        libatalk/compat/Makefile
        libatalk/dsi/Makefile
        libatalk/iniparser/Makefile
+       libatalk/talloc/Makefile
        libatalk/tdb/Makefile
        libatalk/unicode/Makefile
        libatalk/unicode/charsets/Makefile
@@ -267,8 +297,26 @@ AC_OUTPUT([Makefile
        macros/Makefile
        man/Makefile
        man/man1/Makefile
+    man/man1/ad.1
+    man/man1/afpldaptest.1
+    man/man1/afppasswd.1
+    man/man1/afpstats.1
+    man/man1/apple_dump.1
+    man/man1/asip-status.pl.1
+    man/man1/dbd.1
+    man/man1/macusers.1
+    man/man1/netatalk-config.1
+    man/man1/uniconv.1
        man/man5/Makefile
+    man/man5/afp.conf.5
+    man/man5/afp_signature.conf.5
+    man/man5/afp_voluuid.conf.5
+    man/man5/extmap.conf.5
        man/man8/Makefile
+    man/man8/afpd.8
+    man/man8/cnid_dbd.8
+    man/man8/cnid_metad.8
+    man/man8/netatalk.8
        test/Makefile
        test/afpd/Makefile
        ],
index b37955512118e7164e9f379ba1d5260e1074dc23..7366a8b6c7135fe4b1fa755eecc3fef381676ff2 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use Socket;
 use File::Basename;
-use vars qw($MAC_PROCESS $PS_STR $MATCH_STR $ASIP_PORT_NO $ASIP_PORT $LSOF);
+use vars qw($MAIN_PID $NETATALK_PROCESS $AFPD_PROCESS $PS_STR $MATCH_STR $ASIP_PORT_NO $ASIP_PORT $LSOF);
 
 # Written for linux; may have to be modified for your brand of Unix.
 # Support for FreeBSD added by Joe Clarke <marcus@marcuscom.com>.
@@ -19,7 +19,8 @@ if ($ARGV[0] =~ /^(-v|-version|--version)$/ ) {
         exit(1);
 }
 
-$MAC_PROCESS = "afpd";
+$NETATALK_PROCESS = "netatalk";
+$AFPD_PROCESS = "afpd";
 if ($^O eq "freebsd" || $^O eq "openbsd") {
         $PS_STR    = "-awwxouser,pid,ppid,start,command";
         $MATCH_STR = '(\w+)\s+(\d+)\s+(\d+)\s+([\d\w:]+)';
@@ -38,10 +39,10 @@ $LSOF = 1;
 my %mac = ();
 
 if ($^O eq "freebsd") {
-        open(SOCKSTAT, "sockstat -4 | grep $MAC_PROCESS | grep -v grep |");
+        open(SOCKSTAT, "sockstat -4 | grep $AFPD_PROCESS | grep -v grep |");
 
         while (<SOCKSTAT>) {
-                next if ($_ !~ /$MAC_PROCESS/);
+                next if ($_ !~ /$AFPD_PROCESS/);
                 $_ =~
                     /\S+\s+\S+\s+(\d+)\s+\d+\s+[\w\d]+\s+[\d\.:]+\s+([\d\.]+)/;
                 my ($pid, $addr, $host);
@@ -82,8 +83,19 @@ if ($^O eq "freebsd") {
 
 open(PS, "ps $PS_STR |") || die "Unable to open a pipe to ``ps''";
 
+$MAIN_PID = 1;
 while (<PS>) {
-        next if ($_ !~ /$MAC_PROCESS/);
+        next if ($_ !~ /$NETATALK_PROCESS/);
+        my ($user, $pid, $ppid, $time, $name, $uid, $t, $ip);
+        $_ =~ /$MATCH_STR/;
+        $MAIN_PID = $2;
+}
+
+close(PS);
+open(PS, "ps $PS_STR |") || die "Unable to open a pipe to ``ps''";
+
+while (<PS>) {
+        next if ($_ !~ /$AFPD_PROCESS/);
         my ($user, $pid, $ppid, $time, $name, $uid, $t, $ip);
         $_ =~ /$MATCH_STR/;
         $user = $1;
@@ -91,7 +103,7 @@ while (<PS>) {
         $ppid = $3;
         $time = $4;
 
-        if ($ppid != 1) {
+        if ($ppid != $MAIN_PID) {
                 if ($^O eq "solaris") {
                         open(PFILES, "pfiles $pid |");
                         while (<PFILES>) {
index 7d69713e3249932c9282f1adfefb490d6a28d3d6..14245d2d6cc987819bb2fe081b8fac731c387dd1 100644 (file)
@@ -1,8 +1,35 @@
 diff --git a/libevent/Makefile.am b/libevent/Makefile.am
-index 46f6d34..dda19b2 100644
+index 09505bd..8114ad3 100644
 --- a/libevent/Makefile.am
 +++ b/libevent/Makefile.am
-@@ -173,7 +173,7 @@ NO_UNDEFINED =
+@@ -86,9 +86,6 @@ if INSTALL_LIBEVENT
+ dist_bin_SCRIPTS = event_rpcgen.py
+ endif
+-pkgconfigdir=$(libdir)/pkgconfig
+-LIBEVENT_PKGCONFIG=libevent.pc
+-
+ # These sources are conditionally added by configure.in or conditionally
+ # included from other files.
+ PLATFORM_DEPENDENT_SRC = \
+@@ -108,16 +105,13 @@ EXTRA_DIST = \
+ LIBEVENT_LIBS_LA = libevent.la libevent_core.la libevent_extra.la
+ if PTHREADS
+ LIBEVENT_LIBS_LA += libevent_pthreads.la
+-LIBEVENT_PKGCONFIG += libevent_pthreads.pc
+ endif
+ if OPENSSL
+ LIBEVENT_LIBS_LA += libevent_openssl.la
+-LIBEVENT_PKGCONFIG += libevent_openssl.pc
+ endif
+ if INSTALL_LIBEVENT
+ lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA)
+-pkgconfig_DATA = $(LIBEVENT_PKGCONFIG)
+ else
+ noinst_LTLIBRARIES =  $(LIBEVENT_LIBS_LA)
+ endif
+@@ -196,7 +190,7 @@ NO_UNDEFINED =
  MAYBE_CORE =
  endif
  
@@ -11,9 +38,12 @@ index 46f6d34..dda19b2 100644
  
  libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
  libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
-@@ -221,3 +221,5 @@ FORCE:
- DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
+@@ -249,5 +243,7 @@ doxygen: FORCE
+       doxygen $(srcdir)/Doxyfile
+ FORCE:
  
-+install:
+-DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
++DISTCLEANFILES = *~ ./include/event2/event-config.h
 +
++install:
index bb4d9cbf0637381a924e0a0d2f41b8364381ce72..dc4c7db7076b6f2b1de73ed7546a2acb1c3a182c 100644 (file)
@@ -18,6 +18,6 @@ SUFFIXES = .tmpl .
 
 CLEANFILES = $(GENERATED_FILES)
 
-bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
+bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES) afpstats
 
-EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl
+EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl afpstats
diff --git a/contrib/shell_utils/afpstats b/contrib/shell_utils/afpstats
new file mode 100755 (executable)
index 0000000..8c5413c
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python afpstats.py
+"""
+
+import sys
+from traceback import print_exc
+import dbus
+
+def main():
+    bus = dbus.SystemBus()
+
+    try:
+        remote_object = bus.get_object("org.netatalk.AFPStats",
+                                       "/org/netatalk/AFPStats")
+
+    except dbus.DBusException:
+        print_exc()
+        sys.exit(1)
+
+    iface = dbus.Interface(remote_object, "org.netatalk.AFPStats")
+
+    reply = iface.GetUsers()
+    for name in reply:
+        print name
+
+if __name__ == '__main__':
+    main()
index 6054e0f8bb26bb5a71382a9f3736619e981ba0f1..30dbb425ba957317765dbfb3d152b862e184be22 100755 (executable)
@@ -50,6 +50,7 @@ use File::Basename;
 use File::Spec;
 use File::Temp qw /tempfile/;
 use bigint; # require perl >= 5.8
+use IPC::Open2 qw /open2/;
 
 # check command for extended attributes -----------------------------------
 
@@ -841,50 +842,46 @@ sub hexdump {
 sub checkea {
     my ($file) = @_;
 
-    $file =~ s/\\/\\\\/g;
-    $file =~ s/\"/\\\"/g;
-    $file =~ s/\$/\\\$/g;
-    $file =~ s/\`/\\\`/g;
     if ( $eacommand == 1 ) {
-        open(EALIST, "getfattr \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'getfattr', $file) or die $@;
         while(<EALIST>) {
             if ( $_ eq "user.org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 2 ) {
-        open(EALIST, "attr -q -l \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'attr', '-q', '-l', $file) or die $@;
         while(<EALIST>) {
             if ( $_ eq "org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 3 ) {
-        open(EALIST, "runat \"$file\" ls -1 |");
+        open2(\*EALIST, \*EAIN, 'runat', $file, 'ls', '-1') or die $@;
         while(<EALIST>) {
             if ( $_ eq "org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 4 ) {
-        open(EALIST, "lsextattr -q user \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'lsextattr', '-q', 'user', $file) or die $@;
         while(<EALIST>) {
             $_ = "\t".$_;
             if ( $_ =~ /\torg\.netatalk\.Metadata[\n\t]/ ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } else {
         return 0;
@@ -893,25 +890,24 @@ sub checkea {
 
 sub eaopenfile {
     my ($file) = @_;
-
-    $file =~ s/\\/\\\\/g;
-    $file =~ s/\"/\\\"/g;
-    $file =~ s/\$/\\\$/g;
-    $file =~ s/\`/\\\`/g;
-    ($eatempfh, $eatempfile) = tempfile(UNLINK => 1);
+    my @eacommands = ();
 
     if ( $eacommand == 1 ) {
-        system("getfattr --only-values -n user.org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('getfattr', '--only-values', '-n', 'user.org.netatalk.Metadata', $file);
     } elsif ( $eacommand == 2 ) {
-        system("attr -q -g org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('attr', '-q', '-g', 'org.netatalk.Metadata', $file);
     } elsif ( $eacommand == 3 ) {
-        system("runat \"$file\" cat org.netatalk.Metadata > $eatempfile");
+        @eacommands = ('runat', $file, 'cat', 'org.netatalk.Metadata',);
     } elsif ( $eacommand == 4 ) {
-        system("getextattr -q user org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('getextattr', '-q', 'user', 'org.netatalk.Metadata', $file);
     } else {
         return "";
     }
 
+    my ($eatempfh, $eatempfile) = tempfile(UNLINK => 1);
+    open2(my $ealist, my $eain, @eacommands) or die $@;
+    print $eatempfh $_ while(<$ealist>);
+    close($ealist, $eain);
     close($eatempfh);
     return $eatempfile;
 }
index 4b88ba9d39f6ac37bc4195ff89f0b9459fcb951a..79dcfb3f283179abfcfb342ff095881577113c18 100644 (file)
@@ -13,6 +13,7 @@ pkgconfdir = @PKGCONFDIR@
            -e s@:SBINDIR:@${sbindir}@ \
            -e s@:ETCDIR:@${pkgconfdir}@ \
            -e s@:NETATALK_VERSION:@${NETATALK_VERSION}@ \
+           -e s@:PATH_NETATALK_LOCK:@${PATH_NETATALK_LOCK}@ \
            <$< >$@
 
 GENERATED_FILES = \
@@ -39,6 +40,7 @@ TEMPLATES = \
 
 CLEANFILES = $(GENERATED_FILES) $(sysv_SCRIPTS) $(service_DATA) afpd cnid_metad
 EXTRA_DIST = $(TEMPLATES)
+noinst_DATA = $(GENERATED_FILES)
 
 # overwrite automake uninstall
 # not beautiful, but this way we can call the OS specific init script
@@ -52,7 +54,7 @@ uninstall: uninstall-startup
 
 if USE_REDHAT_SYSV
 
-sysvdir        = /etc/rc.d/init.d
+sysvdir        = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.redhat
@@ -74,7 +76,7 @@ endif
 
 if USE_SYSTEMD
 
-servicedir     = /lib/systemd/system
+servicedir     = $(INIT_DIR)
 service_DATA   = netatalk.service
 
 netatalk.service: service.systemd
@@ -96,7 +98,7 @@ endif
 
 if USE_SUSE_SYSV
 
-sysvdir        = /etc/init.d
+sysvdir        = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.suse
@@ -118,7 +120,7 @@ endif
 
 if USE_NETBSD
 
-sysvdir = /etc/rc.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 netatalk: rc.netbsd
@@ -139,7 +141,7 @@ endif
 
 if USE_SOLARIS
 
-servicedir = /lib/svc/manifest/network/
+servicedir = $(INIT_DIR)
 service_DATA = netatalk.xml
 
 install-data-hook:
@@ -156,7 +158,7 @@ endif
 
 if USE_GENTOO
 
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.gentoo
@@ -178,7 +180,7 @@ endif
 
 if USE_DEBIAN
 
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.debian
index a0eb758f16374cc4db896637dd175d5fe7f5de96..9b26b61a5ec8c72c11e190fa2933324227adee74 100644 (file)
@@ -5,7 +5,7 @@
 # Should-Start:      avahi-daemon
 # Required-Stop:     $remote_fs $syslog
 # Default-Start:     2 3 4 5
-# Default-Stop:      1
+# Default-Stop:      0 1 6
 ### END INIT INFO
 #
 # netatalk      Netatalk :NETATALK_VERSION: initscript
index 1f84c4398fa761bbd72d0ff71df7773dbf9959e4..af5602a581fc4dfe3e272718dc06e44225be1af7 100644 (file)
@@ -13,7 +13,7 @@ rcvar=$name
 command=":SBINDIR:/netatalk"
 etcdir=":ETCDIR:"
 pidfile="/var/run/${name}.pid"
-required_files="$etcdir/afp.conf
+required_files="$etcdir/afp.conf"
 
 load_rc_config $name
 run_rc_command "$1"
index 643448ae688869d8cb44b0b1f6fb59e708fc5881..0721d9f64a9c2c9e8e113c53c6633066c08f1cab 100644 (file)
@@ -21,7 +21,7 @@ RETVAL=1
 netatalk_startup() {
     # Check that networking is up.
     if [ ${NETWORKING} = "no" ]; then
-        echo "[Network isn't started]"; 
+        echo "[Network isn't started]";
         exit 1;
     fi
 
@@ -51,11 +51,17 @@ netatalk_stop() {
     fi
 }
 
-# code to cause apfd and cnid_metad to restart
-netatalk_graceful() {
+# restart code
+netatalk_restart() {
+    netatalk_stop
+    netatalk_startup
+}
+
+# reload config files
+netatalk_reload() {
     if [ -x ${NETATALK_SBIN}/netatalk ]; then
-        echo -n $"Restarting cnid_metad and afpd: "
-        killproc  netatalk -QUIT
+        echo -n $"Reloading $prog: "
+        killproc netatalk -HUP
         RETVAL=$?
         echo
     fi
@@ -68,20 +74,21 @@ case "$1" in
     'stop')
         netatalk_stop
         ;;
-    'restart'|'reload')
-        $0 stop
-        $0 start
-        RETVAL=$?
+    'restart')
+        netatalk_restart
+        ;;
+    'reload'|'graceful')
+        netatalk_reload
         ;;
     'status')
         status netatalk
         RETVAL=$?
         ;;
-    'graceful')
-        netatalk_graceful
+    'condrestart')
+        [ -f /var/lock/subsys/netatalk ] && netatalk_restart || :
         ;;
     *)
-        echo "Usage: $0 {start|stop|restart|reload|status|graceful}"
+        echo "Usage: $0 {start|stop|restart|reload|status|condrestart}"
         exit 2
 esac
 
index 2183f3036f1a4e5b0e054ec5d83d6bbdd9be233c..cd17076a0f1908c5ea5a23ca65890b93e5c51ce4 100644 (file)
@@ -2,12 +2,16 @@
 
 [Unit]
 Description=Netatalk AFP fileserver for Macintosh clients
+Documentation=man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)
+Documentation=http://netatalk.sourceforge.net/
 After=syslog.target network.target avahi-daemon.service
 
 [Service]
 Type=forking
 GuessMainPID=no
-ExecStart=:SBINDIR:/netatalk
+ExecStart=/bin/sh -c :SBINDIR:/netatalk
+PIDFile=:PATH_NETATALK_LOCK:
+ExecReload=/bin/kill -HUP $MAINPID
 Restart=always
 RestartSec=1
 
index ade949aa9484fb06c410eaf6ec495e6885e769df..c8189ad19eff21550c168c583c8fda78a0752d30 100644 (file)
@@ -1,4 +1,4 @@
 Makefile.in
 Makefile
-.gitignore
-*.o
+html.xsl
+man.xsl
\ No newline at end of file
index 4a1e7de1ce6ac562ed49d931f4394702208c8c31..d0f8b5b58eb4dd51902d3d7d6d48ab1b7e6e933e 100644 (file)
@@ -200,9 +200,9 @@ The ini parser is taken from <http://ndevilla.free.fr/iniparser/>.
 It has been slightly modified:
 - case-sensitive
 - "include" directive added
-- iniparser_getstrdup() to complemnt iniparser_getstring(), it return allocated
+- atalk_iniparser_getstrdup() to complemnt atalk_iniparser_getstring(), it return allocated
   strings which the caller must free as necessary
-- the API has been modifed such that all iniparser_get* funcs take a section and
+- the API has been modifed such that all atalk_iniparser_get* funcs take a section and
   a parameter as sepereta args instead of one string of the form "section:parameter"
   in the original library
 
@@ -288,3 +288,54 @@ filenames for the usock_file parameter.
 
 - There is no protection against a malicious user connecting to the
 cnid_dbd socket and changing the database.
+
+Documentation
+=============
+Netatalk documentation is in Docbook XML format. In order to build manpages
+and the html documentation from the Docbook docs you need the following:
+
+1. Install `xsltproc`
+
+2. Get the latest Docbook XSL stylesheet distribution from:
+   https://sourceforge.net/project/showfiles.php?group_id=21935
+   Tested docbook-xsl stylesheet version is 1.75.2.
+
+3. Fix indexterm bug in xsl stylesheet:
+   inside the xsl stylesheet distribution in manpages/inline.xsl remove these lines:
+
+        <!-- * indexterm instances produce groff comments like this: -->
+        <!-- * .\" primary: secondary: tertiary -->
+        <xsl:template match="indexterm">
+          <xsl:text>.\" </xsl:text>
+          <xsl:apply-templates/>
+          <xsl:text>&#10;</xsl:text>
+        </xsl:template>
+
+        <xsl:template match="primary">
+          <xsl:value-of select="normalize-space(.)"/>
+        </xsl:template>
+
+        <xsl:template match="secondary|tertiary">
+          <xsl:text>: </xsl:text>
+          <xsl:value-of select="normalize-space(.)"/>
+        </xsl:template>
+
+4. Add the following argument to configure
+   --with-docbook=PATH_TO_XML_STYLESHEET_DIR
+
+5. The manpages and html documentation are now automatically built when running `make html`.
+
+Editing Docbook Sources
+-----------------------
+Free WYSIWYG editor with only one minor drawback is XMLEditor from XMLmind:
+http://www.xmlmind.com/xmleditor/persoedition.html
+
+Drawback: in order to be able to edit any of the  nested xml files, you have to
+"promote" them to valid Docbook files by referencing the Docbook DTD, insert as line 2+3:
+       
+       <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+       "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+These changes will however prevent XMLeditor from opening the master xml file
+manual.xml. Before further processing can be done these changes then have to be
+reverted for any changed file.
index a6fcda87c26eefbfa516d8a595f87943f11a3cb7..f8ccbb76f3e30a272a7977935dfeece14958f2f6 100644 (file)
@@ -1,3 +1,9 @@
-# Makefile.am for INSTALL/
+SUBDIRS = manpages manual
 
-EXTRA_DIST = DEVELOPER
+DISTCLEANFILES = html.xsl man.xsl
+
+release-notes: www/ReleaseNotes
+       cd www && ./create-relnotes.sh
+
+upload-release-notes: release-notes
+       scp www/ReleaseNotes.html $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.1/ReleaseNotes$(VERSION).html
diff --git a/doc/gfx_and_css/logo.ai b/doc/gfx_and_css/logo.ai
new file mode 100644 (file)
index 0000000..0534656
--- /dev/null
@@ -0,0 +1,687 @@
+%PDF-1.5\r%âãÏÓ\r
+1 0 obj\r<</Metadata 458 0 R/Pages 2 0 R/OCProperties<</D<</RBGroups[]/ON[15 0 R 23 0 R 41 0 R 49 0 R 67 0 R 75 0 R 93 0 R 100 0 R 126 0 R 180 0 R 234 0 R 292 0 R 350 0 R 408 0 R]/OFF[119 0 R]/Order 407 0 R>>/OCGs[15 0 R 23 0 R 41 0 R 49 0 R 67 0 R 75 0 R 93 0 R 100 0 R 119 0 R 126 0 R 180 0 R 234 0 R 292 0 R 350 0 R 408 0 R]>>/Type/Catalog>>\rendobj\r458 0 obj\r<</Subtype/XML/Length 25612/Type/Metadata>>stream\r
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.277092, Fri Feb 23 2007 14:16:18        ">
+   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+      <rdf:Description rdf:about=""
+            xmlns:dc="http://purl.org/dc/elements/1.1/">
+         <dc:format>application/pdf</dc:format>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xap="http://ns.adobe.com/xap/1.0/"
+            xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/">
+         <xap:CreatorTool>Adobe Illustrator CS3</xap:CreatorTool>
+         <xap:CreateDate>2009-06-03T14:56:54-07:00</xap:CreateDate>
+         <xap:ModifyDate>2009-06-04T10:49:53-07:00</xap:ModifyDate>
+         <xap:MetadataDate>2009-06-04T10:49:53-07:00</xap:MetadataDate>
+         <xap:Thumbnails>
+            <rdf:Alt>
+               <rdf:li rdf:parseType="Resource">
+                  <xapGImg:width>256</xapGImg:width>
+                  <xapGImg:height>256</xapGImg:height>
+                  <xapGImg:format>JPEG</xapGImg:format>
+                  <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW&#xA;du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT&#xA;l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj&#xA;PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9&#xA;Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A&#xA;VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ&#xA;IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8&#xA;Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man&#xA;9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd&#xA;II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8&#xA;X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l&#xA;jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM&#xA;VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN&#xA;ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj&#xA;F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk&#xA;Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9&#xA;/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8&#xA;1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd&#xA;WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG&#xA;D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui&#xA;rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+&#xA;bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3&#xA;Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd&#xA;YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8&#xA;TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P&#xA;gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA&#xA;dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu&#xA;w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK&#xA;s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma&#xA;hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t&#xA;dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q&#xA;7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz&#xA;fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx&#xA;aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz&#xA;KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr&#xA;G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5&#xA;5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu&#xA;DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE&#xA;dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0&#xA;b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG&#xA;syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW&#xA;8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M&#xA;aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz&#xA;DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj&#xA;irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx&#xA;V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF&#xA;PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE&#xA;v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI&#xA;qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2&#xA;KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ&#xA;vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp&#xA;xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C&#xA;81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5&#xA;Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd&#xA;+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/&#xA;ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX&#xA;UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/&#xA;AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k&#xA;9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx&#xA;Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4&#xA;o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa&#xA;KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ&#xA;/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW&#xA;o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf&#xA;lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1&#xA;Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na&#xA;SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc&#xA;Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0&#xA;4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg&#xA;k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr&#xA;+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD&#xA;6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM&#xA;nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn&#xA;v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu&#xA;/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL&#xA;79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB&#xA;1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk&#xA;rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw&#xA;zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf&#xA;/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt&#xA;iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+&#xA;r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb&#xA;Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L&#xA;dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te&#xA;SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1&#xA;Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/&#xA;AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8&#xA;/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o&#xA;pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW&#xA;+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2&#xA;5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s&#xA;8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC&#xA;ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/&#xA;8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p&#xA;8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq&#xA;wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A&#xA;Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu&#xA;+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA&#xA;8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h&#xA;f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7&#xA;uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK&#xA;t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n&#xA;cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2&#xA;7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX&#xA;2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX&#xA;u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z&#xA;9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc&#xA;8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba&#xA;qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69&#xA;piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH&#xA;/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq&#xA;ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK&#xA;se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/&#xA;1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A&#xA;kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd&#xA;5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW&#xA;H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2&#xA;vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd&#xA;5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn&#xA;/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV&#xA;fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5&#xA;x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13&#xA;y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A&#xA;bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/&#xA;/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4&#xA;NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x&#xA;i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM&#xA;AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY&#xA;q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp&#xA;flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L&#xA;wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+&#xA;RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP&#xA;OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV&#xA;fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q&#xA;8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT&#xA;W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE&#xA;tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo&#xA;6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+&#xA;Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A&#xA;yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5&#xA;OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn&#xA;/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6&#xA;ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5&#xA;f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+&#xA;f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7&#xA;A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY&#xA;UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q&#xA;pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp&#xA;HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf&#xA;9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv&#xA;y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380&#xA;tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl&#xA;djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK&#xA;on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr&#xA;9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/&#xA;HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn&#xA;fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV&#xA;2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8&#xA;66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A&#xA;oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3&#xA;/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj&#xA;8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7&#xA;UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm&#xA;Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h&#xA;wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV&#xA;REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/&#xA;8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl&#xA;zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH&#xA;NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/&#xA;AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq&#xA;2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0&#xA;r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr&#xA;Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/&#xA;AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd&#xA;kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN&#xA;/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/&#xA;AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q&#xA;ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl&#xA;pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx&#xA;+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5&#xA;oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl&#xA;pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+&#xA;kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8&#xA;j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM&#xA;akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI&#xA;rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib&#xA;r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr&#xA;p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE&#xA;v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d&#xA;FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN&#xA;Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV&#xA;dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0&#xA;NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a&#xA;1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA&#xA;6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+&#xA;fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8&#xA;Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N&#xA;j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q&#xA;4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF&#xA;7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu&#xA;IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/&#xA;zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK&#xA;u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/&#xA;AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3&#xA;/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE&#xA;gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+&#xA;g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m&#xA;aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN&#xA;jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX&#xA;Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81&#xA;a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W&#xA;O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ&#xA;hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV&#xA;vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS&#xA;7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj&#xA;odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN&#xA;B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH&#xA;cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK&#xA;UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj&#xA;/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f&#xA;aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9&#xA;WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L&#xA;Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB&#xA;V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G&#xA;1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7&#xA;+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/&#xA;u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq&#xA;+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq&#xA;eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+&#xA;cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G&#xA;Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz&#xA;svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35&#xA;V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv&#xA;JvlTyvam18vaTa6XCQA/1aJUZ6dDI4HNz7sTiqc4q7FXYq7FXYq7FXYq7FXYq7FX/9k=</xapGImg:image>
+               </rdf:li>
+            </rdf:Alt>
+         </xap:Thumbnails>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/">
+         <xapMM:DocumentID>uuid:1B95FEC551E111DEA13AAE68EE5F3A4D</xapMM:DocumentID>
+         <xapMM:InstanceID>uuid:58ad672b-c584-4acf-894c-9cb9832873ef</xapMM:InstanceID>
+         <xapMM:DerivedFrom rdf:parseType="Resource"/>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+            xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+            xmlns:xapG="http://ns.adobe.com/xap/1.0/g/">
+         <xapTPg:NPages>1</xapTPg:NPages>
+         <xapTPg:HasVisibleTransparency>True</xapTPg:HasVisibleTransparency>
+         <xapTPg:HasVisibleOverprint>False</xapTPg:HasVisibleOverprint>
+         <xapTPg:MaxPageSize rdf:parseType="Resource">
+            <stDim:w>3.000000</stDim:w>
+            <stDim:h>3.000000</stDim:h>
+            <stDim:unit>Inches</stDim:unit>
+         </xapTPg:MaxPageSize>
+         <xapTPg:PlateNames>
+            <rdf:Seq>
+               <rdf:li>Black</rdf:li>
+            </rdf:Seq>
+         </xapTPg:PlateNames>
+         <xapTPg:SwatchGroups>
+            <rdf:Seq>
+               <rdf:li rdf:parseType="Resource">
+                  <xapG:groupName>Default Swatch Group</xapG:groupName>
+                  <xapG:groupType>0</xapG:groupType>
+               </rdf:li>
+            </rdf:Seq>
+         </xapTPg:SwatchGroups>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
+         <illustrator:Type>Document</illustrator:Type>
+      </rdf:Description>
+   </rdf:RDF>
+</x:xmpmeta>
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                           
+<?xpacket end="w"?>\rendstream\rendobj\r2 0 obj\r<</Count 1/Type/Pages/Kids[5 0 R]>>\rendobj\r15 0 obj\r<</Intent 16 0 R/Usage 17 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r23 0 obj\r<</Intent 24 0 R/Usage 25 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r41 0 obj\r<</Intent 42 0 R/Usage 43 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r49 0 obj\r<</Intent 50 0 R/Usage 51 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r67 0 obj\r<</Intent 68 0 R/Usage 69 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r75 0 obj\r<</Intent 76 0 R/Usage 77 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r93 0 obj\r<</Intent 94 0 R/Usage 95 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r100 0 obj\r<</Intent 101 0 R/Usage 102 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r119 0 obj\r<</Intent 120 0 R/Usage 121 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r126 0 obj\r<</Intent 127 0 R/Usage 128 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r180 0 obj\r<</Intent 181 0 R/Usage 182 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r234 0 obj\r<</Intent 235 0 R/Usage 236 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r292 0 obj\r<</Intent 293 0 R/Usage 294 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r350 0 obj\r<</Intent 351 0 R/Usage 352 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r408 0 obj\r<</Intent 409 0 R/Usage 410 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r409 0 obj\r[/View/Design]\rendobj\r410 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r351 0 obj\r[/View/Design]\rendobj\r352 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r293 0 obj\r[/View/Design]\rendobj\r294 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r235 0 obj\r[/View/Design]\rendobj\r236 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r181 0 obj\r[/View/Design]\rendobj\r182 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r127 0 obj\r[/View/Design]\rendobj\r128 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r120 0 obj\r[/View/Design]\rendobj\r121 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r101 0 obj\r[/View/Design]\rendobj\r102 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r94 0 obj\r[/View/Design]\rendobj\r95 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r76 0 obj\r[/View/Design]\rendobj\r77 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r68 0 obj\r[/View/Design]\rendobj\r69 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r50 0 obj\r[/View/Design]\rendobj\r51 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r42 0 obj\r[/View/Design]\rendobj\r43 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r24 0 obj\r[/View/Design]\rendobj\r25 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r16 0 obj\r[/View/Design]\rendobj\r17 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r407 0 obj\r[408 0 R]\rendobj\r5 0 obj\r<</Parent 2 0 R/Contents 452 0 R/BleedBox[0.0 0.0 216.0 216.0]/PieceInfo<</Illustrator 401 0 R>>/ArtBox[16.0 18.25 200.0 202.25]/Group 453 0 R/MediaBox[0.0 0.0 216.0 216.0]/Thumb 457 0 R/TrimBox[0.0 0.0 216.0 216.0]/Resources<</XObject<</Fm0 425 0 R/Fm1 431 0 R/Fm2 441 0 R/Fm3 447 0 R>>/Properties<</MC0 408 0 R>>/ExtGState<</GS0 416 0 R/GS1 448 0 R>>>>/Type/Page/LastModified(D:20090604104953-07'00')>>\rendobj\r452 0 obj\r<</Length 1187/Filter/FlateDecode>>stream\r
+H\89\94WÝnå4\10¾ÏSø\ 5âzl\8f=¾¥\v\­Ð\8a\v\1e bAb\vê®\84ÄÛóÍ8q\9c\9cs\8aPÕ&ãùûæÏ\99>ýôì\9e>>\a÷Ý\87g·¼-ÁE*ö»ê\9f¯¿.¿¸?qj?¾&v\7f,O?þ\1cÜoß\967GvN\8e\828jâ%\ 5r/¯&ÿº¬Y|\8b\8ax®Í­©ùÂrÐú\14÷²\8c\ 3JÕ3\8cìº+Õê\89\9b\1aÙ__\96+óª}5\7fq?péÏËòyù´EH\88n\8aJ\92¯9EW¢\97*m\8a\8c|FÈÙÇ\88D\15H\15\10Ì\8aÀ\88â\93d\8d\8b\82o\92\1c\15Ï\ 5\0\1aÎ\9bK    T\85pó"Ñåâc5áê3Þ\98\ 4\83\81\9dàoPäÙ×\96\1dÁ^Ð\f¬)xÎÙ  \ 2VvD\1eØU\81\ e\fçäIª+ð\9a\1cpP\\8d¾Uä\8d\9b' ¬ìK\89\b\0F²Ó\æ¨Ò %²\1eD¸X\8bx
\8f0ªd*¢dJ¤â5ø\92\93\1e¨mõA¦,\ 5ÊðAµ\93Ü¥\11d\9aøÍ×Pwe!¤\8b\12\81¡\fß 9æ\81\fd\97Þ\80\93h\9h~Cð\19Y\ 3@M\1ehønÉ\95ìS5ó\14\918Õè5\     %\89\88¥©¢ÑÙ7h\10!*\1c¨
+\a3\ 2ßÅzPK\ 3\15Î\e\1d³ñ3\ f\8dTÌh\ eæ\16´\18Mb\12fAPw\19¨2Ù\ 1Ê\974\f4H«\11ó\17t.     í\9b0Cè¾M\1a)\14ív$Öø=j ñ\82aøg±£Xy\1c}±Æ\f5Ï'hF.ÓAïñµ7¹öýßÓ¤\84Ó¤DÌ\15\1aºa\ 6ÐìcL\82KäÑH_\96\82«!\r\8aì\89n?\88ÁB\a\92A_1ITíµ\987ÊãU\ f\7f7t\a&F/°ÀMds>PaD\13ra6û+&\98J\ 1\ro9Þ\90È\88\82\a\a²\1d °\9a³+ÝcI¸EøBì\81\86ãõ\1a\b\ 6\8a9ßË."\v¢Y@\91*\9aáJ\8f,¢y3_É!-z|¦î%\94P0°\1eÀHÝ­\96èJÏ~oÉ!\8d[¦Ü\90w\81dd\ 4ß\1c6ýùRÖ®\8a¸:Ìy\9cZ\ 3\83l\94>SÊ*\8d«\0Ú\98÷FZëlj\83ljk\97Fy¬\85fZÙì+wOjj\90é¬{\ 3\9f\93\ 1\7f<-ç\19èT9%íLÝMRÁ<Ãÿÿó\82ø5\14µ|ÔI\87»\1cdÖ0{/\18S\13\16ú k
+Ãa,m¥8\86ø\ 6%nh*!:»xc\9cË\89/ièÃ\16{A   ÃV¦\ 3-+ʦ\9fà\bWVU½\8b\9bCm¥ß\ eµ\f\ 2íÂCÔªF5M4:DW\bý.ÕÞ¼l\1f\91qp1ÐcÁm_ðE¦Ä&\86OKl\98<\·¢ÛÓç¥\81Ñd\bÀJÁÇt\12Ð\1ch\15\87\ 5\9fÿ\93\84í\ 2C\800Ýõ,P4{\87\ 4\93Ä\9bnm¤[ÛÓ\ f¯Á}øk\86\8eU\ 2yz\az\17x\ fú&ñ\18ú&ð\ eôMâ}èÔ¡SCw 
+X\8fZ@\11±\ 6\85\98Ô\7f¬u\ 3\8f¯"°l\12
+\9dÏ|Dß\ 6¿\ 3?ñ     K\1aíü\8e\vv¹¶³;è\99}B\1dϨ±]\90\1f£Þ$\1e¢Þø\ fQoüG¨7ö\7f N\1dõÝÕØ\16~`ÑåiZø±±´¾ðc0\81cÅB^ëAÚÓVµýDëÞ°}îº+a¿¬1Ù¿½báWf<N®Ú7öÏþ7õ}áWK\9bä«\11:7\84\r\v«\9d\90ý_Q\8c\e\8e×¾DËÄ\gÍu²©\8b((Ýò\19Ï®V}ÁÒ\16¶ç˲¹êä$~²£\17Ì÷\1fñ\8fÙ§å_\ 1\ 6\0\1dï§\\rendstream\rendobj\r453 0 obj\r<</I false/K false/CS/DeviceCMYK/S/Transparency>>\rendobj\r457 0 obj\r<</Length 209/Filter[/ASCII85Decode/FlateDecode]/BitsPerComponent 8/ColorSpace 455 0 R/Width 27/Height 27>>stream\r
+8;Wj7_%"=*$tC/]^3R!p2\5ei:.#<:*2umX&QHWQSX?kZ@O:XR!2NU",:A&.IZ])d
+4sFGTN1#]GN(;N>&A>I\_UN-X/[rs;\^P(tkhZ2Z7Hcmh,559!+mO$N-ilCO;X7ru
+%4su8Hf\-trRT+fp'uERM?YVbeK1tUK;H$F2;_7f40XmgrX\d.MSJ41ioUrN`kD>a
+XoSNdk^<4~>\rendstream\rendobj\r416 0 obj\r<</OPM 1/BM/Normal/CA 1.0/OP false/SMask/None/ca 1.0/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r448 0 obj\r<</OPM 1/BM/Normal/CA 0.5/OP false/SMask/None/ca 0.5/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r425 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 414 0 R/Resources<</Shading<</Sh0 422 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[106.997 139.145 150.43 135.867]>>stream\r
+q
+150.43 135.867 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 137.5058594 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r431 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 428 0 R/Resources<</Shading<</Sh0 422 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[106.997 133.854 150.43 130.576]>>stream\r
+q
+150.43 130.576 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 132.2148438 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r441 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 434 0 R/Resources<</Shading<</Sh0 437 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[39.6411 84.9033 88.6143 81.626]>>stream\r
+q
+39.641 84.903 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 83.2646484 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r447 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 444 0 R/Resources<</Shading<</Sh0 437 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[39.6411 90.1943 88.6143 86.917]>>stream\r
+q
+39.641 90.194 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 88.5556641 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r444 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r418 0 obj\r[/DeviceN[/Black]/DeviceCMYK 419 0 R 420 0 R]\rendobj\r419 0 obj\r<</Length 91/FunctionType 4/Filter/FlateDecode/Domain[0.0 1.0]/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>stream\r
+H\89ª6Ô3\0\ 3\ 5#\ 5C\85¢ü\9c\1c\ 5bD\f\142óRR+\102\ÉeE
\15É\19
+Å¥I\b
+ºèf (4\ 5\ 2Ë\9b\10Pi\ 2\18S!ªåÆ0\95F\ 4\8c4\82[^\90_ P\v\10`\0.§G±\rendstream\rendobj\r420 0 obj\r<</Subtype/NChannel/Process 421 0 R>>\rendobj\r421 0 obj\r<</Components[/Cyan/Magenta/Yellow/Black]/ColorSpace/DeviceCMYK>>\rendobj\r437 0 obj\r<</ColorSpace 418 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 438 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r438 0 obj\r<</FunctionType 3/Encode[1.0 0.0 0.0 1.0]/Domain[0.0 1.0]/Functions[439 0 R 440 0 R]/Bounds[0.967026]>>\rendobj\r439 0 obj\r<</C0[0.735]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r440 0 obj\r<</C0[0.735]/C1[0.735]/FunctionType 2/N 1.0/Domain[0.0 1.0]>>\rendobj\r434 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r428 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r422 0 obj\r<</ColorSpace 418 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 423 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r423 0 obj\r<</FunctionType 3/Encode[1.0 0.0]/Domain[0.0 1.0]/Functions[424 0 R]/Bounds[]>>\rendobj\r424 0 obj\r<</C0[1.0]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r414 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r455 0 obj\r[/Indexed/DeviceRGB 255 456 0 R]\rendobj\r456 0 obj\r<</Length 428/Filter[/ASCII85Decode/FlateDecode]>>stream\r
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
+VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
+PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>\rendstream\rendobj\r401 0 obj\r<</Private 402 0 R/LastModified(D:20090604104953-07'00')>>\rendobj\r402 0 obj\r<</RoundtripVersion 13/ContainerVersion 11/CreatorVersion 13/AIMetaData 403 0 R/AIPrivateData1 404 0 R/AIPrivateData2 405 0 R/NumBlock 2/RoundtripStreamType 1>>\rendobj\r403 0 obj\r<</Length 860>>stream\r
+%!PS-Adobe-3.0 \r%%Creator: Adobe Illustrator(R) 13.0\r%%AI8_CreatorVersion: 13.0.2\r%%For: (admin) ()\r%%Title: (Netatalk_Logo.ai)\r%%CreationDate: 6/4/09 10:49 AM\r%%BoundingBox: 1168 738 1352 923\r%%HiResBoundingBox: 1168 738.25 1352 922.25\r%%DocumentProcessColors: Black\r%AI5_FileFormat 9.0\r%AI12_BuildNumber: 434\r%AI3_ColorUsage: Color\r%AI7_ImageSettings: 0\r%%CMYKProcessColor: 1 1 1 1 ([Registration])\r%AI3_TemplateBox: 1260.5 827.5 1260.5 827.5\r%AI3_TileBox: 972 472 1548 1206\r%AI3_DocumentPreview: None\r%AI5_ArtSize: 216 216\r%AI5_RulerUnits: 0\r%AI9_ColorModel: 2\r%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0\r%AI5_TargetResolution: 800\r%AI5_NumLayers: 1\r%AI9_OpenToView: 1107 936 4.86 1509 1080 26 1 0 50 75 0 0 1 0 1 0 1\r%AI5_OpenViewLayers: 7\r%%PageOrigin:0 0\r%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9\r%AI9_Flatten: 1\r%AI12_CMSettings: 00.6\r%%EndComments\r\rendstream\rendobj\r404 0 obj\r<</Length 16856>>stream\r
+%%BoundingBox: 1168 738 1352 923\r%%HiResBoundingBox: 1168 738.25 1352 922.25\r%AI7_Thumbnail: 128 128 8\r%%BeginData: 16714 Hex Bytes\r%0000330000660000990000CC0033000033330033660033990033CC0033FF\r%0066000066330066660066990066CC0066FF009900009933009966009999\r%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66\r%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333\r%3333663333993333CC3333FF3366003366333366663366993366CC3366FF\r%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99\r%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033\r%6600666600996600CC6600FF6633006633336633666633996633CC6633FF\r%6666006666336666666666996666CC6666FF669900669933669966669999\r%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33\r%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF\r%9933009933339933669933999933CC9933FF996600996633996666996699\r%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33\r%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF\r%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399\r%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933\r%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF\r%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC\r%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699\r%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33\r%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100\r%000011111111220000002200000022222222440000004400000044444444\r%550000005500000055555555770000007700000077777777880000008800\r%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB\r%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF\r%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF\r%524C45FD35FFA87D52522727FD09F8272752527D7DA8A8FD64FFA87D2727\r%FD19F8527DA8A8FD5CFF7D52FD21F827277DA8FD56FF7D52FD28F8277DA8\r%FD50FFA852FD2DF82752A8FD4CFF7D27FD14F827F827F8272727F827F827\r%FD13F8277DFD48FF7D27FD0FF8FD04275227522752275227522752275227\r%5227522727F827FD0FF87DFD44FF7DFD0FF8FD042752FD132752FD0627FD\r%0DF8277DFD40FFA827FD0BF8272752275227522752275227522752275227\r%522752275227522752275227522752275227522727F827FD0AF827A8FD3D\r%FF52FD0CF827275227272752272727522727275227272752272727522727\r%2752272727522727275227272752FD0427FD0BF852A8FD39FF7DFD0BF827\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522727FD09F8277DFD36FFA852FD0A\r%F82752FD2F27522727FD0AF827FD34FF7D27FD09F8272752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522727FD09F8A8FD31FF52FD0AF852\r%272727522727275227272752272727522727275227272752272727522727\r%27522727275227272752272727522727275227272752272727522727FD09\r%F87DFD2FFF27FD08F8272752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752FD09F852FD2DFF27FD08F8272752FD3E27FD\r%09F827A8FD2AFFFD09F85227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522727FD07F827A8FD27FFA8FD09\r%F85227272752272727522727275227272752272727522727275227272752\r%272727522727275227272752272727522727275227272752272727522727\r%275227272752272727522727FD08F8A8FD25FFA8FD07F827275227522752\r%275227272752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%2752275227522727FD08F8A8FD23FFA8FD08F82752FD0727F8F8FD4127FD\r%08F8A8FD22FFFD07F82727522752275227522727F8272752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752275227522752275227522752272727522752275227522752FD07\r%F827A8FD20FFFD07F8FD0427522727275227F8F8F8272727522727275227\r%272752272727522727275227272752272727522727275227272752272727\r%5227272752272727522727275227272752F8F8F8522727275227272752FD\r%07F827A8FD1EFF27FD06F82727522752275227522727F8F8F85227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752275227522752275227522752275227522727F8F8F85227522752\r%27522752FD07F827FD1DFF27FD06F8FD08275227FD05F8FD3927FD04F8FD\r%082752FD07F852FD1BFF7DFD06F82727522752275227522727FD05F85227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522752FD05F82727\r%52275227522752FD07F87DFD19FF7DFD07F8275227272752FD0427FD05F8\r%272752272727522727275227272752272727522727275227272752272727\r%52272727522727275227272752272727522727275227272752FD0427FD05\r%F8272752272727522727FD07F8A8FD18FF27FD05F8272752275227522752\r%2727FD06F827522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752FD07F8272752275227522727FD07F8FD17FF52FD06F82752FD0827\r%FD07F8FD3B27FD07F8FD0927FD06F827FD15FFA8FD06F827522752275227\r%522752FD07F8272752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227FD08F8522752275227522727FD06F87DFD14FFFD07F82727\r%52272727522727FD09F85227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%272752272727522727FD09F85227272752FD0427FD06F8A8FD12FF52FD06\r%F852275227522752275227FD08F827275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522752275227FD0AF8522752275227522727FD05F852\r%FD11FF7DFD06F8FD0B27FD09F8FD3B27FD0BF852FD0627FD07F8A8FD10FF\r%27FD05F82727522752275227522727FD09F8272752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227FD0AF8272752275227522752\r%27FD05F827FD0FFF7DFD06F827272752272727522727FD0BF82727522727\r%275227272752272727522727275227272752272727522727275227272752\r%2727275227272752272727522727275227272752FD0427FD0BF8FD042752\r%FD0427FD06F87DFD0EFF27FD05F82752275227522752275227FD0AF82727\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522752275227FD0C\r%F8522752275227522727FD05F827FD0DFF52FD06F8FD0B27FD0BF852FD3A\r%27FD0DF852FD0727FD06F87DFD0CFF27FD05F82727522752275227522752\r%FD0BF8272752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227FD0CF827275227522752275227FD05F827FD0BFF7DFD05F8FD0427\r%52272727522727FD0DF85227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%272752272727522727FD0DF8FD04275227272752FD06F87DFD0AFF27FD05\r%F8275227522752275227522727FD0BF82727522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227FD0EF8522752275227522727FD05\r%F827FD09FF7DFD06F8FD0B27FD0DF8FD3A27FD0FF8FD0A27FD05F8A8FD08\r%FF52FD05F8272752275227522752275227FD0CF827275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752FD0FF85227522752275227\r%5227FD05F852FD08FFFD06F82727275227272752FD0427FD0DF827275227\r%272752272727522727275227272752272727522727275227272752272727\r%5227272752272727522727275227272752272727522727FD10F827522727\r%275227272752FD06F8A8FD06FF7DFD06F85227272752272727522752FD0E\r%F8275227522727275227272752272727522727275227522752FD0B275227\r%522752275227522752275227522752275227522727FD0FF8272752275227\r%5227522727FD05F87DFD06FF52FD05F8FD05522752525227525227F827F8\r%27F827F827F827F827F827525227525252275252522752525227FD055227\r%522727FD07F827F827F8FD04275227272752FD0C27FD11F8FD0A27FD05F8\r%27FD06FFFD05F852FD29FFA87DFFFFFF7D7DFFFF27FF52A8527D527DFD0E\r%52275227522752FD0427FD11F82727522752275227522752FD05F827A8FD\r%04FF7DFD05F87DFD29FF7D7DFFFFFF7D52FFFF27FF7D7D52527D527DFD06\r%5227522752275252522752FD0627FD13F827275227272752FD0427FD05F8\r%7DFD04FF52FD05F8FD0D52FD1027FD0C522727F8272727F8272727F827F8\r%27F827F827F827F827F827F827F827F8F8F827275227522752275227FD12\r%F8272752275227522752275227FD05F852FD04FFFD05F827FD047D527D7D\r%7D52FD057DFD0F527D7D7D527D7D7D527D7D7D5252272752525227F87D52\r%2752FD0727F827F827F827F827F827F827F8FD0927FD13F852FD0A27FD05\r%F827FFFFFFA8FD05F87DFD2AFFA852FFFFFF7D7DFFFF27FF7DA8527D7D7D\r%527D527DFD0652275227522727275227522727F827F827FD0FF827275227\r%522752275227522727FD05F8A8FFFF7DFD05F87DA8FFA8A8A8FFA8A8A8FF\r%A8A8A8FFFD13A8FFA8FFFD05A8FF5252A8A8A85227FFA827A8527DFD0552\r%275227522752FD0827F827F8272727F827FD12F827522727275227272752\r%272727FD05F87DFFFF52FD04F827275227522752275227522752275227FD\r%12F827275227FD2AF827FD15F827275227522752275227522752FD05F852\r%FFFF27FD05F8FD0F27FD13F827FD42F8FD0D27FD06F8FFFFFD05F8272752\r%2752275227522752275227522727F8F8F827F8F8F827FD07F827FD07F827\r%FD08F8527D527D527D527D27FD06F827F8F8F827F8F8F827FD07F827F8F8\r%F827FD07F827FD08F82752275227522752275227522752FD05F827FFA8FD\r%06F852277DA8FFA8FF522727A8A8FFA8FF27F8F8FD08A8FF27F8FD0BA8FF\r%27FD06F8FD08FF52F8F8F8FD0CA852F8F827FFFD07A827F8F8F827FD05A8\r%7DFD05F8FD04A8FF7D2727FD04A8FF7D27FD05F8A87DFD05F82727277DFD\r%04FFA82752A8FD04FF52F827FD09FF52F8FD0CFF52FD05F87DFD08FF27F8\r%F827FD0CFF52F8F852FD08FF27F8F8F827FD05FF7DFD04F827FD06FF2752\r%FD05FF7D27FD05F87D52FD05F82727F87DFD05FF5227A8FD04FF52F8F8FD\r%09FF27F8A8FD0BFF27FD05F87DFD08FF27F8F8F8FD0CFF7DF8F852FD08FF\r%52F8F8F827A8FD04FF7DFD05F8FD05FF7D277DFD05FF2727FD05F85252FD\r%05F85227277DFD05FF7D27A8FD04FF522727FD09FF52F8FD0CFF7DFD04F8\r%27FD09FF27F8F827FD0CFF7DF8F8A8FD08FF7DF8F8F827FD05FF7DFD04F8\r%27FD05FFA827FD05FF7D272727FD04F85227FD04F8FD04277DFD05FFA827\r%A8FD04FF5227F8FD05FFA852527D27F8527D52A8FD05FF7D527D27FD04F8\r%52FD09FF27F8F8F852527DA8FD05FF7D7D5227F8F8A8FD08FFA8F8F8F827\r%FD05FF7DFD04F827FD05FF7D52FD05FF522752FD05F82727FD05F8522727\r%7DFD06FF52A8FD04FF522727A8FD04FF7DFD08F87DFD05FFFD08F87DFD09\r%FF27FD06F87DFD05FF27FD05F8FD09FFA8F8F8F827FD05FF7DFD04F852FD\r%05FFA87DFD05FF27522727FD04F827FD05F8FD04277DFD06FF52A8FD04FF\r%272727FD05FF7DFD08F8A8FD05FF27FD07F8FD05FF7DFFFFFFA827FD06F8\r%7DFD05FFFD05F827A8FFFFFFA8FD05FFF8F8F827FD05FF7DF8F8F82727FD\r%05FFA8A8FD04FF7D272727FD05F827FD06F85227277DFD06FFA8A8FD04FF\r%522752FD05FF7DFD08F8A8FD05FF27FD06F852FD04FFA8A8FD04FF27FD06\r%F8A8FD05FF27FD04F827FD05FFA8FD04FF52F8F827FD05FF7DF8F8272752\r%FD0BFF7D27522727FD0AF8272752277DFD06FFA8A8FD04FF522727FD05FF\r%A87D527DFD05F8A8FD05FF27FD06F87DFD04FF7D7DFD04FF27FD06F87DFD\r%05FF27FD04F852FD04FF7D7DFD04FF52F8F827FD05FF7DF8F8272727FD0A\r%FFA8FD0427FD0BF8275227277DFD0CFF522752FD09FF27FD04F87DFD05FF\r%27FD06F8FD05FF27A8FD04FFFD07F87DFD05FF27FD04F852FD04FF7D52FD\r%04FF7DF8F827FD05FF7DF8F8522752FD0AFF7D2727522727FD0AF8272727\r%F87DFD0CFF522727FD09FFFD05F8A8FD04FFA827FD05F827FD04FFA8F8A8\r%FFFFFFA8FD07F87DFD05FFFD05F87DFD04FF5252FD04FF7DF8F827A8FD04\r%FF7DF8FD0427FD0AFF7D27272752FD0BF8275227277DFD0CFF522752FD09\r%FF27FD04F87DFD05FF27FD05F87DFD04FF7DF8FD05FFFD07F87DFD05FF27\r%FD04F8A8FD04FF5227FD05FFF8F827FD05FF7DF827522752FD0AFFA85227\r%522727FD0AF8FD04277DFD0CFF522727FD05FFA8A87DA8FD05F8A8FD05FF\r%27FD05F8A8FD04FF2727FD04FFA8FD07F87DFD05FFFD05F8FD05FF2727FD\r%04FFA827F827FD05FF7DF8FD0427FD0BFF5227275227FD0AF8275227277D\r%FD0CFF522752FD05FFA8FD08F8A8FD05FF27FD04F827FD05FFA8A8FD05FF\r%FD07F87DFD05FF27F8F8F827FD05FF5227FD05FF27F827FD05FF7DF8F827\r%2752FD0BFF7D27522727FD0AF8FD04277DFD04FF7DFD07FF272727FD05FF\r%7DFD08F8A8FD05FF27FD04F852FD0BFFA8FD07F87DFD05FFFD04F827FD05\r%FFA8FD06FF52F827FD05FF7DF8F8F82727FD05FFA8FD05FFA8272727FD06\r%F827FD05F85227277DFD04FF7DA8FD06FF522752FD05FFA8FD08F8A8FD05\r%FF27FD04F8A8FD0CFFFD07F8A8FD05FF27F8F8F852FD0CFF52F827FD05FF\r%7DFD04F852FD05FFA8A8FD05FF52522727FD05F827FD04F8272752277DFD\r%04FF52A8FD06FF522727FD05FF7DFD08F8A8FD05FF27F8F8F827FD0CFFA8\r%FD07F87DFD05FF27F8F8F852FD0CFF7DF827FD05FF7DFD04F827FD05FF7D\r%7DFD05FF7D2727FD05F82752FD05F85227277DFD04FF5252FD06FF522727\r%FD06FF527D7D52FD04F87DFD05FF27F8F8F852FD05FF7D27A8FD04FFA8FD\r%07F87DFD05FF27F8F8F87DFD0CFFA8F8F8FD05FFA87D527DF827FD05FFA8\r%52FD05FF7D272727FD04F82727FD05F82727F87DFD04FF5227A8FD05FF52\r%27F8FD09FFA8FD04F8A8FD04FFA827F8F8F8A8FD05FF27F8A8FD04FFA8FD\r%07F87DFD05FFFD04F87DFD05FF5227A8FD05FFF827A8FFFFFFA8FD04FFF8\r%F8FD05FF7D52A8FD05FF2727FD05F8527DFD05F85227277DFD04FF7D27A8\r%FD05FF52F827FD09FFA8FD04F87DFD05FF27F8F827FD06FFF827A8FD04FF\r%A8FD07F87DFD05FF27F8F8F8FD06FF27F8A8FD05FF27F8FD09FFF8F8FD05\r%FFA827A8FD05FF7D27FD05F8527DFD05F82727277DFD04FF522752FD05FF\r%52F8F8FD09FF7DFD04F8A8FD05FF27F8F827FFA8FFA8FF7DF8F8FFFFFFA8\r%FF7DFD07F87DFD05FFFD04F8A8FD05FFF8F87DFD05FF2727FD08FFA8F8F8\r%FD05FFA8277DFD05FF7D27FD05F87DA8FD05F82727277DFD04FF7D2752FD\r%05FF52F8F8A8FD08FFA8FD04F87DFD05FF27FD04F827F827FD05F827F827\r%F827FD07F87DFD05FF27F8F827FD05FFA8F8F87DFD05FF52F8FD09FFF827\r%A8FD04FFA8277DFD05FFA827FD05F8A8A8FD06F852272727522752FD0527\r%52275227FD55F82752275227272752275227522727FD05F8FFFF27FD04F8\r%2727522752275227522752275227522727FD55F827275227272752272727\r%522727FD05F827FFFF52FD05F8FD04275227272752272727522727FD0DF8\r%27F8F8F827F8F8F827F8F8F827F8F8F827F827F82727F8F8272727F82727\r%27F8272727F8272727F8272727F8272727F8272727F8272727F8272727F8\r%272727F8272727F8272727F82727FD0C52FD05F827FFFF7DFD05F8275227\r%5227522752275227522752FD07275227522752275227FD08527D527D527D\r%5252527DA852FFFF52A8FFFFFF52A8FD38FFA8FD05F87DFFFFA8FD05F8FD\r%0E2752FD0B2752275227FD0C527D527D52FF52FFFF52A8FFFFFF27FD39FF\r%52FD05F8A8FFFFFF27FD05F8522752275227522752275227522727F827F8\r%27F827F827F827F827F827F827F827F827F827F827F827F827F8FD0527F8\r%FD3227FD04527D5252527D52525227FD05F8FD04FF27FD05F82752272727\r%5227272752FD0427F8F8F827F8F8F827F827F827F827F827F827F827F827\r%F827F827F827F827F852F852272727522752F82727522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752527D527D527D527D527D5252FD05F852FD04FF7DFD05F8272752\r%27522752275227522752FD05275227522752275227FD08527D527D527D52\r%FD057DA87DFFFF52A8FFFFFF52FD38FF7DFD05F87DFD04FFA8FD06F8FD0A\r%27522727F8FD0C275227522752275227FD08527D52A827FFFF527DFFA8FF\r%27A8FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF\r%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFF52FD05\r%F8A8FD05FF27FD05F827522752275227522752275227FD04F827F8F8F827\r%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F827F827F827\r%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827\r%F827F827F827F827F827F827F827F827F82727522752275227522752FD05\r%F852FD06FF7DFD05F827275227272752272727522727FD57F8FD0927FD06\r%F87DFD06FFA827FD05F8522752275227522752275227FD56F82727522752\r%2752275227FD06F8FD08FF27FD05F8FD0C27FD56F82752FD0827FD05F852\r%FD08FFA8FD05F827275227522752275227522727FD55F852275227522752\r%2752FD06F8A8FD09FF27FD05F82727522727275227272752FD56F8275227\r%272752272727FD05F827FD0AFF7DFD06F85227522752275227522727FD55\r%F8522752275227522727FD05F8A8FD0BFFFD06F8FD0727522727FD55F8FD\r%0927FD05F827A8FD0BFF7DFD05F82727522752275227FD58F82727522752\r%2752FD06F87DFD0DFF27FD05F82727522727FD5DF8272727FD07F8FD0EFF\r%7DFD06F8522752FD67F87DFD0FFF27FD06F827FD67F827FD10FFA8FD6EF8\r%A8FD11FF52FD6CF852FD12FFA827FD6BF8FD14FF7DFD6AF87DFD15FF52FD\r%68F852FD17FFFD67F827A8FD17FFA8FD66F87DFD19FF7DFD64F852FD1BFF\r%52FD62F852FD1DFF27FD60F827FD1FFF27FD07F852275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522727FD07F8FD20FFA8FD08F8FD4F27FD07F8A8FD21\r%FFA827FD07F8522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522752275227522752275227522727FD07F8FD24FF7D\r%FD08F8FD0427522727275227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%27275227272752272727522727275227FD08F8A8FD25FFA827FD07F82727\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227FD08F8FD28FFA827FD07F8FD4527FD09F8A8FD2A\r%FF27FD07F827275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522727FD08F827FD2DFF52FD09F8FD042752272727\r%522727275227272752272727522727275227272752272727522727275227\r%27275227272752272727522727275227272752272727522727FD08F827FD\r%2FFF7DFD09F8272752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522727FD08F87DFD31FFA8FD0AF8FD3827FD0AF87DFD34FF52FD\r%0AF827275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522727FD09F852FD\r%37FF7DFD0AF8FD0427522727275227272752272727522727275227272752\r%2727275227272752272727522727275227272752FD0427FD0BF87DFD3AFF\r%52FD0BF82727522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227FD0BF852FD3DFFA827FD0CF8\r%FD042752FD2027FD0DF8277DFD40FF7D27FD0DF827275227522752275227\r%52275227522752275227522752275227522752FD0427FD0CF8277DFD44FF\r%7DFD11F8FD042752272727522727275227522752FD0627FD0FF82752FD48\r%FF7D27FD11F827F8FD0B27F827FD12F827A8FD4CFFA827FD2EF8527DFD50\r%FFA87D2727FD27F8527DFD56FFA87D2727FD21F8527DFD5DFFA87D522727\r%FD17F827527DA8FD64FFA8A87D522727FD0BF8272752527D7DFD34FFFF\r%%EndData\r\rendstream\rendobj\r405 0 obj\r<</Length 23765>>stream\r
+%AI12_CompressedDatax\9cì}i_úÈÒèý\ 2|\aP\91\1d\92\10\12\82\8a\10   ¢¸áΦ"«\ 1fÎÿyñ|ö[ÝÙC\12Ârî\9cçÞû\9b\19G;\9dªêêªêªê®´ßWoÄòÝI»\17KÆ     ¯Çï/\88½Ö|"f¼¸Õ{6\1c.fs\115\ 5oB^\12zA§üYú]îxß\13gýÉ8\83\1fÅ)x( ·\83­î¨?\ ey\83!h¹íÏ\87=h»ìÍ[óÖpð^\9b|Mâ­~HÁ\a\0\8a­9ta\12t\82à¼$\91¡9oþ\ 2\9eó\93ŸÛ\1f\7fñ\93\7f\ 1\ e\92I{Ùd\1a\90¥(/G%¡C¥\7fÓ\9b\8aS)¥#\ 5¿Cß⤳\18õÆóº8éôf³Âd8\11g\19/?lu\ 6\1e\18Uê]è\ f{0\80QkîåÐPóg$õÎ/úÃîåbÔîÁÈè$\8d\9a\93ïøí»Yë\vèÆ¿£föýl\ 4-\8dÞ|\ eä\0lÄ®ÂE³ªG      $Êÿ\ 4\9foz_}Ì`àÁkH\82\eM\87À\ fi4\14CÄSÞ4ÅÂOý\1fr_ \18÷ãXÊKÃ\7fd\8a\ 6þP\ 4#=×\86Üû«ßû;ã½\9c\8c{ÒXóâ¼Ñÿ/ \9e"\19ô\9fÔz³\18öÄ»q\7f.\11\9f?ã¤\91^Lº½!ôUß\15\86-<@ü\ f©ý\94:ܶįÞ\1c¦f2\̱\80¤     ù\11p²ÖúÓC\9c'%\ 4WÓÞøvr\8fé#I\82õrIÆKÇÓ\f\8c\ 6\8bC\9aðR\f\ 6\9f"¼lJE\86ÿ\93 "\18\b\82\ 2\9a\ 5Æ×a*®ÄþW\7f\9c\91    cßËb¿«M\ f0,-ýÀ´ÇÓºÿ8å?\89H\18ï|Þ\eËD\83X\14.tÓLÄ\81\7fþÒ¸[\98\8c\10¿gHvanÇ0ñÃÉ\97ôLý\1d?\81·\17S\89xü÷;LM]ì\8f\11HÏ%~\92\ f\17ð¨,N\16Ó³ñçÄ\13\94ô²Þ\9a\7f\83ÜöÆÝ\19(\99Ô&ýé\95Þ\80ÖZÿ¯\9eÔ\ 6º6\r9»\15[\1d@ë½jÿô:sxYnÐ~k,úóÞj@\8d\ eâ\92èåÅÅìÛ{;\99\f\8c\8fT2åfÜ\8aúÿgà¨ã\17ÆWc\89ÓË\98ä\ efL \18ÿqX ·=\ 6\9f\f½Ð\1a\ eû_bkúÝïX!°x®b\92\9e­\81\fT\ìiïã?\95ÿ»\10Ë?£ödØ\9f\8d4iÔµÔ[â¼ß\19ö\1a\7ffóÞh5´bï\13Ö!\1dÛpkiüWo8\99ê\88T[Zã®÷¡%N\9d@£iúì\8f» !X\9f56NFS´\86z\eß­i\ f\93;ÿ\16\86\v1\1e¶Æ-Ñ\8bÛU\90Èü\80ü\82=3\9a$©M\ 5\9az\aè7}±\98\87òòc\9da,\8b­n\1fl*¸\10wãqkÔëz¿ä&XéB\1eËV0ç\0¦ëyö\1c{\ 4A(    E¡ ðB^à\84´À
+\8c\90\12h!)P\ 2)\10%¡T*\15K\85\12\97¸RºÄ\96\98RªD\97\92%ªD\96\88¢P,\15\8bÅB\91\8b\1í)²E¦\98*ÒÅd\91*\92E¢ \14J\85b¡Pà\vù\ 2WH\17Ø\ 2SH\15èB²@\15È\ 2Á\v|\89\ 5\9eçó<ǧy\96\14OóI\9eâI\9eðä\85|)_Ì\17ò|>\9fçòé<\9bgò©<\9dOæ©<\99'8\81+qE®Àñ\\9eã¸4Çr\f\97âh.ÉQ\1cÉ\11i!]J\17Ó\854\9fΧ¹tÚ\93\1f&\9dJÓéd\9aJ\93i\82\15Ø\12[d\v,ÏæY\ e\1e³,æX\9a\14\ 4#0%¦È\14\18\9eÉ3\1c\93fX\86aR\fÍ$\19\8a!=\f\91\12R¥T1UHñ©|\8aK¥Sl\8aI¥Rt
+|¬\14,Ô´@\97è"] y:Ost\9afi\86NÑ4xLI\9a¢I\9aH
+ÉR²\98,$ùdÞ\93ä\92é$\9bd\92)x\9eLRI2IP\ 2\8aT\81â©<ÅQàçP\f\95¢h*IQ\1485¤@Â|\90E²@òd\9eäÈ4É\92\f\99"i2é!)\12\9c\a\ 2¦\93\80\19!x\ 2Ø\ 6\9e\ 3\vÿ\0ñ\ 4 '\0\ 2x!Y\ fáõ¿ó"È\ 6\14Ð\8aZ\1a\1eÿ»®\85\9fy\90W@sq:E0^µ\8b±\11zñEU\92\15Yu)¾´\95ôÒÿ/\v/\88¯'\9dþ÷
+ïzâK\e\ 4&N1)ID8&Î\12ɤ*8ËOv$@\16\8e¡\95Õt|\81\93\9e \9f\7fþgØ\9by\12Õñäï1þÃ\9bñ\ 4\9fa½i-\86ó×\907q      âè\8dz\12\8d>Ä!=¥\vá½ò\10²»?\80ßn<\8aë_õ(\ 1\0á}ü\ 3\7f\9cÃ/?Ðô·\97ö^x\9f_        o\17Z\1fo<(æyìz\12Ò²päñ&\0\1f\13\b#ÒÈ[9Æzk\bÞw\ f\13Qo{ì"*DÒ̳\14gÕ;\9e:¯"Q`­ÃÁ\1a\80Ó³mi\f¸Ã&SE\12Ò#É{@`þ\97Ü\f\80L\8d6ÀeZ\95à\ f\82ë\16\88]Bù\ef\13ýÕï n´Ä?Òß\8f\17µK\bò,\1f\1ey\83ÿ\1a\rÇð8\ 6\1e©Øo/æ=\88<¢¨k^\14[ÿg@ì\0¾®Wç\eÂz±7\96úPÞÄ\19pF}\8a~Ìÿ _\b=\r\1e\8e\7fµÄÙ\11èG\ 3 C¬dèúWk¸Pú¢ö\99M?dê¥n2%3Ã_ÿC¹Óîãô\vé\829ÀD\b|/1\eVsHß;úÏ\8eq<\19Û\91¬\1fßpÒ\19ôºnƦôÜÑôo9zÒqô®æµßj\ f{n\ 4\7fåLþORôÌ_®U\1d\87¥\18\r¯³\98Í'£\7fÖ\92ýûä03k!·\ 5­y`:Ü\8aã¿]/\80\96ÿ RþoÐÒÙçßÿÁ«ñ?¬\ 6³a¿ó?Ý\16Ç\18. |¸éµ\86\ eÓëjjÿi»+oG­\18Ë\1f7cùóO\8f\85d\92é䪡üÝïοÝ\fGîø\ f\ f)M¯\1aÐw¯ÿõíÆ\84«=ÿá!y\13üdâ0 öd\ e^@­÷9\97¶ÈÜ\f\9dÿ\80\15\1f\9bºÆd!vzx\1fø\1f_òaaú§I\18õæ­.x@ÛÒÁmIÇ~WN9¸\91.]g\83\ 4£Ü\87÷SÚ~í\89ÞÉb>ì\8f{ÞÙ\\9c\f\14AcS2¥¦Îbk6ï\89±¿z\9dùDô¶[ÃÖ¸cÖ\10Ó+\9da\7fêíL\90\13ù/¯Øû\ 2\8d\95\91\10+(\9a÷þµ\82ö©Ø\9bõÄ¿zÞÉ_=q\8ar6\16 I¯Â\bo\7f6A'\ 1¼m´\8f\8b÷c1ô\14¡\98«ü\19ç½è;½7x ýÿÂ\19.¯¶ñî\b~0\86\18\14¨÷~I[7¨+J·§UfrÞ\968oOZb\17X2\ 4\16\129hM\12\ 6\16ó)\80r&\84\92){è$PÒë¹íÛV\ 5meW\95hJ×k.¶Æ³i\v¤½ó\a\10÷»ÞYÿ¿z.Ù !_ÙU7&Ò0\11uE\1cnAr¼¥n\7fÞj÷\87ýù\1f\14\99&½jò\ e\9f\eðÖZã¯Eë«ç­O¦ÊÔ\ 5\9f/zÝþb¤ã:ÊçÞ\8dû\1dÐ5Eÿ¬       ª\84b\92æ^]ÊÅ´j%\19eÒ§­n×$^£Öl`\1aèl:\99\9b\86}Em\95éèNûq\99\9br\v0OTMCþÌ\9b_Ì'ªpõ\96Ä\9aóN[S \7fÖ\1f-\86-MàHÛ\891òx\90?«·D\18\9f\95\1e:­ñÙ\1föª½?¦\11\19º¢\9dS`ÙLC¨i\85¡ccÑ\ 6Æ
+\13\e
+¸õ¢\80@CO+\890t}øîw¾ÍÔ\1e!":\13±Ûë.O£7q9\99\e\1e\93zN\8e'\9a}òöÇØ\18Nfx¿ØAÌtòEyaÂ\1cdJ¯\ 5¸o\ 1ÙÜ\82lsoô6WoØqWI\ 4\12÷\92\r\16]o\17¥ÞÊv
\1e\97\f¥É"\19¬\98ôR\r\19stt\ 1½\84UÓü\92Q\8d\86Ã\b\96û^ÉëEC¿\82­æªô²\ 3[õ¼\92:»f\96Ô}]nIo­f×
+á3ì>\99¬UBs)\13 ¶Ã\1e\ 6d\10%<d}¿Ù|\18ïJ\10±\f÷d]\ f:\83G¯Éý5¿ÄÍ;Óî\b\1e\ fÇ®\89\9av]\ 3\97\92\edZa¿¾ãt*Æûèàa|\b¾¹»\9eó\89²às´sOQ\8b\14åÜW\8a\10TcµÜµ\83úþ=ùü\9cõ櫺}\9bº-ñôgÒ\ 6\9eÎú_ã\96Á×±ì\88§¶-G\b+;bkê¢_kÖîÏG­©sW©\8f¸ì\95-õî\fÅxg2\ 6ó;G+§\ 3©¨§j¦Ûè\f«C_±\e\9f\88HµW0
+uü\ 4Sö=\11ÿ˸\1e\9b\15A\ fËb\12\11V pØ\9aÆ¿]öûË\9e¶é×h\10\87\90\ 1\9cawú\83_\90\14H\9bM7ïÀ\94ÍÑ¡e\99\16]êf©+¨4²åº4\86-éSP\90þøs²¢\9b¨Ûª^Ñ\15û1í\968s`.î\b,ëuA\9btöÁEgÕD¸è«3\12.zëÍ\ 4cÛ}Ô\12\a3#Õ.:«T»è«£ÚEo\93q[\9a\93Ïñ<Þ\1dNÅÏÉØIq§³xÇì5Úô\ 3ë\87\82Ç1tÕ­¿K=ÿ5\8d\8b\82×w«\9e_nÌ\10ô\93­\90!¨uè'\85ãö\10Å®83j/iÅqÜM    \98g3{Ì\9dQ\\89¦;£?N¶Oë8\99\7f÷D\a\86Ã\9a#\99\v{´¨\8f¼\8cËrCÚ.\89:£Àr¶½ôù 8B`MºÞö\1foQìÿ\85¨u´Vð¾æv8,¸\ 6ód·Üö\f:¼ÌHÔ ÇÌ®,)Z÷\f´Y®`ÈsZµ4\88\97\ fèó×R\0fî5ë´\86=\ 5ÖÊ~\ eK\91Ôi:ìüq\10Pܧ3\9e\14úÌ!¾S2;¶\96~\86\8e\14­è\83­ëª\15CT¬£\15É\92\ 13L\89e\9f\19IJò¨\92Ö\86kØû«7tТ\19¸D(\1cp´~ãÞ\17¬\82\7f9\8d\e\99RpÖÆÎ}\86d\aÇÒK1\8cÙÜ~·º=±7s°OSÉ:i¶\89µr\90À,\9as\8d\96¦\13<-G\e<\99v\1cÜ\ 5Üaæ0tÜ¡»X';dzÝY\81%c½\18w\\19\ 4Ü»5\1e+ù -¸]êµÊ
+\83Mפ4x\17oĽ\ f½6Äß­9\18Ï\97`ãáªþ\12òþE­\b(ÑÒ óô)kT($ì+ié#5w\87²#y¥£\17\85¸\9b¥í\bÛ\ 4ÁíÒÚ«u1%\90\97R*\96I\ 6®\97\86iE¬n=i8\1c\82aÁâðå ò¨ÛlÐ\9fB´2vX±Q7\11\f\888ë!Ä\ e+6ê\89²\ 2­yÏjvÎ'mT§ \9b\e\1d\1f¥ñ\9bظ2S¡«\88ôî6k\81è®\17\85w%)¬Ã\84\0·Ð\9aJùá~ÏÉãW\0ñh=¸q^Ó\95¾Bk6\a\rB\15t\8e\14R¨s±7\ 3rp\80R\9f\fû\9d?n\89¹uZ|\94\9ee\94Ü\82    ½ý^\8cÚãV\7f¨¸\e·ßÀ\18/*Ñ\ 17Î+;aÞ\99\\84çýû»7öÎZ\7f¡  h\8dõ¥«^¤áÞÖ\f\15+EAÝ\ 5\89{ïf\18$ü4\ 2û3Yx§ ªÞÉØÛ\93¦\0£\96À}µúc\94\1dÕ!\8az\ 1\99úê\18\9dO\10\88NÏÛÇ©Ô\96wØú\83v\ZÓ)pNÚ$\99-:ß\88¼³q\11'340\12¶10u\ 1ÔM>5ôý\99w1\1e 3ÜqgñÓMZGìO\9dÝ~¥3p\ 4kЭÎ7q\9a3U\8d\H\9aäÛßêö^V¿\84KPy5Ð¥R\8c\93\0\89­?¨.¶¡ß'°\1d*\102\9dêü¦\15"\Ó\12l\96¡\98ÒYK÷ãl¿½³¤\rÑ6ÛoÕýVì\8f.P`ºZ]\eóÖ¸Û\12ÝåL\94\97ä\ 3ó\7f.\1d\1d\80\95\e¡«\19Êë\82jçyÅBàjbe;\8a\88I·ÚÉ1×Ê¢X8wF[Ù\ 6Ê\8b\ eÁ²^@\8b ¹ÒIÅj\7f¼ÞÔH\e­\85ɸÛ_©Í\12'`e>Ó\12O6é,³d=èÂAç¡Ã´X\rÇ\89$\10/\9dô®ÒfÝ&\9b\ 6Þ\89&IUû\8a\93a\9d5Q:_LÆ\93η8\19õ´Q\98w0,½|÷g\r,\93KªñÄYn\ 1\b\0éù{"\ et6&e£@®vý\9d\8c\ 2\9eµ\ 2NÝÌLë8m\999ÒsKã\13þÜA¾=ùËYGÜ\9d¯X©üÖràH®Q×\96\8e#¬5/:_\8a¢l\ 4ÂJ%\2I§ÜðXúN\ 5\91\ 5ÒÏ\8f3nã\80]¢68\9eù\8e8i·æÒ\97\146°LKÄo\1a\7f\9a\\ 6x.myj0×\9b¿Uî'%q\ 1Ó}6î\80[gÒ\f\17Òmy:i3Ã~Ö\ 56ö?ûJ\0´jIÑiÇõ¢¥¹\11\ e×gv\9cðZÙNw&Ú¬Ð\ 6\92iW(-ÍÁz"¡÷>\9c\r\89I§ÍL\82À\13zIòYP#>9¨äûJ@\99o\14ÎÎÒ©b\ fÉ\ 2zH\9f^\1d>G\8e\1fN\ 2G­Çèyòð*ÆçÄòè;ó5ö\9d\v¾h0Pè·â³\ 3æ®Rbö3¹»rö\82>ÍÔ^\ 2\179qÑa\85\12u\91ö\934½O\10³âOñ+J\1cä\8eÞâáÜqt:ËͪTÂãÏ\1dÕ|¢Òé|Î\7fU®k¹cº×(ôO²\9db<\1eøZBUë6\ 1\1f[\14ü\19ö©</þ¼òôS,\9a\1fMj³üYcþ\1dÉ2û\v¡H\1f<ð?ÃÀ\83Ç_ü$ÎÛ\96À\ e\93½¿~~Éß\16â÷öHõý2¯¹ã\81ð\9aËÌâ£H1ê_\bÁr÷ÓãÇÌ\12>@[\8a\9f¯\ f,?Ì\r\1f3\9fü÷¼ðÍ>\91\ 6v|\1c\16;dí7w|\1ax\90à\0ɳÂÛ×Û\ 4~;ü-\9euÏ||,ýs\90oÄöÇ\12\r\8f­îÂãç~\82\91\93º\ e\16¾é÷£ã¼?y\18á/£\1f\91\!p'\14z\8bpöþ|ÿû¨Ói\rÐoýHé³ö-a&\89D\8b\15û\a\1f\99þÛy\97\1fúO\ 311ò²È×\1a\87¿\88þPîèü;éñ3G÷¯¹ü¸\13\18EN.\8e\12ìèå¤Ï²\89Ùg2/vÎÈÈ Cª\10;ÅóÙ=°\8d\rôØ\87$ÑÍô\v\89\16Ì/yq\12\8cE{ü\90­\8f¤\114kþ\áìxÿ¡\14åR3\98\97³gf?Ë\16&o\91ãûîs\86jï¿b°Ù±\1f\ 6\94eÂûhJ\9e\99\aæz\8cø\94å\a!&&\8bæ}·F\90¯û\17ÅDëøPðE\9eD\84\85A\ fÞ0\14ÜÅã'Ú{g4þ=\92\15\8eåß\8e\1fJU©{!Zú\90\80QMê\f\91\88d³¥(U<ý:\91á<\9c\1c\1fu\7f.ßðLª\ 4\ 3¼+>%c\81Nü¹JÀ«F\0\19<¹A\9dz4nKùøâ;f5øò§4óÄütò·Å\9fHñ3Qý-µZ\81\ 3\9eiß]suÿã]þªÀ×\8b\9f\8dþoî÷5óåñótóö]bæ\13Ó}*½\93\91{\9e~Ì_  Å\9f\87÷Bÿ\87I\1c}\8eü_Bá3L\ 2\ 3³\1f,{Ó\9dhøÒ\8dßQ5\7fU\vW\85b¨[\95x£0Z\92}\98ýù4~\1d9½oýJ\ 3Ê2éVîèv¾\97¿=\9f/\96\87fâ¬\8e\ fÊD<\8a>\ 5T\ 34çª0÷øKO]ÿ\17õq|Z$\84\97\\12\8bÀñDZP\ 4é\bG#ü\84{3Ï\95\91³ú\89U&B\92\9cÓïÙ\ 2s      Æ¢çÓy­\9c\7f?'±Ä\1cG¦G\1fBð³\1aÏ\13Ç·OTèàíX"ÄÈ\ efqÃõ\84àá4Tøfn\ 6¥H-.h\92
+
\9c \vÓ(u\91\84æ@©F\870´ýPáë»4c\8f:w7yö\89z0ÏA½2¼7ÀÞ+\97bÑ6g5%Ü W-xüùÛ\8bn\ 4,L\96+òµæÀ\8aZÜSׯÜd?AiJ\14A\95é\8b\99×\85P}x$\14SOT$[þ\88yüÚ¸`T\9dO¡TL±<\13»ºÇ\ 6'NVî£\18i1ñ1\ró?óî\88\1f\8eï'ùÛïÇ\ 3\0Q\8d¨\0¦¥èä\92\12\ ecìsþæó;\0¯½\1eòL¤ö-YËPñ3Pa\80ÚÉ·d\ 5\8f\15Å\84\ 3\82êëmRÈ}\9d<"\ 3ÿQlÓß\ fù;\7fgfìw\98¿¹}\1e¦\7f\86±clÑ´\85\0°hÏ'çÄ1?\bNûÂq\99\fêlûÓM¸¥ç ¬%:cM\12íE)xòû ­4¦§ ÉÁ9û\ 5äõ\1aKÏÓ\1f\91£³Ù'XìÎ~\8a'\17Ãç|ãã¼ ?åò\1f¹ãJ!\ e]>îÁ
\ eyòiñ\9eo,nií)î\fv\f\1aF9±\939\94fK¯\9f\89æñéUa̲ÕÖ>q>\7fÎ\11Õà¨\ 4?º<Q%»yé·óæ    \a¿=f¡­\95Ó·\91_<~\ 3°è{¶ä\17«Á¯\82òâ\ fü\16âêÒ;òÛ
+\ 2      \8e\ 4l*@¿Ó\eøí§(u\91;O%,*y¨\93\1e\ eÆ2*\9a\88Wß8\1ft\8b25\12I\888\f\ 5h@X¤ab\88\88¨\10ß\90Úd`xT\18\vzÛÌ"\95d3ÒÚ±Ì\9b\10úl\ 6üþ|ª{û|~u\f\9dîNáÇs^ÏJ\8c\96S¸x|­c\81ÄϧÀ¥\ 2å-¯q\16°\18'Êzj×\9d\bÓ4¨X(_Ya\16\10*w\7fÎ*|2°Cê§ý)É\v\96\1c\84y\99\ 6\9b±¸\99\12<\ 2é7\ 5¢üÔð'\12\v4/6\82á \16\9a~T\9a¨,3\v?@X¬\98%\8d\15sBa\87n̨³%S±lèÆ,3På\98yÔð\8e\8cO\ 5a¤F§¤Òp\9f³\ 6¤\b\9e\84ô1\8bõE\9b-ã¬J¼C\10¥îÀ     +U\91\15\12½\86\14@/¤\18Õ\8f4/èùº¬^)/\98å\8fÒÐ4\19S'^{GRïÂFb\88§D\1e\18Öd\84;Ä\)¿á\ 1¡aH#\r¼\18]¿pþö¾:\16r\81Þ-xú\9f\94~ÁàòcX+¿jÂÇE3#øÂqXÅ\88^;\93VVûØ\ 1x.ÅAîaÿî«Ð\7f\95:\83ßCi)Ó¹\13©yÝ>\9cÑ»\1d\91\a\1cÎ@,f\fh0¡Å¯XèTrýêg¡\17m\8d3D[Ä~î>\10ä
\8b\9b§bµúËë\aÄ¿÷ò7ãâU®ñ&¶Á\83\1dæF¤\11K\ 4Ç\ e\99Kbø~\1cû\10&æ§\89½Ð3\ 4¡÷ÓR'z\9bà\99(\15ÓÅ\8d$ßþ*öÂ¥\8aæ[!Oé|Ái\8c\91½°\97ô\95.tÓ;\11\10\9a¢Ø\96\e
+\93OÉ=[\ 2ðHÃk±Iââæ%\8fè\ 2O   ¼õÔÓ6Á\89\8bÐ\ 4yJ[\ 6'.B\13ÙëS%4\86\86&\85Ì\86Ø÷m E(éa\¬Èspç\1fÈþÑã9\8fÝX\8dO\1a¨ÿ»"\v\1cH+ñTå¥Xå\99\0 \bµùëÜs\98¿ö¿Î±;L\9c\97\82OúX\ 4\8b}\96©Æ\8bg?lKc\eD\16\88q2ûóã
+{?¬Ýæ¯îª î \9d\92pj\84Ø\ 5\15\12\f\13V\15 Ì\ f\99¯gìý{ü\9aÿ¿LhïªX\1eø{ ^×Ç(©Ñ\81ߢóòt¯Íq\91#úÒ\b¶f\13¤\1c\a$\vSÎ7\86¹\b¼ýõKvnN¯¨Ü\9d\8f\93#ÕÛp\ f\985 W\ 4ýÔÓ`¿ðýâË\0ÁïïÊì¿Æa¸qÐ}~p\11\81@¤þI«
+\92A\81Á\e? N|Ú\ 3\89Lw4$Ð4\9d\ebd\86\17\8eK/q\15
++¼½\86÷\85Òx\ 1ZIù¹QZ\92¬èAâ9Ívjçüu=W§BÍÑ¥Lè¢éÏ__6îQ¬­<xøIÊ<\9e\1c\12ì`Ñ®\13µi|X\9e]õD\13fY+M¸w\8dÙãWERÈ\89â÷\1d\9d¹x8Å\b\8e\88Ó£\ f\14Ý\15\88O®p¦\83}5å+<½à      2z7ó\19æàù-Ï\1dUcê\83[ú7ÞÏãX¬\14Í\89\15!\1c\8aÒ`®\1e ¾\89vÇ°:Ý\bÂ{È÷\8dF\9aÌ\1d·ßƹ\87ëÈaæ;ÒèÃâÇý`9×Yç¥\ 4F%V<\17Ã\1f\92\8c-K\8c¯\94\95zªúk\0æN{)&\7f\88W@\94\87Y¥¿I-é謽:B\90öB(8ù,ôg}\ 6e\15bÏà?\94~\v&\88.¢|\1dØk\1f¬Ü\8aö&Ér¡:Ä]2dÚw     º\1fjÖçùj\93ìJÓxR½ì\16»#.ª\b_p\ fâôS¢X¨ìK\8bQlâËÆ¥D\80\94={8Iez\1f\87=\99O\95z\82\1fÆoH\0Ú\18c·Ãã\970^Þ¥_Kïoþg þË\97ûåß\82ê\8aõ­Ê\1dX]æÀ\ f\9càÎÐ\9aóUj'\9eúF\1f\ 6[ìg\1c»Ëëuí\v<%öábÚ0®ÝROö8Yê\95Þ®Úïà\9b\1c!\83ó\19*u"ÄK\9e\ er×`þ\1eÒ\92/ £Oîç\8e\ e\8bg£þ3\9fº\7f\8cç¯Ãõ¯üÍmD\0\97_Y~@vE½«"-('\89âÙs-\8c\9c\8d\eÜ\ 5¬óqÃ\8aøôíáACxß?l\16?\81_ü0}\10-\9e\aCAìܨº\8f=\89/ÿ\0d\82Û;ú<h|[ ¥a\11½\99\95"aæS¿¼¡¹\9a¾óo¹û\83ù\Å\\83)þò1ó\9a\0\11\1f\9fº\8dÇ\f\8f\ 2>âì¹|,\83P×W\9c¡½øùüÌô\ 3×\17àÒEóB±Y [\rÍÐ/\15ñø\8f^'Ï/N\9dîÃìeçèÊÔå¨÷\16\1aà<Zæòë¨_\8aN\9e\8c)2ßH  ¯åÑ<\1c@ý~\14\v³$Eû3XK.¾%Gæ\94y,\17.\v÷§&Ù\90w/Dß3¸Í¿70ɹ_\9dw(\83"âÀÞ\87©Ç\8f7@,æ \ eúYe\98\13á·r\\1cÖR\9a\13,OâÛ¾¯øÙø\98\14ú{\a)\10lú*?Î\9eM¬d\bÍ\v\12º«IgÊ]|&ÈüU¦TÍ\1dñS½ç-Sv\1cÌ\891n\81lr.Ͼv/\8b\9fÜG¢ôÔ\9b\1f\9aøÉý\88\81[X÷ß\9aùÉ÷U\188f\14\16Õ]Â\7f¾hÄ\ 3Q§g:)É\9e0I+\ 1á\ 6ùÒm\9eån°Ûø^ü¼\9eÁ\8a,\99
+\99ÚʾPÈ¿~       \85Ü×Måà\84<Ës\89AÞºK>ü\93»¿=k  ÇŹ\8e\1a°Äû{¹ÌCï+\97\9e\8aï8/¹\8c¥D\85Âx®@\8aêßnG*¹yF(ÇåáÅ(\92\15X_~\9f\8e\9aêD%ÁP\96f(M\7f\rÒñs\98»ÿ¼]è`g\99óE1~Ó\8då2\97?\ 3\9ds\ e?n\99=a\\90·^TuýÍS5\98ý\11¬~\ 6ö\a\86\89ïÞÛ\87Âê\1e­gGåjQ¬¼ÍfÅh[(\1d\87\ eüw \8bÍ_~t\95M-ËËõ\1e,_\83'ä)\1d\91A°s´\1f\µÛDñóV\84`\8fø\1cé4ðlöúQö\v¾jNÜ\7f\9b\17?'bÜB`9X5¿BhoçQ(|\9e&5õ\91ã\17ä\1c°(\11+¹XÂÁãO\9f R\83ïb9ëï\81\8bÁ1R\ e¶sÿ4SÁ\1e  Á³î\ 3
+._\90\8dx,ö\8eßP¨°\88h\ 1 ²Ë\93\f\a\94\0^Qù'\7fEòI\b³O\a¹ãìt_¯\8b\8a\e\ 4¿u\13:.\12³è\13\93½£KùT¬z\8d\f\ 5¹\8c\ 5\84¾   Ô­å\9a´'ÔÆ¥Nj\f\98\90\ 5³\ 2y¶q!À\1c4ëùIþ3h\8d\ 58_MÛ\81\b\82÷D|e."IXIo\ f}®4Õ öҼؠ  ¾\9a|'æ Ä·ã<A\9d
+× T\1fD©Ó\14SÅ\eú«t\8eN\90CÇH\82Tnæ\86xãPö."'çOç:îÀªóÊ\ fÙÜ\ 1\ e\8dL\ 6m.°á\ 5²\177\99Ø籨\8eT\9f\82¸¨\84\b~8\9c~\98\ 2xMÆtËv»Ô&\8e\ 3z\ f^\86ÈH\ fòÃV\ fíJì£Í\1a"\7f3©Í\8bÑ«ß®â)\r\16*Õúõ\ 5c9,uö\98W\88d|¢PJßTÌÎÆoå¦\86\ 3å£ÏÑá!\84í?\8cæ\vYx\0\17`±Kq<Ù\80ÅÁ§8z(E®\ 2\11`à[Ì©_ö\ 2Ô°z\0\98\85¸\15f\8f_Åý\90;\1e
+!½\ fn\ 6Ö°×;¤Â¿\85Ë\12÷\81\17\94HvöÄiÆ\11¬%\1aÒIð:ÔD\11Êe¢¿\9f9\96\1d\93ð>L0`#\99a\8e}ü*åÓåÙ\1cm}Ç\84àyì¾@Þ\8dfG½Ìá\bÆ|\13ÒcÎþ\8e°\17\89lòi§g´\96\8b|º=ýÒ-     Øà>]·*àëM\88£ÏEO4\9bÞI¸\98н¡n\fc¡ñø\99_\92k\80¿Ý\10\84Ü\17\bycqÛ]-¿\f\ ez1\1fP\1cp\ 1âó@\81?r|\91oDs0/_á±\1e\v\16ÎRg±÷\9eOÇâ{È!dLâ\ 5ËѸÇÓ\83n\97¹¿{½O³§\85Jáû¹»\87"¬½Üñuò­X­\1eï©\9bÅæ×À\1f\13Z\87¥ÜÑë\1d¸\9dìu\84 ¢¿\1d°òO\ 3Xs?§ù\9baêÁ`\84[\8f¹Yèq¯X=O\82ØïóSãª")\88\1a\f0\88Uà'c#Åd\7fÃçÅN¡5(\86û\89Î\92ÍRÍ\95j©T#õb8\fP\7fbo?öE\14p\85sGoÄ\aóKÌú\80å'\17\12\82Tý0r\92/uAÏoó\ fÅ/úu\0Ó4Ïé\92\92R\97«Àk)Æ°}0ká=)\ 3¢\18«R¦ÛíÜf.ßÚ\váãuñ\8dÎ*tÏ\ 2Èf½\80\8bÕÜç\aÅÀOé©Ó~²\808\ 1öWbBþÆ'Êg(\f`\91e¿\ 5\93Y>Ç6R{
+¾%~\ eâ~¤ã]û±wÃÿüd+åÛw\b{\95@\19sù±Ôú\98F`Â&\19öî-ÒP\12\9a\98Y\ f$?\8c\91·å§ôí\1d2p\1f\b\1e`\81H4ü+\1c\v\9d\993çÕÉ\99É\83¼\ eÎ\910\1c3wõn\ 2tö.j\8cÝa\r\88     û\8cx]9EÑkàô\10¥\83¿ÁëeZ ë×\17Å×\9f8oê\9e¾\82åZV$öé©(¤³OÖ°\99ê7\7f\9d?`xz6fXövÚµÌZ3ª\ e½\16?ùù\ 2çmõ³\7fý²/\ 4«|:\7fU\r\ 35}\10\9fÀÜ\84OÓD\98\97O^\f\98\16k=\9c·jI(¶\9f(;\10íÜQõ
+¼'"úX<÷}\12\86ôù58*\17B\0Å\95\9c¾:+¥\94ñë³ö\99þ[ô+\7f[\9c\85\10¬b#\14½\86\8d\989¡À'\93R¤"%PQÛÛg     °\b¹\1aÙ5.[¥ãè¢Ø\16\82·¾ ûпmbe\80\15"ÖÔÈ+ûA\7f±=`\1eâ§d>)v¥±\10T'·(~í¿\1cH *Å÷s°c\9dT\87æ\99\'®\1f+ÛcØÑÛÁHZPª\ f\ 5\1e,\91_L_=ùBÄç{ì"qñò\9bBFø\9c\9eÑ\13p&_ÃQ æ:\ 6\ 3ÿX\80°\7fEüd*ù\f?8\98\97Hé"Â\81çÍÿ\80VN\17°\10\84îÿ;«Ö[Q¨ÒÊ{\85\8bKfÞ"þð\1c>÷¨UÛH'ãÐ\97\15&R7ó\17\83ô\10t\9f´aYåTðR/­\f@ÿ\91'SÁÝ´5V
+Q\8f\96ë¹´«\a\8cßÊG\87þ¤oè£Â8å\99\a\1f\ 6Ô·X\1eé\8bdS\1fläô¾\9d \12\91\8bpäô{\9eD¿Qôñu&©>¸V\7fÃ\ f\8e\92§·s\1e\1c\94ò ²\7fsÒB\91EV}
+&ó\86ùö\85\92\95\13_,\11¸ñø}\91ìàØ\17º|â|Ñï><úø\8cû"\8b\17½x,úbÄ\ 5E$N\9aA\8c>å+\84®é\195\83u1U\1cЧW\1fÙ$\9fN¦ÁÇ\1e=á´\1a>S¦=%*ï½\82Ç/\8aÙ\93v>:½<ÏU¹Y6]9~\88\v\93'ú¾$¾<\11Å'¡y+\9cäO:$80c\19Kòà:R\88\85o\0_\8d±\e®4 x²Æ\1d\0Ç\96:Õf¢x4»\ 5$Ñ3"A7¤ah\94ÍÒÂü\9ez\9b\f\ e\89î!\891_i`ÅWbv\f°Ó\8bH¶ì;Ä\ 3ÇóR\1cÄ\13³t\99Nÿ\1eýÀ\9få!¼Ý,\1a\91¾\88¯\17/×ÖHËì[*sö\16·Dúvp  î¾      ­\86\94éWË~k¤Ç¾ 8#ý¢5Ò:ùBïQ\99°\86Ôã×ÐÎ\ 2Ñ\8b\98\rÒÔw°\15h\96¬\91ÒÍGB Â\17\96\84.8I\aà
+^Z\8d\95\10n\1f\8a6H\99}\7f½\91ÏÛ!ý ÊûOw\1aR|\ 4VE[\8eå\ 2gíø£%\83_~Z\8c\8c´\1e\b\98Ø\9b<\9a\ f»\18)Èb»d\9cÓ&Hò+u~\89Ð\86\96g5þL\9f\\14"\80\94\9e,\89ÒÛ±`\8b45¼Ú\9bkH5I\96жķCÿ½\rR¡Ã\8c\ 3\12é,ÿ\9a´CZ\ 1\8eM\9e\9b\13ë±\1eï\ 5g\ 1öP´B*.>H\7f(xúüj\85\94\10¸RVC
+X\8c³zи\17\8f¬\91ÒÍWBx­ÜX\8etO\98\1dù\a\89û\86\15R\bÂËÑÅ¥ÍX\99ý@ï+we3R_B\9c\a\bix\89½×Bø\94Íù.\0);õøÍc­±|SFÚ\8c\ 5MH\99Fmp/!-½\f\ 4ÃH\9frDí¹\94²BêñÏö*¿3v°\7fÍ`´f¤\17a±o\8b47x/äm\90>\87\88F¦7ÃH\91\8c\99\18\\8d½T»½èÜ\12iãýôÈ\16éåKå\9c·B\8a,?ý\ î\ 3\8fiK\ 6ïÕö\17\8d¯v7m\89ô¾FNl\91Þ\95\13å!Fêñ[\8cµJÜO\939\e¤àw¼=|ä-\91>\9c¶ÃVHÁ&#´ïïùù\83\r\83_hâõê6d\8dôòêëç±x\1c¶Dú:\8f_c¤h}Y\1eë¨\1e9|´C*\10ï׿ÇÖH¯r!ñ9?+\98\90¢D\ fB[ú\8916\fNW|\ f\97DMBÚÚ\9b\97\8dJs*.\1e\9ei\844²¤4WûG¡·Å\b\1c>@{"\9aÇú\11¾9\90\91\ e¸°i¥9$..S\18)\15È\ 6+F¤qqöu¾\8f\90Æ4¤\80E6\10µ¸ïe\9f\11\0in¶d
+_&\9c\84ô4X\8a\9aØëË\vW\ 1        éë<sn`oè.rT9\ 1\17\19¡M,\9bÂ;2ÆÖ\ e\7f\0©°0#\15û§!\19iæ:n\1c\8e÷\1f\1ec¤ÉÓ»Z\r#EX¤±î?ÏRíç:BJ,1ø\92;Ü{\9c_\9f\ 1RjÉ:\8bùöø.âO\1e[?åÓ`\93\9b\97sËç\8b\96ï\988{\89Ìm\9e\ 6²dkVß·z
+3 D}~þ \88\9eZY\98J\e\ 2\80\97Äf¯ò=U\962«§c\91½z­¤LOÕÙ¯Ìçi2ûÄZ¿}vèË]=\94®m\9eÎ\8fªgçû3ë§UâÕã¯E"©\85Ísæý2\97]\ 4l\9eV?ë\19ö>fù4}ùD*+)\15µàX\8d8Pç2¶ü\949¼k=\97²6O\8f\83÷\85ã\87\9cé©Ê±\1a\1f~8\10û¼ÍÛ\95è{\81¹}±~z\91\17~NBÉ\90õÓË\eX\91\7ffï¥\88ÍóÇ\9fQr\96 l\9e\8e~'±A/mý´ùZgnZ1é]\v\8e½\86\9fUé^~ûý÷^±\87\16O[\8fÔÉ^\94\15ì8Ö{¼\14æ{W=ë·?\89×ïÀÏÙ\9eåSÿÃu÷>è»<µz*\8a'ï×\1e\7f2w\1d\f¢çñåç\14\7fvuÍ\8fÐÓ%#$æßG3ßk hùtñ\91   \86ýǾ\ füt\89cðü$\14Î=\84[ÚÛÙièxªF|Sl¤²\åà\a[/¢òQ/¨¡\19k\15\9aé#\v_¸zDú¢Å\9b\a_ôþ\râÊ÷î­/ø\1cZ ßê(þ,øbÕwð\85\1e\a¬ôZöd2\0j\1a9\8c\9c¸H\8f\ 3\10\8e>.p°\83£¤Ïc\15í~¢\7fÒ\ e\81\87·W\82p'a´\9bâ\1e\158©Çä`ç`¢_\82³ûIdþÏGR°Ó>¸\19è×}=Z:ôb\8ftOx\8bÙ"%\ 4\9e¹²AÊìC\944N\1e½kh\rH\9bo\ eH˾\94=ÒrYlªH\93ȻСMWö\7f\99Å\8b\82´<42øU\8f\94n\1cèÙ{\9d»Ñ!í\1e\1eîkH\ 1ËÑðîRCkb0\92¶©5RºÙ´Gº'|\11\ 6­4¢Å±\83\rR\88&!vhÛ!mÙ"EÞx\99ÈØ\8e\15{$¶H\91?rgÇÞ¨\86Tò.\8cc=;4Í*\19\ 5_\ 3£Ç¿É\13q¹èºêwµß3é¾MϽ«\13\9f\8b~ââ}à×Y\v4fÕZ~\9c\9f\9aT\17Þ\8e\ 6#üdv)I?üV@þß\19f\8cÂYUãËW7Àã\8b¨üã\94Ð¥op~\bÅ\95tôF¯OõC0\8f\81ç\82Lwë\86\aB3\93ì4ؽ5g\8d\0=\9føî\15ýèǾ\8a lJsÉÙ\1e ç¹H\86ù\1f\ 1uJJ 4³\97Í\96üº\1f`\195\7f[Ndé:×\ f\7f\95.òpÕÜ\ 5\90L\9c§ü~ü\ 3       \83\90)»RGP\8cdÛÉ\8a\8e\81\97ïêðç!Z¹\16a\8c\ fë\8b=Q3\13Q&\92Ð^`\14ÿ\90ø)åk0\14MT 3Äbá|m±\9aéøÇ\8d>&·\18ß)Y­Y\8dÏã×\8f\10ÿh=\16­fP\9b¿äéýíŪù\8b^Èò\82â!$cË#\ 4j\8fÂÎÌr?\7fåÆ\18Ʋ\16³\1c\80ÝÛ\v»ÇïBÜ5f\11\9f\81ßG·\92¥É\95Ê1½dMÈÖA ²\16çíø\9eÐ$ykηgÎ|÷81Ëdz\9aѱÙô\94^
+S=\ 2uôFÓãñ¯\9c\8d·\12Yz\9d\95U\10Ik\ 6\96«\87²Sf©\95¥\97«¹#=ضGÑ\8fg}Òu\89w%\14AW­Ì¶½V\86l\87\96<8ªÖÖ\1a\9aq\15\93Xý²¿\9aÕ\87\92\ eÙ\11BôZïwÚ¨ô2f\18\10\8fÊÚ\8c6£Sã2±b@6\16¦ôR\8c\e,\8cª\86\ 69'zõø¡ÑW×OS¶üò\8bÙ¢æ\937cÌg&ød·J_«B\13ÕIrÄ\ 1X!þì\ 2\985(Ð:C\ e6Û¦&f½kíÕ~]è\9d³ÖaIn    j'\9b¹\8c\96¢Ò\ fy®È±µX´\93{VÓ\89<%ã\84¢\1f²\15ÄIl\vÙhí]\90¶²\91ø\9eDNLtaIÎüÚPFýRü\13wn=È(\1f´ðÂÌSbØKÊ\ eÒSÓ\94ÀÛw¢£­ukzÊÈ\1f«\a\ 4\9b\85i\95Ïh\90ÅnÙhUuË\92Ç¿æü\ eÒ\8bõ½'kÝ\aâÛ>gf¡ñ%V\92Äí9¬\95\1a=.\:À÷½oGRNÖ}·3hëÒÉFö}\11pôù\r3¸Ê¥s?\7f¢ºT%\15\ 6X7Ð*9Óåñ»\ 5¶*îp¢Ë°'\8e\81ÙÉüú\83Ô¼º\1dpÌhi·ä\98Ñ¢­Í19³%\v\1aÅ?6cÆÀµ\82T¥´¾w¬Ñ\8aóü\88\9eyv\85åÐ9´Övì«bë^{üK\ e¶µ¯P1Gâ[håW\85z]ðÕ5\ 2ei7wy&ç§~\8d7r$¾>wÜ\87\eÙ·\âN¹>r\9b\19Kv¦\19\ 5;}q1MN!\9e\89\10ÉS²$e\95\15pC\88Ù·Ü\88'«Ô^!D\89\91Á\93
+Y.Qüã;½^L.miª)nÃ\19\92\8f\fÞ$¿Ñ§¶\83Åùz\bl\92\1a\ eoy\róa£ \10\86íÙ\92äñ¯EÔ\86\ 6@\951\9dÊý\9cíÈ\0Àø\ e×\8d÷mÇw\9a¹¾pf¹Ç%Ó\8bsÍWp\95Ï0xòú\\1fõ:û&w#T \90M]ºAZ_\9cs\9dv1ùÏ\19ñIî5]ȧgõ\f\16çnUÜ6ì\91£$ĬÔ\16Ì\9aêMÆÇÉܤàÈ\8e\91ç\v\17Q÷êìÒ¹YÁí2$N¹\ 4ð\93\ f\9c©±õß\972$É\83\fJ\14¬
+\8a]¤dÏ5ÿ]\91äõÓ$\0"è6½\81,¿õ"t¾´,o\90Þ\18\9d\13½ÙàÁã·ÉI¸\1dÐùÂE\8eγ"K\97<à~i·\8c±e\vÆò.®\93\ 3±Ë×\0wìò5FIÖÙMC¦ÁèJ££É\84Ñ\95®\9a]i#ó=~\17δ<\a\99ëý\ry§øc\12=ö{R«<a\ 3ï¦Uã2\984Æbk-\84\99ë\1dèKÕ¼\ 6n ç\99k\1f~*íWn\9a\8dN\9eÞù\93.\ 6äq\96ôªiÑÛÈ\0\80Å´ÞmÆ\18çd¡gy©³ñe\11wØõÖ!Õ\97\95ü1ãR\17
+F\8cç7`T\8f\rg½sÖ:£$\83\11Þz\7f\ 2%ßc¦1/í\8aºÍW!`     7ú"É\98\15\ 1#¶µÉ\ 5lÇr\8b-e\15Ï\9aÃ\92§Ûã[\rÇ\18H®A\8d¶'\8eál­\81\18Ê\8b9g¯³ÉëÀYsÝÓÅû\16À\8c;\84k-¢\86ýA¼"7Ææ\90\12µíj\87\ 1ï#\9b\16\9c\8d\13/\88V»M\1fû=>;'âñÖ%+uî§Ço\15\9f«\16-laÑîÖ·hv\19\12Ð\8d\1dX´ÙÀ¼[íÞ\83·\0f¿é£¬\95.w\90\100j{O©\0!Bco[Ý¿³²h\eèþÝú\16Í*w\81áloÑîv³÷\8aá¼Ûn\9då\88Þü.\81ÅÇc\9bÙº2LØ\11a\eA\e\ e­}\14Õ·\94I      @\0\9c\r\9b\ eu¹=øàâÌÕã½m í~K\1e¦ÓÊȪ\19øõÌ,\0[ÇÈbÝ·5³Íéú§\1fÌÊü\19ø¥=þ­\1d\14tä âά\84cväÖ¢\ 6\7fäH\82³õ\11\b\fÅÒ\13\Þy_ Ç^sl]rËÝ\ 4\flͬ\9fÓZ\18\95s\17ÆÕða\eÿÞ ¤/¿x-Ü.âC\11Ö\8a\ 5Lµc«WC\0æ&|´]\v\r\91\ 5\0Û:çR\80\19ê\1dl¿\8a\9fÒ²\Å\1e¶ñï\rPäµp«UìÁÊ¿·\82âñ¯\84ãj5t^\v\15\vÓÚ» ¶Y\rMk!:\12\1eÕÖB\19ËÒÙ\97\8d\8e#¡Ñ?Z­\85ÚZ©?<¥\1dðõ\ 5\80®7ÛpÔÀJ¬\95Î\19\v\14,¬ð(]xº\8aM\ 6\14Ò\85§\v R«²
+\8e¶Öȱ±ßíLJÖÒ6"zt©çN»§ 
+(?6\e<ØXùÕÇéÌ$Y+©$É.ÔË´0Å-\96¥¦í²d\9bO¶]\98Ú3\87 Íæä\9a}¼ß4\9f¥·c¥\vå\82H\9c\7f\1c8ÇFn\8f¼\15\0Ô¯yiÑÍËZ»7\bØܵ\10¯ä\98\9b|²ÝÎ\80q&\91$\e\8eß:\ 2³q1
\84Ãá[\90\btBU\96\89\84\v¢\9cÒÂ\ eÊ¥®Èr\ eHs\18¤Cá\16øôßf   Þ\1c°¾è÷O\ 3UÆÕ|±tìݪ\82ÎãßM\r\9ds\ 5ÝRÅÐ\865tÎ\15tRôº}\r\9ds\ 5\9dSµà:5tÎ\15tÆjÁÍkè\9c+è<þÝÔÐ9WÐ\99ª\ 57®¡s® óøwSCç\A\87Owì \86ι\1f®~ÚA\r\9ds\ 5\9d>\v·M\r\9ds\ 5\9dì\8f­®¡3\1eH¶¯0\9bZyÛ\8egàí+\81r¿k\91d\97é­\1f\8a«\ en\9f´\83.ê¥t^ß\96'kë\87+N\84ºÏô\ 2\9fêë\9c%·ßã«\aìO\9a­Å§å*\9bå\93fa\97Å`\11\17I\14ã\1e\9f\ 3°èÖãÃ\1eìêÊ9÷ã\8b¯Ò\17×L7mæ¬&ɾòqE\1aÌ\81¤å\929\94\1d]³hÎ\9d­iFujm8Û³^\ 1Õ\1aGA$Ý·;\fòVÚÍÞ\9ctvÔ²\98h½¡9ºîê©\9b\95ÅnÆ\93\90ë%\ 6\95õeÛ\8c±Rìf½mc\8e\92V\16»mt\14Äh-\811\ eûºúÐte\18\82@©Õ\fÒº¿&0ã\11,\88\11ã\8e\8d`\8aÉ-*\1f]Ú¬ÖÞ£ó\11\a]Ðë1|ÃpiB\ 5Û"ÓµJLå:>·%\8c«c{Áj/[ç\8f\99Sg«\13\87\91å4I·lU@´Ù\8eÕ m_ØoU\ 5\86Ï*ØÖ\81\9dÝ\89uêø\9c?]àú\9c]·l{ÂX\7fæJKZÙ\97¦eç§ç6$9¸ÒV\19x Êþ\f¼:u®æ\8f;Xu\ 6~\8d:¾±ó·\ 6öרã[Y\13c/\f\86\1a^\19Xc¼\eÉÒv>ì+SÖ\ 1f\U¶äØË\8a¨e-\8eÙo\85¬=HÙ¢mÊ1câ\97\15\97\ e:ͳ3\93?¶a\0ñUYYÀ(«ºmÞÒ¡^Îl-ì\0h\ 6Ƕòñ\96\94~¬Ðîùé\81\8b Æã&Ø[Jí:\ 4{6ß\88 øæÞá6 P\19\96eÔ¢\8f\99¶á\89¾TnÃ\19\19±\8fyìKÜVëþjvØo1\9aÄÞ\96\19ºï*¬b\87\93çRqþä\89úµ\13w~$õ:kÇ\8d~äÙªªX©bȱÖG©ô\19¯\93¶X
+\95\14Oɱ@ÎÂêØWDYÍÀFþ\18\90äw\9b¶Pý1[>Í\83nÕÕ¹Ì.àYA\94[>\99wq\8c" Tع\12\81U\95q¶$\99ê\91Q9ÛZ\19\19\a\92\92ë­bN\15vî22«N§»(\8as Éüí\8eSò|fÊÈ$\ f¸é\8a\98ÎMFæ\94¬îm\7fJíÜEFFÝã[\116\8cηÉÈ\18ï\1a «\81í\87f\91\91ÑNw¸/C[?#cQ-Èýnz@X?WK\15©\9bÕç­\93\91±Éó#Ƭ:iï²8\axø\92dw§$r\8b¥Bó̵oõ¹q7Îò´º\8b\9aÄÓ;ÒùÄ\80\9b@CÊÂUw\93ÔÁC³:9k\\91Ý\94¡\9d\84Ö?$gZ_¦UWG\17V\96\8f\99\8e.ØU\v®ª«[ï,£å        ÕªÛózÎuuÚ\1açX)¼ú\80\b.®³ÿH\95^\92Ý\1dÕ\ 3Ìû\ 1Ó±uh\v:\9fÈ\92\16:ÏÊï{î¢\1eN:\ f³"-¾u=Ü&ß\1f[¿\1eÎú\14ô®ëá¶:¡êº\1enÅ       Õ\1dÕÃa\9b¼µ\ 6®ª\87³ü"¨íùÀMëá̧¡\94í¿ÝÖÃY|w\14\ 1Ûq=\9c\8b¯\ 4ì \1eN7/ú#};®\87³\88Ål¶uP&eÓ\r:S½\98à,@®ÏD\9a>1lï[º9\13¹tJb3Ý¿Û¾¼\1e\19!\97'!WÂq:\19ìz\87\17Á1\ 5ÏëP£úc\18ζ5ö\12\94å¸Ùú\füj\8b\98\103\9fw\96ußQ\r-¶òP  \9bs!\8eA   ÕÛ\7f¬+½vR\11u5÷lïÐ+Àîí5ÇÆ\8eÙªa饹A9©É\e\a~ï@\r\9bÓ\9d|\r\18ÃqRCwÑ+\86³\85\1aê¡\80\12nù­\e        \8eý~ºõ×´l\8fY#`ÆO̬ú\1a\9a\1eÔÒ×\1aÑ\b\1c\16j[éJ;\ 5Ôº\8aÔ\87]T¤¾üî°"\15\80í®"õåw\a\15©ä^j\17\15©­½\8b¨3\14w\15©\0g+»©«\17sõU\91\95ÔX\86OëV¤ºÿ\f´>$¶>×\87\15Äᣩë\1e2B_ÔÉ\ 4£Kj\98       ®\98\bwy-¹\14ÎÖ\8eí´\14\ eùc+Oô\18\a¹A)\9cݼì¶\14n«¼¥ëR¸õâÊMKáLß z\17ÿ-¥p\96Y\85\9d\97ÂÙåÇ\Æg°~º1\ 5¦ï';\14>\19\ fH¬÷Ex³?ÖÜÙ\aÕPÁ\99\92>vþö \8b\ 42\ 2\10ù\16>L{füÈðz\9b\vÆjôB<áj\vÇáè\ 2ªò[ÊÑ鱸(n6\91äR"tÙQ\8båAÎ\0]K·ÆY\8a³âúá\v2ÙÏçO¶ø$Ü7r\899_+\89o'ï'·Å\ 1éñ\17øÄùCq¿wÞ(\9eF\1a·'\93\8f\b\ 3¿\95ëÐ3P\10\1e_\84.\15Èî\15%\9f\10§{uùä{\8bb·ËS}-\16*ØÁ;\89J±Ûþ}³®O_\19JÀ²\99ÂsÓ®ØíѶê\fÝÌG:TØákµm\902ûèRíW»b·\15\15vÓ¤=Rt­¶-Rt©ö\97]-VÈXag*v«§(\1dRc        \18¾jZEj.vC\17d\ eí*ìè\90C\85Ý\9eÐ\8aÛ"%\84êqÝ\ 6\8fÏO_\16\8aÝÞ\9d\8aÝ\ e\18{¤åëý'\réÒ}|þ«þEË\ eé\8d\ 3{/«÷¶HÁ\8e\95Jw\82qV\ f¤\ f\16(¿É·Ù-Â'  Wý\92\ 5ÂÔOÒ\17sOâ#\9cÉ»\80\18ÉLæ%mé\8417i³#ªEI\ 1\8b\ 5µàtæ~å\91\a\vlM\85\8c{DE\17\a\9cÜ\95ö\9c\98¾ª±Í¥_Î\9f\12_:seOTaÅyRçs\1ejôº³\9bä¬î\91³ü¦ÊV7É­\99\85[úèÙÆÕ\90\13\8f\9f\1dqã¹>ÇKäV\9cëÛÑ%r¶ã³¹÷ÍåYÝ\95$­¼kÀ5Ó]\1c¥4|KmóûãÜëËdÕ÷ò­\8e»º»\80nU¦wýjºMó0ëUÓYÅ\ 1Z\16nWÕtVµtÖß\87Ù¦\9aÎ*'h¡/[VÓYÕÒmQù¸Æ6¶íIû\8d«éLl1×Vï¨\9aÎ
+ÔÊ/\84¬]Mg\15Ô»[\91ש¦³Ú§Ñ­\95;ª¦³ª¥3í\8cì \9aÎ*Ïbü.Ü.ªé¬ríZôº«j:\87ï\í°\9aΪ\96Îxvt\17ÕtV\8b6\9eý\9dVÓY\91dþ²ñöÕtVóçñ¯å\9eº¨¦³\9a?ËÓP[UÓ-\83Zy§ð\ 6Õtö¾å.«éÖâØÆÕtˠзÔv]M·)ÇÖ«¦³\8a°<;¯¦³\ 2\80k«wZMg\ 5À³ój:«Ý\92¥3ð[WÓYÕÒ\99£×í«é¬ÊÇ\8c;#»¨¦³ª¥3¯/ÛWÓY1Ãp[ÖNªé\Trí \9aΪ\96Îá>¾í\ 3ÀÓ`QÄ\ 1 é~±¯\15.\86Ë\ 21\9f\89ï\16ws¸+|rm-$ïb×÷ÕY\91äè]lt_\9d£wá\8eO+o¶5\b©Ê%s\9dø\99\vÇÂ\9d\b\14çZ\81\82mÕ³5QË$¹2\ 5®n\97[K\9e¬H\92Nw\9c¹¨§wË'«\83\14N\16Æ\81O=·Úk¨ä2FD\16{Ï£sã\92`qÌÚ!3§[Åì/º[£fÍú\9a;ãw®Ü¹äë_sgoaÎ]ì\ f»½æΡ\92Ë]!\9d«o\82;\9fO\96/ºÛr@ç\vàضÅ-.®¹s\93Q<_ÿº\1fÛ\8c¢ÃEwnj\95
+N×Ü)µoë\9c\ 3¶Í
+%Oï"ûVc^ë;WÓêÎê,N3×®\8a_W\1fQ\82¡e\82[×òL«®Îb\80|ªgG­\véV\W°Z>«òyþ\1d\14Ò­<ûëqUah\7f\88ÃmU\1ab\f³\93CdUÛÔ\9fY\92ÝÜ°\85mär=Q}d»ÐY\7f\eÊn¿\a\95«Å7¸PÁ|\1aª±»ÓP\8d]\9e\86j¸<\rµâ`s}äVùVT>\86¶Þ
+ÁPl?\aîñ¯\ag³%Ï\10%a8Ûj \86b²Ýv\95«Ova`«\vk\1dn`7\17Öª\9f(ÖiN÷ðÄy\95vÿU@\ 4¬0_Ëó÷ù\110ÊÕÉpÝÚeËÊ\9f^ØÄJ\17÷½:8\11È[\v\99OPC\9bù\92¦\15\91¸í\19껵\w\87\9bòvv\83!Z+wV±y?±-dXG÷g\ 3W\17\r­®|<
+oëN\14\96¿R¼Ñn5\86³^=§Í=VK\9f(ÞtTæ{\12×¼\88Ǥ Î\85\fkîð"ß+¼TÈ\10ø]Y\99âR\r·ºáN«¯´¾ãn#5\ºánó{«×¹áÎÁ\eÇwÜí¢ò±\18ÙÑMy+ÔÇõMy;©K²»ãníQY~Ú{©.ÉÍE\93kßp·â¾×èr\ e\rwn¿j\8e\ e©ì¤°K>®a\1f½º/¬mí½¯¨\85õ¸/¬míµ-S\ 6.\82PCåã\ e
+k_~­ªÛ×ýÎ\95\ 4gÝl\96Õ7"\0Î\ e
+k\11\14å \93s,¶\1a\8eýwáôGktß\84´-czX«¾ÝB£§Æ]Ñ\8fPÌB\r\1f·Îkéî}³ÿ°\8cÁ\rpQÄô.\9ab-ã*¶²\8ci©"Ì]Ø®\9b\17ÛÀýqëÃX:­|t\95¦vSÄô.\9ab÷ÍãJTèé¶\88I½SxÙp\ 1Gïí\róZ\8e¡T\91\1aw[ÆäÊ1,Äc&Ç\10a)ÄW¬%.\1dæ­c\88Ï]¬[ãÊ?~Û~©Áò\9bõÒ\17Úm+ÂV\84Enwl$\8e\99ïhÜ0\81\8c@Y&'7ña\96Îo¬Ü\p¼[pûë\1eÛ3¼ä¹ªz^mÑ\9a\8e_\982U=;Õ¸jÚK$\84÷\98\15>¥Ì¬W\10ÅSÒ/\95èÝ\9e²M\8f¿ø$4o\8bO%1\97¯0·ç\ 5>Þ)\14øD\15\1dãlL\95\85Ç?4\92'g\97L÷°ÝM\87ÏVõp\1e?*N{v¸èî:S×\8b\92¡\1e.rÔ¹²+ÂK}\87ýǾ\89Þ&\e\v¶ìkÿf{B7a\8b\94\10nùk+¤\1e¿t\ f\9b¾JÌ\8côÃé\9a»\18§Cj¬\12\13g\99ÈL\97»0_tG?~×\8eljÿö\82¶¥iââ\83´,Â\ 3\8ea\ 6\1f\1f\8dtwÎ\99ÊðL·ë\19GúìTù7¢\f9XsíßäöÖ\16éa­÷ݵCÚ³BªÜû\96¾¼~´e0Qº|.Û MW\f\97\17\9a\91^c¤º\15y\1f«¦L\0þM.ÂÌ,;u¿#Û~òYk\ 5÷KÍ\15D:T\97úÉËd\8d±p:\15Å\15\16Ã¥»\9f\1cóÄ9«ÕÎéØdDúº\9cé\16³Ã\89ÛócNkósѸ¯@¹È\90Ø\97$ýº%Iª\80p j­£0v\85\8eíèhÕsÑö`\951\aë"\93T\ f\98\8f ¹\98\9bY\9e\8bë\1d­r(\03\7fyróª´U'4ås°®äib\12ÎMÏö\14×9²¹\8a$£hZè\8bk¦»?¥\85óÉN¥\80öyâ5õÅá\9cVN¢gùl¬Ñ\Å'K\9b×ͨî¾LëJ.wæê­ävûÒ!\a\8b¿\e·\93¯Æ\95Ðìï¨\1aé­´\83]\1e\18ÚËöù±·Ò\ e¾Û\bsnµe½æw­7Ì,\9bÏ\8e\96\11ZT\ 3è¶^leÔ\82\80Ù\9fÒr\93\1f3\1d\1aiS\13sÕlk¯æ<f·\16¦M-v\15#»H\a»L\ 6\v¶gæ¤Ý\ 47××\19\98uaÿ±+«\94\1dæ\98Óµ2øË\10\86\ 3\ 4åÕ_]0û#¶\91x·ì°8®WH\96\9e¯¾3Åu!Ù\9bù;\aÚì®]cµÊy3dc\15\92\96ë_\ 2'=Ûï\1c¸ðÆõ$-\9dSÚ¢\14Ðhï-K9Mß¹²/\ 5Üjþ\fu\16\81\93ûØÎ\84á>nZ÷·\ 2\96pA\97±ºÖ\ 1ØÊË\7f×áØÊû}Ü\ f\92Ú%Ç\92»ä\18m\vl©\xÙ;Ôôe\93*@·5\80æ\13Ý\e\14\88¹\b=\95¯jØ\81XYvìª\ 6\10ÍþæU\80æù³«\ 1Ô\9dPÝ 
+й\86ci_lÃ*@\93¨ØF\98Ëç`ífh\9b\eõ$\8emZ\ 5h\98\12\87\1aÀ¥\1a+[vls£\9e.FÞ 
\9e$c®Ä´_)ѳ4*\93÷´ö¥|kÝȶñ¥|ÆÛ²þ]\97òYe\15v\7f)ßê\eÙvq)\9f\14#\avÃ'ÛKùðNâz7àmp)\9fU\16\97ò9Þ\8f¼³KùÜÞ_¹Å¥|\86¬\ 2"ªF¿Ø\10uuçª4ØæV¿]|\e
+Ýë·\93\9d}\ej»{ý\8cC3\176mpÚÖò^?ç¬\90E\8dÕF÷ú\99¤Òt«ß¦ß\862ßëç\9c\15²\8dÄ×¼×Ïf\86ä[ýì¾\rµî½~®$yë{ý\9cO\86è2W[Ýëç¢\92k\a÷úI\95\vé\u}Ùò^?ç¡-ß_¹Ù½~ηúmt\ 3\88Ž~Î\ 3²>¡ºþ½~\96ÓäüUó\rîõs>qfó-h#wÜ\96\1fÙ\16\1fé¼¾­îõs^èð.Ï\ eîõs¾ÕoÍ/PÙÞëç\1cêÚ\9c\82^û^?«SSÚ­~[ßÇ·\83\9aw÷÷ú9CÁ³¿\83\9c7Tt'º·º×ϹpÖ¢"u£{ý¬7T\94[ýL·3l|¯ßò18ý­~Ê9¥\8dËSä{ý\9c\8d\99Ç´vmz¯\9få>\8dêBHQÒö÷ú9\1f¸6V¤n~¯\9f¡~kéV¿¥ï\mx¯\9f3\ 3\95\eÙ¶½×ÏQ×î\90$ïâ^?çÝá5îãÛâ+\1eÆûø¶¯{°¾Õoýj\ eë{ý\9c·\88íOÛ®w¯\9f³'/Õ¼o\7f¯\9fó­~»©J[u>Ããßͽ~ÎûÍæS\ 4\9bÞëç|«ß\16÷ñ­qHÃé>¾­?¦£Þê·\93ûøVÆÒ.¾Þ°|¯ß:Wñ\99,Ìv÷ú-\15[\18nõS+\86¶¼×O\15/Ë[ý6©J[ßͱ\95±5ïõsrs^~!Þßɽ~»¨{]}¯\9f»º×mïõS¡¸¼\e}³{ýÖ;seq¯ßæÅðê­~\86û\91õ»Jk×<9Ýêçð\95³µîõ³±c²\e°´\8amx¯\9fóA§¥\9cÒ\86÷úÙ\95ÇÑ«8æ¾æ)\13\95[ßë·^\¹é½~\ 6õt«ß6§\aõ÷ú9K\84Çe\92wÕ½~Î\8e¡º\93¸å½~Î\ 5±Ú\8a¼Ý½~ηúYøc\eÝëçä\ 4I>Ì.îõsëÃlw¯\9fq&Í·ú9V×®q¯\9fóâ°¢\8aÓõ½~Î\8b\83ì)¡­ÊøÒòP£\1dH\96Ö\85å\13\1f¦ïJ%\ f¸ABZ÷u©]¼\8bi«ÎÎ\aï\8de\8b&;FGo\8c»7\87\ 6µ\ f\8eô)/l\98Õ\vAp\99\80R\ 2\95Ö'»Í'\88PE\98\1cZøb\89ÀE<Yã\ e\94\99(R³¼/ü%^'bþÌaòá"\95O1\91Ù÷Yb²h\ 5*½4\17>}>xÚó\9dÍC¾¼p\93Øk¾1\99\83ÆÃ~Ñ?\187j\81Þ÷4æñ3\8dÚï;;èÖ¾r\83\8fËïj¯Áq\97/\95ßû\ 69­~6¾¯ûûrâbqÿ^\ e\ 5ßß\v\91ÐO3õs5ªG\8e\91§Ü\ô7\82\ 1QL\1eøö&½IÂO\1c|\1f\85\9ej\9dû\b\17¹\bf\7f\ fF5¤ûþqQ\14O3u_øµ|á£ø«aä¨Cç\b\81\12Âí\83@\94÷'\97Dùêò[\14û§1qñ\9d\rÌ\ 2ÑË6\1a¸O®´Ìþ\96"'éËg4%>\öF\94îÙwqöu¾\8f´òªgi\90äyÁõ¥ÙÙ¼ø$äkÂIþ¤£]\ 1)Ý#\18à\7f\995$éÆ÷Ý\ 1øÉbnÌ\ú\1e\11«±J#]|Ä\ fÂ\87þÇúAúhÈûë\8dòyà£qvBs\87\17LD-\ e\85iz)ÅØÚá\ f\88ED\98í g1\9fØ\7f\ 5IF\ 5\9c7°´\\89F}*èÕçÃ?4äV\91;!ç[ymhÚê£çÃ)\eð\80àL(ºôÀÿÞç\12óp1ÂÑí\ 4_¢*9h»8Ï}ÞÝ^æ+ÌÇe\84K\9d\9c
+ÜÁM·ðz¾_Á#¥øf¤$©5ÞUÉ\96\1f\82è·`¤\18õ/\84`åì\8c,½e\8e=þB¿\95 Ñä\8cK\9dÁ/G$\1e\a1ê½Ò\8d\12\89Öo\f­ñ\aèDD\f½\r\væÉd\90<½Û\8b\8a\ fà`\88H¤è\10þ\138\96\9bÀ\9f\99\bþ\13\7fµéæ\17\1ar\18N\0 ö¢ø·äÁññ\87ð\1e\12\9fo¹¯ÜQÍ'\ 2ÝU=¡Qbü¡>\bë\1f\14ümõ\ 1:9¬{tGvÕGqý\83¯\93\ 1¡{\10;8ûV\1e\\86ðHÉrm¯¥¶Epg\8f_ê^~\8bwÔG1\1d\9cò4C ¶\84¼$¤.    T°4"+L\8dB\7fR\12ìö\9b_\85}\1d\91º´§$úJÇuL·¾\1c\80°\ 4Ñý0×q©S\87) 8×\ 4þl<Ù©\á?e°\9d§g\12\87)D¢)D\12\17\8dA\12\9eÞFñS*ÄdT¶ÜÆ\15,Dè MPÃ\86¿\14åBo¹#âþ@/\92J9¦\14a\9a¢WSÔ     `\13\9eÝ\80%t`ÉÄþì8rw$2Ù;ú2Ͼv\83R,ÆS¡§\86O\96äæ3\95\1fMj³|õááC\134*´è\ e\14&<Dµ©£\8a\95\13ä\13>HrN\15\9fÎHI\ 1\8a\9d«\14þ\r$¹8¼£äÖÅó\eV{ª\14j½/­\81heÓUÍ\9eLý:\ 3 ê§\95\ 1@5ï`\ 2\96\f@\8a>\86ß\8eÏs\89Ùô\14[\81â¢U¿²4\0¦ûseY<\1fI¡      \98è\90´cu\0\1ckmDétÿ\vê\1c\8d£9\bÂ\9f/3ô4\11)Ý?V\10+_ä\13Í)2\88¯ÙÄ7bÂ\9flXÒýÖ^\83Æ\90Qt\ 36Y\9e\rÐ}\l\8d|\8a'XÙïEIL{uöX2\9e\9fw\99gËä\bX\86\17YJÔ\1fè\ 1\19\r|'\90\1e\87\8cºÏÍQη\1aQI~\ 5¢Î\80Æl\ 1Û\9f ÄÕw§²åË\9e%\88T»z*Ù\83,{_)|\fò]ð\1d§¼Ü¥\9c       ¡É¹ÀQR\8d\ 5î¤%Á ËwÁ4Ìï×\95iWÍÊ\ 1Ñ;-Kñ¼\9câ^®­ÆëÁIâ\aû^øbe¢·\97Jhë'nC·Ý^Ë\vù\89\18Ñ;<\18ÀñiC\aàì%\1a×G|x½ËÞ½\87Ñ\19ã\9b9r_î\f\92\ f\85äú\1c>QçºåM*\90?¾Ò\0\98kÞ1\88\85\ 1Då°¢¹t\88'R==\9a!°¿ÑK
+\96\91,ØÃh%\82~\8b«m   µ\r&,ÚøEÞÅ<(J\82F\ 4ãO:Î+£Æ\1f{øP¾ip\82LæÅ\18?\b\ fn^@@è²ùc\aÈY\94\aD\87^ñýÈ\8a·\83>{ 8:¡&z°/»AûK\83\83y8\92ç\85\ fUUï¢ò\1aßû¹Ã\8e\ 5òG\90ѯO05\1e¿áÂdì\94\12Ç\92ìÕ@.ا\16$Bº§}å\ 2­á{
+5º\8aÿ°\8e\ 5Ïb²\8dY\0\16\ 6]_­1!Txê¾)L\88Ç\f\7fU&¼éY@}]«,xÒ³@ùæ\86²\93\98\9dg\7fW1¡u|©1!ù<$Óæ\93Ñ:¤º»£¹=åK\14NL\88kÒÿè\9b$J\92\1c\88|óÙ\9d\1cä\168\v\87E{áÓ\98à£gÏ\87*\13\1e\1dä@úµÄÅ\17;Az\94\ e\ 4Þ\9e_\ 5     Àý\ 4\ 3@ë˦ \9aSM#\9cõA\9d\173\88\97ß-\87ñ.j\0ìµÒ\11D{æv\18ò\1dÜË zó-\87ñ½0\89\94\12\17ª¡o}©4d­³õûñ\86Ú­\0hN¬\0ÀXÜ\83x\99nkaêï6FÊílÔÛ¢-\r\1e¿»aôf+g#| \ 3pZø\9d\18vF²õï-\85ª>X8\ 3\90\11ÄÄ·\9a\95\8eK\9e¸§ZKº9Ò@\10\95\8bfGß³ù¢ÊNSG-!¼V\8c÷#¿OUÎ\92z\19\93\97D\17\8ciöìç×¥\8c5¿gÛMNs`?»:\19s\ 41^l£®øntq\ 3£a a±çL\83$cNªÒnoªñ
+\80oQ\93±\rA\fÜL§ã\8aÜ\1eÏ·Ñx\0ð»ØvEn¶\7fõ\7f>íï\e\10ô~%\1f;\9bª\ 4¥úÊ\88ñûN\92\8bLôæ¹4öÌÔ\90ùM\17\ eF/Rð\1c8iJ¡0Jgã\1cNL\8eÊ\94@Q­ILUÔ¸³\12Ä¡\rÚ\ fBþh%\8c#l´Ç\89>\ e\\89Ê\7f\16âÈQ«ÄÕ×tQ§> ËÒ0\16\95ÐW¢\e\ff\94G\99°ö\0ç¸\94\a¹¨ö\80
+\9cÖ²Ê\ 3!®{ðºxË)\ fj\84\1e\8b)"Äa\8a\1es9\17Ñx§Ç\\16\ 3\1dX¢r-!Gâå+\ 2\7f\7f,\15I\1ep#\90§ò\1d\85`C4Ý\9as2\88z!\82ºDá\a\vëuý,¦Dw\8b°\1c>ÕkqÜ\ 5\10<\82`×o\bä6\93ð£\8bþ|¤ð\19\92ýpòô\16ü]<\83¡\83xHÉ\15\9dá¹\ 4,\ 5òHFÚ¼\8ac,I>\9dL\1fõÞB\9f\85oF¸â\87éý´>fÅ!\ e\18\94ÇoÚ\ 1\99\ 3Ø\9bÄú`\97êø\96ÁÞ\11\1aXæî$SÈß\1d\1e}\14¾Ùê4\7f\9b\1f? ±ç(þ9ü&§\81:\8f}5\17\96T\ 5íC\9bgdaÚ5MlZ\10ùÇÁ\8doßàüg\14}ª%$ýÖڻǩ\ 5ÄþFXú\8d\7fJVP\1a¨       Bu|&Ñ\8a¢\r-A\8c\83^%\ f\13Χ\ 3rì+I\7f}"\8bÈã ¢¨Ü\eÌå`/\9eèïÕ\ f%y\81xw¦¤Xâ\ 1èÒ\8cáÓ$8\e+%hP6\16\8d%\95\89\90\95\83\9f¬¤rªfÁ;÷\84¤whë\ 5\15ÓÄñi\19\94ä|\92ÔÐúèX\ 5_\94UPO\80©'ºq\ 6\17\85}æä-ÎQF\vþ©L2h¥\94\91\91\12Ä&]\9cµ\aüpo,\90Ñi\81(\9e\1fO\bèrEê´òM:\98$\89@ù$¬\8cï2\82\r\9c\96\9fÿï¬\87á\92¤\97#HÎ\9b¸Y\f\95Øÿê\8f½QÏ\91'\91?#É»qw"\88½Þmï_ó⤳\18õÆsoÆ\9bÈ7
+ggéT±×\99t{^ü\85\8aÔ\a«\v°%ì²ä&í¶\8d\8eÐÍb|ñ\93+\ f*û7'­â'ÑÌê\ 4\1e¯\1c¡då\ 4m)\81\8bd\aǾÐå\13ç\8b~÷áÑÇgÜ\17Yd\1a¾èÅcÑ\17#.(\88\84\9b\96\e³:MxbFODZ\ faÂ>$\89®E¦1{ÒÎG§\97ç¹*7˦+Ç\ fqaòDß\97Ä\97'\ 2\7fá\13ïÆ`˳¼}Xãlwɤ\ 1\85_\ 5\88\9da\ 4?\8f¾èý[        \88\7f\ 5c­=4¾sôãÈ\17Ë'ë0êÃ7_¨?Ìúbãë\17\13çвçw»R)\99ùuÖªõW*4ûë®Uë¯TúuÇíZµþJ\ 5XÖ^«Ö_©ÔSëk¬Uë¯T2\96U\8b
+\9aZ»EÅÅ"åño\ eÑýú\ 4\92ìr\85Úf}òø-W(m§Ýx:\ 6Þ\81¹UÔ>ãBí±Ò#ÝWÕ\1e\8cY\10Y·sdÇêH÷+¾hèôÖBí\ fÌj/I Ø;iµ,½äñ\8efXý¾bª1\95;µ~QBµ\896\18è J\r#M}\9f\v%ê"-+;ÊHö~zh\9f \12\92|\8dÏÀ/þ\13~4'Ò\ 6'\15Èù^åÝÉB\1c\7f\97ì¤\1eÅÌJô\1fçqiáëG3\11¼Ý\93<8*\9d[\1d\0\83       »¬*Û\1dò\ fôàNÞJɲ!M{±7\88t'{\12QH®Få\95ó\8e\8c[ìh&\94µ±F¨{\19¤~O\92ÿe¤m
+X\19Ãx|\927\88\1c\84\97¼~\ 5qØÊÖd\83_:Þ¨\9c\ e&\8cw\88)\19­\83Ò[Ê´5`Ü[\88é7\ 6:éªioÁç\9b\aÕäʬn\ 2\80£§p9NWòJ²ýd\12\ 4\89]hi`*È5\93\86-\10fßçç\ f\8a
+\80Ρ    \0ÂÂ't Há±Lj{z²ò¡\15\19yy\89~ö®\84½8$ Y,\9fJÛý¯ÒÖ\14\13ý«qDÛ\9dDµ½ ,\ 6\1fq\8b\94\ 3¶NÒ©\a¼ç!\1fnðøù\87pKÙ\80ÀB%çÞÍÛ\ fÈ\83ÜpûA:\8b\15Ú2÷N®H\15¬Î6\fÒS\85     ¹dÄðùï\ f\85\ 5tBc\ 1¢f Û~@>µ"ɶL8Í\fÆ
+\13Â~ãöCÌí6\14þZ \15\13NV'\1a\9cäà4S\9fk;0\1e¿\8b\8d(\93\1c Ò\8b\80ÂE¼s¤pQ/H\8e;YÈà\ 6×\ 2±\ 4\0ߪg\0\80¿©¾\ 6\béF°e\8d\aýn©\ΰÅ0¤\83ØË\0LZé\b\ 2ß_±î0,Îûm3\8c»LÂv[\ f}\97Ì\ 5\88·\1c±Öö¨    \0:O\1eø\ en£Ý(B\r9\ 3À»¥\8e ¸yx;\1a
\88r\12Ï\ 4Âål\90ÑZ$ê\82\ 6\8fß\81\8a;2¶Í>-\19}c%\91Òö\97×\1e\ 6x3n¥RÖý%\10Ó\ 2±\92\95NK^ìà\8cÄ\0f{å
+¯YKf\1fí ÿêz\82c\15\92{
+?¤Ö\ f\9dÇøÐ÷«í\85\15ÎâuO欴 ª2æÄ\98ò[ÜÍüÚÏnù\8b\8ei2¶Ñä\94§\197&C/cf\10\95\83\b«aTb\ 2¡ÈØfF\ 3\9dÄ#ÝÒ Ë\98\99\8aö\9b\7f;\8doOIÝtêVd÷ :\all»\15\99ìÄNâ[i|\87)\98ÕÕå\8a\8cOI¥È Å7É3ü¢1/\1a$#\ 3\16Ý¡Ñ|Sbà\8b±\1a\ 3ÇÔx\18â·¡\14ñ*é\9d¦|¸L\8dýäÈ0\15\ f(¿\91A\1c«È±\96îô\1a:(\96:\89*'WùÇgtØ­\10W^\14\12j ©??
+ëл\9aè\f\19\8f£©\a\11ý\83ZD:B\8ag¿ª?D
+\86´§¾\93Ð?\98\16´C²¦\10\ få«.õçcËwAõ|ì¥ñ|,\18\ 3õ èe\1cÇ        H5      )¸\ 6\1dC2}IJQgåô\ 6ýy­\87Ýþ\8a Î^Ë\81)ÈbFÝa\8c^àM.\1e\89\17\92\89ë\84|rõô\f±íZ\ 6Û¹¾Ã`Ñ]*Çû8©\83nÒñË)\9f\18\8d\84æVÇ\16*t\9a;©T\14%ÝÎoøaü+\9c¯w>kÅê\99¯a\11\86\16\8cIkÓ\aGx*T\11\8eÕä¬\19\1e\8e\91×\86x];ÑÒ½©yý W~}J\7få\e\8b\83~é©[G©ö[R\93^|o²\9aÈk¾%\15ñº¥õgSOYõ¼ñCL> z]@æá!¡\88ý\ 3Àn¿¢\80ë\81\92\ f¸ b\82eì!\89\7fW+'RÊÙÔ     \1a_âP:".¿x1\96%þª\17\96¦DM\1dayÁÇÂQxHÈÇÂ\1fJ-9s\85\82\ 5\9c\1d\r\15ðÛqöTK¦&¨bì9\82\15MÕ¦\18\96\1c¬k¨:ìQ=í©Üà\ eªgúÞ\82úa\8fÔ\19\81ê\87®å*_¥Ô\17§_Qü\167e^ñHÉÒ[÷\19\13\8ct1&)¡\94g\ 1ýóøu\1a¨\9d\v\ 6CiSÂtb\ÙoàÑj\1f\97ÇWÎEðÑQå`ð쿳\1e\90^´Ýð^\1awõ[\r\1e¿\1fZ\1a½ùb\8a:¤ÞùÞW\7f\kýé\89\1eÒ+ýCÀ?èÿT*åe9ô/áM¡ÆZÛ\13Ä]½TÈ[\e{üï\89¼8/ö;óþdÜ\12ÿx3¨éñ¢vwVôf¼Rßwè{ä\r\ 21Ä;ô\86G!´½ñ\ e\ 4¾{\bo\1eþ{ü\epç§ð£\b\7f]y\888Ť¼_\1eB&\ 6\81?Îá\97\1fhúÛK{/¼Ï¯\84·\8b^¾ñ\90\14Cx92\1dO'      Ò;\82¿I2N°\1c©5\92$KÆi\8aà¼i\96\8bÃ\1f\9c¾)     \18\8eGkbÓ\10vS\0A\83ÅÒd\9c\19F§þ\ 5o%\89t\9cÃo©]\92t:\9eÂo©\80Ô&\r\9dÖ¤\12¥ÂÒH7\8c®ãùD\\9bÂ\ fÄ,i®\ 6\0*\99\8a³t\92\ 2\84T<ͦ9Ä\87d\92\8eÃ\ 3hcã$A§\0<\ 5à9\96ÄM4\95fQ\13©4qÀx\8aõ\16àM\8a\8e³).\r³ÏÅi:M¡~L<\99\ 2 i\92\81&&  Md*\9efȤ7M\ 1ý4 E¯\ 2©I\92¡a\9cé8Å¡7\93\\9cå \17ÃÄÓi\f\8b â\1c\rÏÒ\1c\15ç\98t\1a¿H¤â$\87\1aY"N£FhJÆSÀ\1cx\15^H2À\fx!\95b\18o\9a\ 6Â(\ 6¿I¥SÐÈBc
+\b¢9Ô\8feâ)\8ea¡    xL\13)ÄG \87H\92\18\18A\91øM\86\8d\ 3oq\13I%½¸\81I\ 2\ 4ÜÂ0RK\92\ 6
+M\0´\0¡\ 4!õI¥ÓX$\80ëTZ\82\8dç\ 1M[<Ťi©)\89_\ 4Z`\82\19\14\13'\92\f©\87\95JÆY\86\18¡\89Fs¤Q\ 5-\14\99ÖS\9e\ 2V&\93\8caxt\12È¢%.P0UÐ\r\ 4\85¢\81£\0\80a)\84\ fd\ 2ä1M³è\7fè-\12\1e¡\96\14\87ÿ\ f\rÀL\98\96$f\ 4¼\0ÔÃß\1c0\9bÅo\900\ 1è\ 5`\fzN !gñ¯ =è\ 1 Pº\82\86$j\87¿P·4AâÇð&L3É »CÊÄ\90\Rn\ 2ñ¤I\18:\8c:Îby\ 5\9esHªI\10æ$C\81´²é8ä±V"\9a¸\14Í@#\rò\9a¤\116<v\80\ 5\12F\80jz\9b\1eÜH3lZk¬!ùOI\80Ì\8dé4È\94¡\11\ 6J¦HZÂ\9dJ"µXÒÅ¿°ÖJÿ\f\10i`'\90l\924Ìy\12©ªÚD\ 3\ e\02ôàq¦\f\rHY\90\$©8\9166é{qxv\96[dlòkð«±\rÐ%\91µ@\93µÔ¦ïg$~\bc\83\89\83YJ¥5´Ø
+³òË\1a)Z\eØ\ 4\ 4\ 5·QË\7f&\11ÅÒ\væ¿(Vý\8b\ 2e°n!ô\18ÓÒ°,Ú´ai#°jSG\80Æ
+¶1\95¢\rÓ§µé\aÂÉX¬Út<Áª²Ü {\v&<M\98 «m:zÍ\94Iô2\1cÖA\1dµr\v\86ÆH\9ccÐR\91´nÓ\91\96\96\19lѤ\7f\93\85Õ\85´iÓÑk¤\fQ\e^H\10\11p=Å áHäõx¹´ÔKkBË\85QÐ)Pî¤\ e\14n¢q'\8aÀdã\16I\9f\f\91²%¢0ka\rÆð\10¸\14VDD+\97\94¸\8bͪ\f\f¿*÷\19\9a\ 3¿ÃK:^\ 59Ò \j\93n¤`pÌ\7f\9dÄÞ\82u\936"3&<"ä\rèû\8dtM:|°Þ\9b)R\9a 'ú\13tÖü\12xG\ 69\87Å
+p G\81\94\94Bîd\9cB\ 4\89\e\r\17ÅJk\83¾Mß\8d4\8dY\96¯$R\7fìÉ \83\ eë\ 6\9a5\1a\89'\89\1c\v\12Ò,¥°3F\9bÛH2Åj/£FpF¸48#\86Æ\14rc8D&ø\80©\94$\v\80\ 6¯\12ÆF\ 6<\13\f¯/Q©\89\
\1c!\99M\8a¢$±\ 3g\92N\ 1 à§qiN\12Ú\14        Ä&Í\8dæ·uò\17Ã\a\98ÐÊOÒ\80\81\ 5\96Òþ®y\82\97!oXí\86ü?\86ÕºÉ\7f+ÝHÉS½\82\90wÍÄ\19\82£õï\ 1Ù°ä\13\ 4­\7fYߨ"Ö¿nÙ¨¾þ)±9Í!ÇÑ\80\8a\ 2\87\14\19_\ 3*µQ\83ª{ݲÑ\80*\85\97à\94     \15\a\9e\1eM\99\8d:¨Úë\96\8dFTà,¦\88¤\11U
+¬4\b\8b\11\95Ö¨\87ª¾nÙhD\ 5Ö\v\ 6Tà"\82L\99\8dz¨êë\96\8d:Tà\13\81YPã<0K\14\9c\19³\1c·\18\8d\1a\12Ýë\96\8dêë|\e¤2x7\1e·F½®÷Kluûè,\1e ñ*¡ÆÀÒÿÑOþË\ 3Î4xu\fþ3\ 6\83\ 6\8f\ fûÝ
+\15\f)!|\1cyÒØ1C­\86ÎHmh­#ßq\r\93·\85©\e\9c\ e.â+Ïc½3\84Ñz\85\ 1¾³)³\12Ê\8dF%\ 4[\9eJ\9aõͲQ}ݨ\84zTª¾éQ©\8d\1aTÝë\96\8d\ 6T\9aÂèQ©ú¦G¥6ê j¯[6\1a
+£C¥é\9b\ e\95Ö¨\87ª¾nÙhD¥*\8c\1e\95ªozTj£\1eªúºe£\ e\95\83\12ê1«ú¦Ç¬6jHt¯[6ª¯ï^        \91\82#\84+\95PéèB      Õ®n\94P\a×V        aY\8f³I\1a¨ hÐ19^\ 2|,\89òQJcÍÐ\ba\17ÐX3¼mÕ¦¾üi\9d¡\vÆÀ¡¢É\14\97\fy\13\8d¹Ø\1f\7fy\83<\9fït\16£\9bɼ\85úêrtØ\89`Ác5\11\v\81\1c\99¦MĪ\8d*aº·­ÚvN,¸RÒü\1a\88%@çQÒÈ@¬Ú¨\11¦½mÕ¶{b\93ñ\14\8cÄÂLB¸C\1a\89Õ\1au\84©o[µí\9eXpXÓ\1cc"\96\82\9d\e\88U\eu\84©o[µí\98X³9#!\ 2cÀ»7ÑNÄ\19\92N\9ahW\eU:uo[µ©/Û\183Z³e1Ùl\80Uc\92i:N¦QB\v~\ 5ãÁ\82ÿ\ fö-\ 6n<Ç&%c\84Í\vÊ\©d¡|,"\ 1Ì[\fB\88\14Ì6K\eú\82'ÎH\80\95¾`\89b°l&S\1c¹\ 2*o\v\15çá¨T\8aÖC\95íÛ\963\96пéM\Næ7½ÎDì\ 2\ fÑãU<KÜôZÃ\8b\16\17ôö\ 6\vù³²ÌúÛÏ\898\92\1eÉÂá\ræ»\93vï=\7fÆ¡Ý\88ÆüÏ°÷®á7\88\10é}ì¢9¥\9cçÔ´>a\8e0Þ`ÈûøàhùI"Nr´Éò+\8dFËÏàÈÖhå-Ú´\97wnù\rÄ*FÞ@¬jùUÂtVÞ¢m÷ĪvÚ@¬bä\rĪ\96_#L³ò\16mÿ\ 6b\15;­'V5òzb5˯#Lµò\16mÿ\ 6b\15;m V1ò\ 6bU˯#Lµò\16m»&ÖÞò\ehW\8c¼\81vÕò«tꬼE\9böòÿ        Ë\8f\17\1a Á\8dåWú®¶üjO\17\96_\aõÿ[þ\15\96\80¿\97\8c\91I»¿#é/\96å ôàÐÎ,H\13HTÊËJyci\97\1dí\0\12\1c\8c\9fãÔÇ$\93Ö½\8dþÒmc£¿Òd\9c`ðþ±ò*GQ*dùw´      ­ V\1ec*Õ·u4wÔü#Þ\91¦A\82H)wJB\0E¤Y­\91\84¸9\ e3\ 5\96\80eâiÚؤÛo\97\9b \86úßÓ33\ 5M\rÃÍ2\a\r\14[\80f¨!óíP\1e\1d\v4\12¡\ 4Xÿé\19\83\11\ 6Á\85\90çÛaBpGÁÍB8\1dÅwÉ\90¡Q\1f'Èr    ×¼\14ðª\ 6]]^UÕ\80ÄôÔ\90¢ÄÌ\9cÔ"ÞôâIJT\85ļ<P\ 2O-\0Ê\0\93LjqI~QªBqF~9H\ 4¨\ 5¦\UÕÕß\8d\17\0¨K.\ 5\rendstream\rendobj\r454 0 obj\r<</CreationDate(D:20090603145654-07'00')/Creator(Adobe Illustrator CS3)/ModDate(D:20090604104953-07'00')>>\rendobj\rxref\r0 459\r0000000003 65535 f\r
+0000000016 00000 n\r
+0000026058 00000 n\r
+0000000004 00000 f\r
+0000000006 00000 f\r
+0000028981 00000 n\r
+0000000007 00000 f\r
+0000000008 00000 f\r
+0000000009 00000 f\r
+0000000010 00000 f\r
+0000000011 00000 f\r
+0000000012 00000 f\r
+0000000013 00000 f\r
+0000000014 00000 f\r
+0000000018 00000 f\r
+0000026109 00000 n\r
+0000028838 00000 n\r
+0000028869 00000 n\r
+0000000019 00000 f\r
+0000000020 00000 f\r
+0000000021 00000 f\r
+0000000022 00000 f\r
+0000000026 00000 f\r
+0000026180 00000 n\r
+0000028722 00000 n\r
+0000028753 00000 n\r
+0000000027 00000 f\r
+0000000028 00000 f\r
+0000000029 00000 f\r
+0000000030 00000 f\r
+0000000031 00000 f\r
+0000000032 00000 f\r
+0000000033 00000 f\r
+0000000034 00000 f\r
+0000000035 00000 f\r
+0000000036 00000 f\r
+0000000037 00000 f\r
+0000000038 00000 f\r
+0000000039 00000 f\r
+0000000040 00000 f\r
+0000000044 00000 f\r
+0000026251 00000 n\r
+0000028606 00000 n\r
+0000028637 00000 n\r
+0000000045 00000 f\r
+0000000046 00000 f\r
+0000000047 00000 f\r
+0000000048 00000 f\r
+0000000052 00000 f\r
+0000026322 00000 n\r
+0000028490 00000 n\r
+0000028521 00000 n\r
+0000000053 00000 f\r
+0000000054 00000 f\r
+0000000055 00000 f\r
+0000000056 00000 f\r
+0000000057 00000 f\r
+0000000058 00000 f\r
+0000000059 00000 f\r
+0000000060 00000 f\r
+0000000061 00000 f\r
+0000000062 00000 f\r
+0000000063 00000 f\r
+0000000064 00000 f\r
+0000000065 00000 f\r
+0000000066 00000 f\r
+0000000070 00000 f\r
+0000026393 00000 n\r
+0000028374 00000 n\r
+0000028405 00000 n\r
+0000000071 00000 f\r
+0000000072 00000 f\r
+0000000073 00000 f\r
+0000000074 00000 f\r
+0000000078 00000 f\r
+0000026464 00000 n\r
+0000028258 00000 n\r
+0000028289 00000 n\r
+0000000079 00000 f\r
+0000000080 00000 f\r
+0000000081 00000 f\r
+0000000082 00000 f\r
+0000000083 00000 f\r
+0000000084 00000 f\r
+0000000085 00000 f\r
+0000000086 00000 f\r
+0000000087 00000 f\r
+0000000088 00000 f\r
+0000000089 00000 f\r
+0000000090 00000 f\r
+0000000091 00000 f\r
+0000000092 00000 f\r
+0000000096 00000 f\r
+0000026535 00000 n\r
+0000028142 00000 n\r
+0000028173 00000 n\r
+0000000097 00000 f\r
+0000000098 00000 f\r
+0000000099 00000 f\r
+0000000103 00000 f\r
+0000026606 00000 n\r
+0000028024 00000 n\r
+0000028056 00000 n\r
+0000000104 00000 f\r
+0000000105 00000 f\r
+0000000106 00000 f\r
+0000000107 00000 f\r
+0000000108 00000 f\r
+0000000109 00000 f\r
+0000000110 00000 f\r
+0000000111 00000 f\r
+0000000112 00000 f\r
+0000000113 00000 f\r
+0000000114 00000 f\r
+0000000115 00000 f\r
+0000000116 00000 f\r
+0000000117 00000 f\r
+0000000118 00000 f\r
+0000000122 00000 f\r
+0000026680 00000 n\r
+0000027906 00000 n\r
+0000027938 00000 n\r
+0000000123 00000 f\r
+0000000124 00000 f\r
+0000000125 00000 f\r
+0000000129 00000 f\r
+0000026754 00000 n\r
+0000027788 00000 n\r
+0000027820 00000 n\r
+0000000130 00000 f\r
+0000000131 00000 f\r
+0000000132 00000 f\r
+0000000133 00000 f\r
+0000000134 00000 f\r
+0000000135 00000 f\r
+0000000136 00000 f\r
+0000000137 00000 f\r
+0000000138 00000 f\r
+0000000139 00000 f\r
+0000000140 00000 f\r
+0000000141 00000 f\r
+0000000142 00000 f\r
+0000000143 00000 f\r
+0000000144 00000 f\r
+0000000145 00000 f\r
+0000000146 00000 f\r
+0000000147 00000 f\r
+0000000148 00000 f\r
+0000000149 00000 f\r
+0000000150 00000 f\r
+0000000151 00000 f\r
+0000000152 00000 f\r
+0000000153 00000 f\r
+0000000154 00000 f\r
+0000000155 00000 f\r
+0000000156 00000 f\r
+0000000157 00000 f\r
+0000000158 00000 f\r
+0000000159 00000 f\r
+0000000160 00000 f\r
+0000000161 00000 f\r
+0000000162 00000 f\r
+0000000163 00000 f\r
+0000000164 00000 f\r
+0000000165 00000 f\r
+0000000166 00000 f\r
+0000000167 00000 f\r
+0000000168 00000 f\r
+0000000169 00000 f\r
+0000000170 00000 f\r
+0000000171 00000 f\r
+0000000172 00000 f\r
+0000000173 00000 f\r
+0000000174 00000 f\r
+0000000175 00000 f\r
+0000000176 00000 f\r
+0000000177 00000 f\r
+0000000178 00000 f\r
+0000000179 00000 f\r
+0000000183 00000 f\r
+0000026828 00000 n\r
+0000027670 00000 n\r
+0000027702 00000 n\r
+0000000184 00000 f\r
+0000000185 00000 f\r
+0000000186 00000 f\r
+0000000187 00000 f\r
+0000000188 00000 f\r
+0000000189 00000 f\r
+0000000190 00000 f\r
+0000000191 00000 f\r
+0000000192 00000 f\r
+0000000193 00000 f\r
+0000000194 00000 f\r
+0000000195 00000 f\r
+0000000196 00000 f\r
+0000000197 00000 f\r
+0000000198 00000 f\r
+0000000199 00000 f\r
+0000000200 00000 f\r
+0000000201 00000 f\r
+0000000202 00000 f\r
+0000000203 00000 f\r
+0000000204 00000 f\r
+0000000205 00000 f\r
+0000000206 00000 f\r
+0000000207 00000 f\r
+0000000208 00000 f\r
+0000000209 00000 f\r
+0000000210 00000 f\r
+0000000211 00000 f\r
+0000000212 00000 f\r
+0000000213 00000 f\r
+0000000214 00000 f\r
+0000000215 00000 f\r
+0000000216 00000 f\r
+0000000217 00000 f\r
+0000000218 00000 f\r
+0000000219 00000 f\r
+0000000220 00000 f\r
+0000000221 00000 f\r
+0000000222 00000 f\r
+0000000223 00000 f\r
+0000000224 00000 f\r
+0000000225 00000 f\r
+0000000226 00000 f\r
+0000000227 00000 f\r
+0000000228 00000 f\r
+0000000229 00000 f\r
+0000000230 00000 f\r
+0000000231 00000 f\r
+0000000232 00000 f\r
+0000000233 00000 f\r
+0000000237 00000 f\r
+0000026902 00000 n\r
+0000027552 00000 n\r
+0000027584 00000 n\r
+0000000238 00000 f\r
+0000000239 00000 f\r
+0000000240 00000 f\r
+0000000241 00000 f\r
+0000000242 00000 f\r
+0000000243 00000 f\r
+0000000244 00000 f\r
+0000000245 00000 f\r
+0000000246 00000 f\r
+0000000247 00000 f\r
+0000000248 00000 f\r
+0000000249 00000 f\r
+0000000250 00000 f\r
+0000000251 00000 f\r
+0000000252 00000 f\r
+0000000253 00000 f\r
+0000000254 00000 f\r
+0000000255 00000 f\r
+0000000256 00000 f\r
+0000000257 00000 f\r
+0000000258 00000 f\r
+0000000259 00000 f\r
+0000000260 00000 f\r
+0000000261 00000 f\r
+0000000262 00000 f\r
+0000000263 00000 f\r
+0000000264 00000 f\r
+0000000265 00000 f\r
+0000000266 00000 f\r
+0000000267 00000 f\r
+0000000268 00000 f\r
+0000000269 00000 f\r
+0000000270 00000 f\r
+0000000271 00000 f\r
+0000000272 00000 f\r
+0000000273 00000 f\r
+0000000274 00000 f\r
+0000000275 00000 f\r
+0000000276 00000 f\r
+0000000277 00000 f\r
+0000000278 00000 f\r
+0000000279 00000 f\r
+0000000280 00000 f\r
+0000000281 00000 f\r
+0000000282 00000 f\r
+0000000283 00000 f\r
+0000000284 00000 f\r
+0000000285 00000 f\r
+0000000286 00000 f\r
+0000000287 00000 f\r
+0000000288 00000 f\r
+0000000289 00000 f\r
+0000000290 00000 f\r
+0000000291 00000 f\r
+0000000295 00000 f\r
+0000026976 00000 n\r
+0000027434 00000 n\r
+0000027466 00000 n\r
+0000000296 00000 f\r
+0000000297 00000 f\r
+0000000298 00000 f\r
+0000000299 00000 f\r
+0000000300 00000 f\r
+0000000301 00000 f\r
+0000000302 00000 f\r
+0000000303 00000 f\r
+0000000304 00000 f\r
+0000000305 00000 f\r
+0000000306 00000 f\r
+0000000307 00000 f\r
+0000000308 00000 f\r
+0000000309 00000 f\r
+0000000310 00000 f\r
+0000000311 00000 f\r
+0000000312 00000 f\r
+0000000313 00000 f\r
+0000000314 00000 f\r
+0000000315 00000 f\r
+0000000316 00000 f\r
+0000000317 00000 f\r
+0000000318 00000 f\r
+0000000319 00000 f\r
+0000000320 00000 f\r
+0000000321 00000 f\r
+0000000322 00000 f\r
+0000000323 00000 f\r
+0000000324 00000 f\r
+0000000325 00000 f\r
+0000000326 00000 f\r
+0000000327 00000 f\r
+0000000328 00000 f\r
+0000000329 00000 f\r
+0000000330 00000 f\r
+0000000331 00000 f\r
+0000000332 00000 f\r
+0000000333 00000 f\r
+0000000334 00000 f\r
+0000000335 00000 f\r
+0000000336 00000 f\r
+0000000337 00000 f\r
+0000000338 00000 f\r
+0000000339 00000 f\r
+0000000340 00000 f\r
+0000000341 00000 f\r
+0000000342 00000 f\r
+0000000343 00000 f\r
+0000000344 00000 f\r
+0000000345 00000 f\r
+0000000346 00000 f\r
+0000000347 00000 f\r
+0000000348 00000 f\r
+0000000349 00000 f\r
+0000000353 00001 f\r
+0000027050 00000 n\r
+0000027316 00000 n\r
+0000027348 00000 n\r
+0000000354 00000 f\r
+0000000355 00000 f\r
+0000000356 00000 f\r
+0000000357 00000 f\r
+0000000358 00000 f\r
+0000000359 00000 f\r
+0000000360 00000 f\r
+0000000361 00000 f\r
+0000000362 00000 f\r
+0000000363 00000 f\r
+0000000364 00000 f\r
+0000000365 00000 f\r
+0000000366 00000 f\r
+0000000367 00000 f\r
+0000000368 00000 f\r
+0000000369 00000 f\r
+0000000370 00000 f\r
+0000000371 00000 f\r
+0000000372 00000 f\r
+0000000373 00000 f\r
+0000000374 00000 f\r
+0000000375 00000 f\r
+0000000376 00000 f\r
+0000000377 00000 f\r
+0000000378 00000 f\r
+0000000379 00000 f\r
+0000000380 00000 f\r
+0000000381 00000 f\r
+0000000382 00000 f\r
+0000000383 00000 f\r
+0000000384 00000 f\r
+0000000385 00000 f\r
+0000000386 00000 f\r
+0000000387 00000 f\r
+0000000388 00000 f\r
+0000000389 00000 f\r
+0000000390 00000 f\r
+0000000391 00000 f\r
+0000000392 00000 f\r
+0000000393 00000 f\r
+0000000394 00000 f\r
+0000000395 00000 f\r
+0000000396 00000 f\r
+0000000397 00001 f\r
+0000000398 00000 f\r
+0000000399 00000 f\r
+0000000400 00000 f\r
+0000000406 00000 f\r
+0000034768 00000 n\r
+0000034844 00000 n\r
+0000035022 00000 n\r
+0000035933 00000 n\r
+0000052842 00000 n\r
+0000000411 00001 f\r
+0000028954 00000 n\r
+0000027124 00000 n\r
+0000027198 00000 n\r
+0000027230 00000 n\r
+0000000412 00001 f\r
+0000000413 00001 f\r
+0000000415 00001 f\r
+0000034140 00000 n\r
+0000000417 00001 f\r
+0000031078 00000 n\r
+0000000426 00001 f\r
+0000032832 00000 n\r
+0000032895 00000 n\r
+0000033125 00000 n\r
+0000033180 00000 n\r
+0000033818 00000 n\r
+0000033964 00000 n\r
+0000034061 00000 n\r
+0000031306 00000 n\r
+0000000427 00001 f\r
+0000000429 00001 f\r
+0000033754 00000 n\r
+0000000430 00001 f\r
+0000000432 00001 f\r
+0000031673 00000 n\r
+0000000433 00001 f\r
+0000000435 00001 f\r
+0000033690 00000 n\r
+0000000436 00001 f\r
+0000000442 00001 f\r
+0000033263 00000 n\r
+0000033409 00000 n\r
+0000033530 00000 n\r
+0000033611 00000 n\r
+0000032040 00000 n\r
+0000000443 00001 f\r
+0000000445 00001 f\r
+0000032768 00000 n\r
+0000000446 00001 f\r
+0000000449 00001 f\r
+0000032404 00000 n\r
+0000031192 00000 n\r
+0000000450 00001 f\r
+0000000451 00001 f\r
+0000000000 00001 f\r
+0000029401 00000 n\r
+0000030659 00000 n\r
+0000076660 00000 n\r
+0000034204 00000 n\r
+0000034254 00000 n\r
+0000030726 00000 n\r
+0000000367 00000 n\r
+trailer\r<</Size 459/Root 1 0 R/Info 454 0 R/ID[<A3DACB2CAA994ADF839A177E1EF222F1><94FD791731084F1087F9FE9BDDF38204>]>>\rstartxref\r76784\r%%EOF\r
\ No newline at end of file
diff --git a/doc/gfx_and_css/logo.pdf b/doc/gfx_and_css/logo.pdf
new file mode 100644 (file)
index 0000000..593dca4
--- /dev/null
@@ -0,0 +1,201 @@
+%PDF-1.4\r%âãÏÓ\r
+1 0 obj\r<</Metadata 50 0 R/Pages 2 0 R/Type/Catalog>>\rendobj\r50 0 obj\r<</Subtype/XML/Length 26031/Type/Metadata>>stream\r
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.277092, Fri Feb 23 2007 14:16:18        ">
+   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+      <rdf:Description rdf:about=""
+            xmlns:dc="http://purl.org/dc/elements/1.1/">
+         <dc:format>application/pdf</dc:format>
+         <dc:title>
+            <rdf:Alt>
+               <rdf:li xml:lang="x-default">Netatalk_Logo</rdf:li>
+            </rdf:Alt>
+         </dc:title>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xap="http://ns.adobe.com/xap/1.0/"
+            xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/">
+         <xap:CreatorTool>Adobe Illustrator CS3</xap:CreatorTool>
+         <xap:CreateDate>2009-06-04T10:50:16-07:00</xap:CreateDate>
+         <xap:ModifyDate>2009-06-04T10:50:16-07:00</xap:ModifyDate>
+         <xap:MetadataDate>2009-06-04T10:50:16-07:00</xap:MetadataDate>
+         <xap:Thumbnails>
+            <rdf:Alt>
+               <rdf:li rdf:parseType="Resource">
+                  <xapGImg:width>256</xapGImg:width>
+                  <xapGImg:height>256</xapGImg:height>
+                  <xapGImg:format>JPEG</xapGImg:format>
+                  <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW&#xA;du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT&#xA;l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj&#xA;PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9&#xA;Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A&#xA;VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ&#xA;IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8&#xA;Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man&#xA;9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd&#xA;II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8&#xA;X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l&#xA;jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM&#xA;VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN&#xA;ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj&#xA;F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk&#xA;Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9&#xA;/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8&#xA;1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd&#xA;WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG&#xA;D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui&#xA;rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+&#xA;bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3&#xA;Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd&#xA;YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8&#xA;TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P&#xA;gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA&#xA;dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu&#xA;w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK&#xA;s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma&#xA;hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t&#xA;dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q&#xA;7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz&#xA;fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx&#xA;aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz&#xA;KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr&#xA;G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5&#xA;5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu&#xA;DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE&#xA;dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0&#xA;b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG&#xA;syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW&#xA;8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M&#xA;aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz&#xA;DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj&#xA;irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx&#xA;V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF&#xA;PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE&#xA;v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI&#xA;qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2&#xA;KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ&#xA;vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp&#xA;xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C&#xA;81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5&#xA;Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd&#xA;+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/&#xA;ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX&#xA;UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/&#xA;AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k&#xA;9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx&#xA;Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4&#xA;o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa&#xA;KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ&#xA;/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW&#xA;o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf&#xA;lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1&#xA;Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na&#xA;SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc&#xA;Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0&#xA;4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg&#xA;k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr&#xA;+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD&#xA;6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM&#xA;nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn&#xA;v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu&#xA;/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL&#xA;79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB&#xA;1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk&#xA;rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw&#xA;zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf&#xA;/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt&#xA;iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+&#xA;r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb&#xA;Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L&#xA;dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te&#xA;SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1&#xA;Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/&#xA;AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8&#xA;/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o&#xA;pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW&#xA;+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2&#xA;5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s&#xA;8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC&#xA;ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/&#xA;8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p&#xA;8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq&#xA;wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A&#xA;Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu&#xA;+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA&#xA;8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h&#xA;f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7&#xA;uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK&#xA;t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n&#xA;cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2&#xA;7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX&#xA;2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX&#xA;u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z&#xA;9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc&#xA;8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba&#xA;qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69&#xA;piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH&#xA;/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq&#xA;ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK&#xA;se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/&#xA;1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A&#xA;kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd&#xA;5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW&#xA;H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2&#xA;vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd&#xA;5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn&#xA;/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV&#xA;fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5&#xA;x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13&#xA;y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A&#xA;bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/&#xA;/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4&#xA;NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x&#xA;i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM&#xA;AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY&#xA;q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp&#xA;flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L&#xA;wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+&#xA;RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP&#xA;OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV&#xA;fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q&#xA;8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT&#xA;W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE&#xA;tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo&#xA;6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+&#xA;Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A&#xA;yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5&#xA;OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn&#xA;/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6&#xA;ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5&#xA;f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+&#xA;f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7&#xA;A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY&#xA;UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q&#xA;pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp&#xA;HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf&#xA;9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv&#xA;y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380&#xA;tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl&#xA;djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK&#xA;on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr&#xA;9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/&#xA;HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn&#xA;fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV&#xA;2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8&#xA;66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A&#xA;oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3&#xA;/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj&#xA;8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7&#xA;UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm&#xA;Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h&#xA;wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV&#xA;REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/&#xA;8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl&#xA;zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH&#xA;NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/&#xA;AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq&#xA;2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0&#xA;r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr&#xA;Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/&#xA;AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd&#xA;kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN&#xA;/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/&#xA;AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q&#xA;ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl&#xA;pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx&#xA;+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5&#xA;oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl&#xA;pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+&#xA;kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8&#xA;j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM&#xA;akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI&#xA;rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib&#xA;r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr&#xA;p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE&#xA;v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d&#xA;FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN&#xA;Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV&#xA;dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0&#xA;NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a&#xA;1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA&#xA;6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+&#xA;fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8&#xA;Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N&#xA;j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q&#xA;4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF&#xA;7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu&#xA;IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/&#xA;zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK&#xA;u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/&#xA;AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3&#xA;/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE&#xA;gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+&#xA;g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m&#xA;aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN&#xA;jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX&#xA;Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81&#xA;a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W&#xA;O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ&#xA;hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV&#xA;vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS&#xA;7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj&#xA;odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN&#xA;B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH&#xA;cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK&#xA;UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj&#xA;/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f&#xA;aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9&#xA;WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L&#xA;Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB&#xA;V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G&#xA;1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7&#xA;+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/&#xA;u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq&#xA;+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq&#xA;eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+&#xA;cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G&#xA;Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz&#xA;svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35&#xA;V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv&#xA;JvlTyvam18vaTa6XCQA/1aJUZ6dDI4HNz7sTiqc4q7FXYq7FXYq7FXYq7FXYq7FX/9k=</xapGImg:image>
+               </rdf:li>
+            </rdf:Alt>
+         </xap:Thumbnails>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"
+            xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#">
+         <xapMM:DocumentID>uuid:94E82CBF528711DE830DC7B65C12678A</xapMM:DocumentID>
+         <xapMM:InstanceID>uuid:a8158099-b802-4d00-acfd-b4ab575a94be</xapMM:InstanceID>
+         <xapMM:DerivedFrom rdf:parseType="Resource">
+            <stRef:instanceID>uuid:94E82CBE528711DE830DC7B65C12678A</stRef:instanceID>
+            <stRef:documentID>uuid:1B95FEC551E111DEA13AAE68EE5F3A4D</stRef:documentID>
+         </xapMM:DerivedFrom>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+            xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+            xmlns:xapG="http://ns.adobe.com/xap/1.0/g/">
+         <xapTPg:NPages>1</xapTPg:NPages>
+         <xapTPg:HasVisibleTransparency>True</xapTPg:HasVisibleTransparency>
+         <xapTPg:HasVisibleOverprint>False</xapTPg:HasVisibleOverprint>
+         <xapTPg:MaxPageSize rdf:parseType="Resource">
+            <stDim:w>3.000000</stDim:w>
+            <stDim:h>3.000000</stDim:h>
+            <stDim:unit>Inches</stDim:unit>
+         </xapTPg:MaxPageSize>
+         <xapTPg:PlateNames>
+            <rdf:Seq>
+               <rdf:li>Black</rdf:li>
+            </rdf:Seq>
+         </xapTPg:PlateNames>
+         <xapTPg:SwatchGroups>
+            <rdf:Seq>
+               <rdf:li rdf:parseType="Resource">
+                  <xapG:groupName>Default Swatch Group</xapG:groupName>
+                  <xapG:groupType>0</xapG:groupType>
+               </rdf:li>
+            </rdf:Seq>
+         </xapTPg:SwatchGroups>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+         <pdf:Producer>Adobe PDF library 8.00</pdf:Producer>
+      </rdf:Description>
+   </rdf:RDF>
+</x:xmpmeta>
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                           
+<?xpacket end="w"?>\rendstream\rendobj\r2 0 obj\r<</Count 1/Type/Pages/Kids[5 0 R]>>\rendobj\r5 0 obj\r<</Parent 2 0 R/Contents 47 0 R/BleedBox[0.0 0.0 216.0 216.0]/ArtBox[16.0 18.25 200.0 202.25]/Group 48 0 R/MediaBox[0.0 0.0 216.0 216.0]/TrimBox[0.0 0.0 216.0 216.0]/Resources<</XObject<</Fm0 20 0 R/Fm1 26 0 R/Fm2 36 0 R/Fm3 42 0 R>>/Properties<</MC0<</Color[65535 20224 20224]/Visible true/Editable true/Dimmed false/Preview true/Printed true/Title(Layer 2)>>>>/ExtGState<</GS0 11 0 R/GS1 43 0 R>>>>/Type/Page>>\rendobj\r47 0 obj\r<</Length 1191/Filter/FlateDecode>>stream\r
+H\89\94\8eÝ6\fÝû+ô\ 3Ö\88¢\1eÔ¶IÛM³\bºèº\18$-ÐL\8bI\81\ 2ùû\1eR¶,ûÞ;E1\98±)¾\ e_2çé§_¿}úê\9e\vî»÷ïÜòº\ 4\17©Øïª\7f¾~Z~q\7fâÔ~|åìþX\9e~ü9¸ßþ^^\1dÙ99
+â¨\89\17\ eä\9e_LþeY\93ø\16Uf\15ñ¹6·ró%ËAëSÜó2\ e\88«Ï0²ë®T«§ÜÔÈþú¼\\99Wí«ù\8bû\81K\7f\9e\97ÏËÇ-BBtST¾&\8e®D/UÚ\14\19ù\84\90\93\8f\11\89*\90* rV\ 4F\14Ï\924.
+¾      ;*>\17\0h8o\8e\19T\85pó"Ñ¥âc5áê\13Þrö\89\18\ 6Cv\82¿A\91'_[r\ 4{A3°rð9%'\b\11yÈ®
+t`8±'©®Àk²ôæ\80\83âjô­"o¹y\ 2Ê\9a})\11\ 1ÀHr\9aË\14U\1a´Ä¬\a\11.Ö"\9e\82ñ#\8c*ÉE\94d&\15¯Á\97Äz ¶Õ\a\99²\14\aÕNæ.\8d yâ7_CÝ\95\85\90.\9alK\ 4\862|\83Ì1\rd »ô\ 6¼A\9dDãJAó\e\82\1a\0jò@ÃwcW\92çjæ)"qªÑk¸\12J\12\11KSE£\93oРBT8P\95\1cÌ\b|\17ëA-\rTrÚè\98\8c\9fòÐàbFS0· Åh\12\930\v\82ºË@\95È\ eP>Ö0Р­FÌ_й$´/c\86Ð}\9b4R(ÚíH¬ñ{Ô@â\ 5Ãðm±£Xó8úb\8d\19j\9a\8c¹L\a½Ç×ÞäÚ÷ÿL\93\12N\93\121Whè\86\19\8f1   \8eÉ£\91¾,\ 5W\ 3\ f\8aì\89n?\88ÁB\a\v_É ¯\98$ªöZÌ\e¥ñª\87¿\eº\ 3SF/d\81\9b\98Íù@\85\11eäÂlöWL0\95\ 2\1aÞR¼!\91\11\ 5\ f\ ed;@a5gWºÇ¸Eò\85Ø\ 3\rÇë5\10\fTÎé^v\11Y\10Í\ 2\8a\fWzd\11Í\9bò\95\1cÒ¢Çgê^B   \ 5\ 3ë\ 1\fînµDWzö{K\ eiÜ2å\86¼\v$!#øædÓ\9f/eíª\88«Ã\9cÇ©50ÈFé\939©4®\ 2hcÞ\e\93©\r²©­]\1aå±\16\9aieg_s÷¤¦\ 6ÉgÝ\eø\99\røãi9Ï@§Ê)igên\92
\19þÿ\9f\17į¡¨å£N:Üå \93\86Ù{Á\98\9a°Ð\aYS\18\ e\95â\18â\e\94¸¡©\84èìâ\8dq.'¾¤¡\ f\ 5%\f[\99\ e´¬(\9b~\82#\YUõ.n\ eµ\95~;Ô2\b´K\1e¢V5ª<Ñè\10]!ô»T{ófû\88\8c\83\8b\81\1e\v\82/2q61|ZbÃäáº\15Ý\9e>/\r\8c&C\0V
+>¦\93\80æ@«8$Ø\17|þO\12\v\f\ 1Ât׳@Ñì\1d\12°ØN\12¯ºµ\91nmO?¼\ 4÷þ¯\19:V       äé\rè]à-è\9bÄcè\9bÀ\eÐ7\89·¡S\87N\rÝ\81*`=j\ 1\1a\14"«ÿXë\ 6\1e_E`Ù$\14z>ó\11}\eü\ eüÄ',i´ó;î\99/ØåÚÎî gö     u<£ÆvAú\81~\8cz\93x\88zã?D½ñ\1f¡ÞØÿ\81\9a;껫±-üÀ¢ËÓ´ðcci}áÇ`\ 2Ç\8a\85¼Ö\83´§­jû\89Ö½aûÜuWÂ~Y#Û¿½báWf<N®Ú7öÏþ7õ}áWK\9bä\8b\11:7\84\r\v«\9d\90ý_Q\8c\e\8e×¾DËÄ\gÍu²©\8b((Ýò3\9e]­ú\82¥-lÏçesÕÉIüdG/\98ï?à\1f³\8fË¿\ 2\f\0~ĨÇ\rendstream\rendobj\r48 0 obj\r<</I false/K false/CS/DeviceCMYK/S/Transparency>>\rendobj\r11 0 obj\r<</OPM 1/BM/Normal/CA 1.0/OP false/SMask/None/ca 1.0/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r43 0 obj\r<</OPM 1/BM/Normal/CA 0.5/OP false/SMask/None/ca 0.5/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r20 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 9 0 R/Resources<</Shading<</Sh0 17 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[106.997 139.145 150.43 135.867]>>stream\r
+q
+150.43 135.867 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 137.5058594 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r26 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 23 0 R/Resources<</Shading<</Sh0 17 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[106.997 133.854 150.43 130.576]>>stream\r
+q
+150.43 130.576 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 132.2148438 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r36 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 29 0 R/Resources<</Shading<</Sh0 32 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[39.6411 84.9033 88.6143 81.626]>>stream\r
+q
+39.641 84.903 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 83.2646484 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r42 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 39 0 R/Resources<</Shading<</Sh0 32 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[39.6411 90.1943 88.6143 86.917]>>stream\r
+q
+39.641 90.194 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 88.5556641 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r39 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r13 0 obj\r[/DeviceN[/Black]/DeviceCMYK 14 0 R 15 0 R]\rendobj\r14 0 obj\r<</Length 91/FunctionType 4/Filter/FlateDecode/Domain[0.0 1.0]/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>stream\r
+H\89ª6Ô3\0\ 3\ 5#\ 5C\85¢ü\9c\1c\ 5bD\f\142óRR+\102\ÉeE
\15É\19
+Å¥I\b
+ºèf (4\ 5\ 2Ë\9b\10Pi\ 2\18S!ªåÆ0\95F\ 4\8c4\82[^\90_ P\v\10`\0.§G±\rendstream\rendobj\r15 0 obj\r<</Subtype/NChannel/Process 16 0 R>>\rendobj\r16 0 obj\r<</Components[/Cyan/Magenta/Yellow/Black]/ColorSpace/DeviceCMYK>>\rendobj\r32 0 obj\r<</ColorSpace 13 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 33 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r33 0 obj\r<</FunctionType 3/Encode[1.0 0.0 0.0 1.0]/Domain[0.0 1.0]/Functions[34 0 R 35 0 R]/Bounds[0.967026]>>\rendobj\r34 0 obj\r<</C0[0.735]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r35 0 obj\r<</C0[0.735]/C1[0.735]/FunctionType 2/N 1.0/Domain[0.0 1.0]>>\rendobj\r29 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r23 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r17 0 obj\r<</ColorSpace 13 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 18 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r18 0 obj\r<</FunctionType 3/Encode[1.0 0.0]/Domain[0.0 1.0]/Functions[19 0 R]/Bounds[]>>\rendobj\r19 0 obj\r<</C0[1.0]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r9 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r49 0 obj\r<</CreationDate(D:20090604105016-07'00')/Creator(Adobe Illustrator CS3)/Producer(Adobe PDF library 8.00)/ModDate(D:20090604105016-07'00')/Title(Netatalk_Logo)>>\rendobj\rxref\r0 51\r0000000003 65535 f\r
+0000000016 00000 n\r
+0000026186 00000 n\r
+0000000004 00001 f\r
+0000000006 00000 f\r
+0000026237 00000 n\r
+0000000007 00001 f\r
+0000000008 00001 f\r
+0000000010 00001 f\r
+0000031007 00000 n\r
+0000000012 00001 f\r
+0000027992 00000 n\r
+0000000021 00001 f\r
+0000029722 00000 n\r
+0000029782 00000 n\r
+0000030011 00000 n\r
+0000030064 00000 n\r
+0000030691 00000 n\r
+0000030834 00000 n\r
+0000030929 00000 n\r
+0000028218 00000 n\r
+0000000022 00001 f\r
+0000000024 00001 f\r
+0000030628 00000 n\r
+0000000025 00001 f\r
+0000000027 00001 f\r
+0000028579 00000 n\r
+0000000028 00001 f\r
+0000000030 00001 f\r
+0000030565 00000 n\r
+0000000031 00001 f\r
+0000000037 00001 f\r
+0000030146 00000 n\r
+0000030289 00000 n\r
+0000030407 00000 n\r
+0000030487 00000 n\r
+0000028941 00000 n\r
+0000000038 00001 f\r
+0000000040 00001 f\r
+0000029659 00000 n\r
+0000000041 00001 f\r
+0000000044 00001 f\r
+0000029300 00000 n\r
+0000028105 00000 n\r
+0000000045 00001 f\r
+0000000046 00001 f\r
+0000000000 00001 f\r
+0000026665 00000 n\r
+0000027926 00000 n\r
+0000031069 00000 n\r
+0000000077 00000 n\r
+trailer\r<</Size 51/Root 1 0 R/Info 49 0 R/ID[<8382E707CCB44A8FBBDD373DCFC0BC17><2861F2D7C5014E0FA17536D73E45D784>]>>\rstartxref\r31246\r%%EOF\r
\ No newline at end of file
diff --git a/doc/gfx_and_css/logo.png b/doc/gfx_and_css/logo.png
new file mode 100644 (file)
index 0000000..8138256
Binary files /dev/null and b/doc/gfx_and_css/logo.png differ
diff --git a/doc/gfx_and_css/netatalk.css b/doc/gfx_and_css/netatalk.css
new file mode 100644 (file)
index 0000000..8096a61
--- /dev/null
@@ -0,0 +1,251 @@
+BODY {
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       background-color: white;
+       font-size: 1em;
+       margin-left: 15px;
+       margin-right: 15px;
+}
+
+.pdparam{
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       font-size: 12px;
+}
+
+TD {
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       font-size: 12px;
+}
+
+H1, H2, H3 {
+       font-size: 130%;
+       padding: 2px;
+       margin-top: 0px;
+}
+
+H1 {
+       background-color: #424242;
+       color: #FFFFFF;
+}
+
+H2 {
+       background-color: #424242;
+       color: #FFFFFF;
+       text-decoration: none;
+}
+
+H3 {
+       background-color: #330000;
+       color: #FFFFFF;
+}
+
+H4 {
+       color: #330000;
+       font-size: 120%;
+}
+
+H5 {
+       font-size: 100%;
+       color: #330000;
+}
+
+.term {
+       color: black;
+}
+
+TR.qandadiv TD {
+       padding-top: 1em;
+}
+
+DIV.navheader {
+       font-size: 80%;
+       margin-top: 15px;
+}
+
+div.titlepage {
+       margin-top: 15px;
+}
+
+DIV.navheader th{
+       font-size: 100%;
+}
+
+DIV.navfooter {
+       font-size: 80%;
+}
+
+A:link {
+       color: #660000;
+}
+
+A:visited {
+       color: #CC0000;
+}
+
+A:active {
+       color: #FF0033;
+}
+
+TR.question {
+       color: #33C;
+       font-weight: bold;
+}
+
+TR.question TD {
+       padding-top: 1em;
+}
+
+DIV.variablelist {
+       padding-left: 2em;
+       color: #33C;
+}
+
+P {
+       color: black;
+}
+
+DIV.caution, DIV.tip, DIV.important {
+       border: dashed 1px;
+       background-color: #EEEEFF;
+       width: 60em;
+       padding: 5px;
+}
+
+PRE.programlisting, PRE.screen {
+       border: #630 1px dashed;
+       color: #993300;
+       padding: 2px;
+       font-size: 120%;
+}
+DIV.warning { 
+       border: dashed 1px;
+       background-color: #BFBFBF;
+       width: 60em;
+       padding: 5px;
+} 
+
+DIV.note { 
+       border: dashed 1px;
+       background-color: #f2f2f2;
+       width: 60em;
+       padding: 5px;
+} 
+
+DIV.author {
+       padding-top: 1em;
+}
+
+DIV.revhistory table{
+       font-size: 12px;
+       border-collapse: collapse;
+       border-spacing: 0;
+       border: 0px;
+}
+
+DIV.revhistory tr {
+       border: 0px;
+}
+
+DIV.revhistory td {
+       border: 0px;
+}
+
+DIV.revhistory th {
+       border: 0px;
+}
+
+div.book, div.chapter, div.refentry {
+       font-size: 12px;
+}
+
+div.indexdiv {
+       font-size: 12px;
+}
+
+tt {
+       font-size: 12px;
+}
+
+/**
+ * netatalk
+ */
+/* definitions for the header */
+div#header {
+    margin-top: 15px;
+    margin-left: -15px;
+    margin-right: -15px;
+    height: 109px;
+    white-space: nowrap;
+    background-color: #424242;
+    background-image: url(/gfx/bg.gif);
+    background-repeat: no-repeat;
+    background-position: 380px 0px;
+}
+
+div#logo {
+       position: absolute;
+       margin-top: 0px;
+       margin-left: 25px;
+       width: 225px;
+       height: 109px;
+       padding: 0px;
+       background-image: url(/gfx/netatalklogo.gif);
+       background-repeat: no-repeat;
+}
+
+#logo img {
+    border: 0px;
+}
+
+div#menlinks {
+       position: absolute;
+       left: 272px;
+       top: 102px;
+       font-family: Geneva, Arial, Helvetica, sans-serif;
+       font-size: 12px;
+       font-weight: normal;
+       color: #FFFFFF;
+       text-decoration: none;
+       white-space: nowrap;
+
+}
+
+div#menlinks a, div#menlinks a:visited {
+    color: #FFFFFF;
+    margin-right: 15px;
+    text-decoration: none;
+}
+
+#menlinks img {
+   border: 0px;
+}
+
+.italic {
+        font-family: Geneva, Arial, Helvetica, sans-serif;
+        font-size: 11px;
+        font-style: italic;
+        font-weight: normal;
+        color: #000000;
+        text-decoration: none;
+}
+
+a, a:visited {
+  text-decoration: none;
+  color: #660000;
+}
+
+/* definitions for the footer */
+div.footer {
+       margin-left: 210px;
+       margin-top: 22px;
+       font-size: 75%;
+       width: 145px;
+       text-align: left;
+       white-space: nowrap;
+}
+
+.footer img {
+   padding-right: 5px;
+}
diff --git a/doc/html.xsl.in b/doc/html.xsl.in
new file mode 100644 (file)
index 0000000..b2fbfd3
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version='1.0'?> 
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
+       <xsl:import href="@DOCBOOK_ROOT@/xhtml/chunk.xsl"/> 
+       <xsl:param name="use.id.as.filename" select="'1'"/>
+       <xsl:param name="chunk.section.depth" select="0"></xsl:param>
+       <xsl:param name="chunk.separate.lots" select="1"></xsl:param>
+       <xsl:param name="html.stylesheet" select="'http://netatalk.sourceforge.net/css/netatalk.css'"/>
+
+       <xsl:template name="user.header.navigation">
+               <xsl:variable name="codefile" select="document('netatalk.html',/)"/>
+               <xsl:copy-of select="$codefile/*/node()"/>
+       </xsl:template>
+</xsl:stylesheet>
diff --git a/doc/man.xsl.in b/doc/man.xsl.in
new file mode 100644 (file)
index 0000000..fca9fec
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version='1.0'?> 
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
+<xsl:import href="@DOCBOOK_ROOT@/manpages/docbook.xsl"/> 
+
+<!-- * Collect date from <refmiscinfo class="date"> -->
+<xsl:param name="refentry.date.profile.enabled">1</xsl:param>
+<xsl:param name="refentry.date.profile">
+  (//refmiscinfo[@class='date'])[last()]
+</xsl:param>
+
+<!-- * Suppress extra :VERSION: -->
+<xsl:param name="refentry.version.suppress">1</xsl:param>
+
+<!-- * Example without numbering -->
+<xsl:param name="local.l10n.xml" select="document('')"/>
+<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
+<l:l10n language="en">
+ <l:context name="title">
+    <l:template name="example" text="Example.&#160;%t"/>
+ </l:context>
+ <l:context name="title-numbered">
+    <l:template name="example" text="Example.&#160;%t"/>
+ </l:context>
+</l:l10n>
+</l:i18n>
+</xsl:stylesheet>
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
new file mode 100644 (file)
index 0000000..4bc3be5
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = man1 man5 man8
\ No newline at end of file
diff --git a/doc/manpages/man1/.gitignore b/doc/manpages/man1/.gitignore
new file mode 100644 (file)
index 0000000..7943d1d
--- /dev/null
@@ -0,0 +1 @@
+*\.1
\ No newline at end of file
diff --git a/doc/manpages/man1/Makefile.am b/doc/manpages/man1/Makefile.am
new file mode 100644 (file)
index 0000000..fbb1848
--- /dev/null
@@ -0,0 +1,40 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       ad.1 \
+       afpldaptest.1 \
+       afppasswd.1 \
+       afpstats.1 \
+       apple_dump.1 \
+       asip-status.pl.1 \
+       dbd.1 \
+       macusers.1 \
+       megatron.1 \
+       netatalk-config.1 \
+       uniconv.1
+
+EXTRA_DIST = \
+       ad.1.xml \
+       afpldaptest.1.xml \
+       afppasswd.1.xml \
+       afpstats.1.xml \
+       apple_dump.1.xml \
+       asip-status.pl.1.xml \
+       dbd.1.xml \
+       macusers.1.xml \
+       megatron.1.xml \
+       netatalk-config.1.xml \
+       uniconv.1.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.1 : %.1.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man1/$@.in
+
+html-local: $(MAN_MANPAGES) $(MAN_STYLESHEET)
+endif
diff --git a/doc/manpages/man1/ad.1.xml b/doc/manpages/man1/ad.1.xml
new file mode 100644 (file)
index 0000000..b6a9f55
--- /dev/null
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="ad.1">
+  <refmeta>
+    <refentrytitle>ad</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">02 Sep 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>ad</refname>
+
+    <refpurpose>Netatalk compatible UNIX file utility suite.</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>ad</command>
+
+      <arg choice="req">ls | cp | mv | rm</arg>
+
+      <arg>...</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad</command>
+
+      <arg choice="req">-v | --version</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>ad</command> is a UNIX file utility suite with Netatalk
+    compatibility. AppleDouble<indexterm>
+        <primary>AppleDouble</primary>
+      </indexterm> files in <filename>.AppleDouble</filename> directories and
+    the CNID databases are updated as appropriate.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Available Commands</title>
+
+    <cmdsynopsis>
+      <command>ad ls</command>
+
+      <arg>-dRl<arg>u</arg></arg>
+
+      <arg choice="req">file|dir <arg>...</arg></arg>
+    </cmdsynopsis>
+
+    <para>List files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad cp</command>
+
+      <arg choice="opt">-aipvf</arg>
+
+      <arg choice="req">src_file</arg>
+
+      <arg choice="req">dst_file</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad cp -R</command>
+
+      <arg choice="opt">-aipvf</arg>
+
+      <arg choice="req">src_file|src_directory ...</arg>
+
+      <arg choice="req">dst_directory</arg>
+    </cmdsynopsis>
+
+    <para>Copy files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad mv</command>
+
+      <arg choice="opt">-finv</arg>
+
+      <arg choice="req">src_file</arg>
+
+      <arg choice="req">dst_file</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad mv</command>
+
+      <arg choice="opt">-finv</arg>
+
+      <arg choice="req">src_file|src_directory ...</arg>
+
+      <arg choice="req">dst_directory</arg>
+    </cmdsynopsis>
+
+    <para>Move files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad rm</command>
+
+      <arg choice="opt">-Rv</arg>
+
+      <arg choice="req">file|directory</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad -v|--version</command>
+    </cmdsynopsis>
+
+    <para>Show version.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>ad ls</title>
+
+    <para>List files and directories. Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>Directories are listed as plain files</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>list subdirectories recursively</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-l</term>
+
+        <listitem>
+          <para>Long output, list AFP info</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-u</term>
+
+        <listitem>
+          <para>List UNIX info</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para><emphasis>Long output description</emphasis></para>
+
+    <programlisting>&lt;unixinfo&gt; &lt;FinderFlags&gt; &lt;AFP Attributes&gt; &lt;Color&gt; &lt;Type&gt; &lt;Creator&gt; &lt;CNID from AppleDouble&gt; &lt;name&gt;
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
+
+  d = On Desktop                      (f/d)
+  e = Hidden extension                (f/d)
+  m = Shared (can run multiple times) (f)
+  n = No INIT resources               (f)
+  i = Inited                          (f/d)
+  c = Custom icon                     (f/d)
+  t = Stationery                      (f)
+  s = Name locked                     (f/d)
+  b = Bundle                          (f/d)
+  v = Invisible                       (f/d)
+  a = Alias file                      (f/d)
+
+AFP Attributes:
+
+  y = System                          (f/d)
+  w = No write                        (f)
+  p = Needs backup                    (f/d)
+  r = No rename                       (f/d)
+  l = No delete                       (f/d)
+  o = No copy                         (f)
+
+Note: any letter appearing in uppercase means the flag is set but it's a directory for which the flag is not allowed.</programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>ad cp</title>
+
+    <para>Copy files and directories.</para>
+
+    <para>In the first synopsis form, the cp utility copies the contents of
+    the source_file to the target_file. In the second synopsis form, the
+    contents of each named source_file is copied to the destination
+    target_directory. The names of the files themselves are not changed. If cp
+    detects an attempt to copy a file to itself, the copy will fail.</para>
+
+    <para>Netatalk AFP volumes are detected by means of their ".AppleDesktop"
+    directory which is located in their volume root. When a copy targeting an
+    AFP volume is detected, its CNID database daemon is connected and all
+    copies will also go through the CNID database. AppleDouble files are also
+    copied and created as needed when the target is an AFP volume.</para>
+
+    <para>Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-a</term>
+
+        <listitem>
+          <para>Archive mode. Same as -Rp.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>For each existing destination pathname, remove it and create a
+          new file, without prompting for confirmation regardless of its
+          permis- sions. (The -f option overrides any previous -i or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-i</term>
+
+        <listitem>
+          <para>Cause cp to write a prompt to the standard error output before
+          copying a file that would overwrite an existing file. If the
+          response from the standard input begins with the character 'y' or
+          'Y', the file copy is attempted. (The -i option overrides any pre-
+          vious -f or -n options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>Do not overwrite an existing file. (The -n option overrides
+          any previous -f or -i options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-p</term>
+
+        <listitem>
+          <para>Cause cp to preserve the following attributes of each source
+          file in the copy: modification time, access time, file flags, file
+          mode, user ID, and group ID, as allowed by permissions. If the user
+          ID and group ID cannot be preserved, no error message is displayed
+          and the exit value is not altered.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>If source_file designates a directory, cp copies the directory
+          and the entire subtree connected at that point.If the source_file
+          ends in a /, the contents of the directory are copied rather than
+          the directory itself.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Cause cp to be verbose, showing files as they are
+          copied.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-x</term>
+
+        <listitem>
+          <para>File system mount points are not traversed.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>ad mv</title>
+
+    <para>Move files and directories.</para>
+
+    <para>Move files around within an AFP volume, updating the CNID database
+    as needed. If either:<itemizedlist>
+        <listitem>
+          <para>source or destination is not an AFP volume</para>
+        </listitem>
+
+        <listitem>
+          <para>source AFP volume != destination AFP volume</para>
+        </listitem>
+      </itemizedlist>the files are copied and removed from the source.</para>
+
+    <para>Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>Do not prompt for confirmation before overwriting the
+          destination path. (The -f option overrides any previous -i or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-i</term>
+
+        <listitem>
+          <para>Cause mv to write a prompt to standard error before moving a
+          file that would overwrite an existing file. If the response from the
+          standard input begins with the character `y' or `Y', the move is
+          attempted. (The -i option overrides any previous -f or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>Do not overwrite an existing file. (The -n option overrides
+          any previous -f or -i options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Cause mv to be verbose, showing files after they are
+          moved.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>ad rm</title>
+
+    <para>Remove files and directories.</para>
+
+    <para>The rm utility attempts to remove the non-directory type files
+    specified on the command line. If the files and directories reside on an
+    AFP volume, the corresponding CNIDs are deleted from the volumes
+    database.</para>
+
+    <para>The options are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>Attempt to remove the file hierarchy rooted in each file
+          argument.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Be verbose when deleting files, showing them as they are
+          removed.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Reporting Bugs</title>
+
+    <para>Report bugs to the Netatalk-devel list
+    &lt;netatalk-devel@lists.sourceforge.net&gt;.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>apple_dump</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/afpldaptest.1.xml b/doc/manpages/man1/afpldaptest.1.xml
new file mode 100644 (file)
index 0000000..a7e73cd
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpldaptest.1">
+  <refmeta>
+    <refentrytitle>afpldaptest</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>afpldaptest</refname>
+
+    <refpurpose>Syntactically check ldap parameters in afp.conf</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>afpldaptest<indexterm><primary>afpldaptest</primary></indexterm></command>
+
+      <group choice="req">
+        <arg choice="plain">-u <replaceable>USER</replaceable></arg>
+        <arg choice="plain">-g <replaceable>GROUP</replaceable></arg>
+        <arg choice="plain">-i <replaceable>UUID</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>afpldaptest<indexterm><primary>afpldaptest</primary></indexterm></command>
+
+      <group choice="req">
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-?</arg>
+        <arg choice="plain">-:</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>afpldaptest</command> is a simple command to syntactically
+    check ldap parameters in @pkgconfdir@/afp.conf.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-u</option> <replaceable>USER</replaceable></term>
+
+        <listitem>
+          <para>Show uuid for <replaceable>USER</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-g</option> <replaceable>GROUP</replaceable></term>
+
+        <listitem>
+          <para>Show uuid for <replaceable>GROUP</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-i</option> <replaceable>UUID</replaceable></term>
+
+        <listitem>
+          <para>Show user, group or local-uuid for
+         <replaceable>UUID</replaceable>.</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -?, -:</option></term>
+
+        <listitem>
+          <para>Show the help and exit.</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/afppasswd.1.xml b/doc/manpages/man1/afppasswd.1.xml
new file mode 100644 (file)
index 0000000..e67727f
--- /dev/null
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afppasswd.1">
+  <refmeta>
+    <refentrytitle>afppasswd</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afppasswd</refname>
+
+    <refpurpose>netatalk password maintenance utility</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>afppasswd<indexterm>
+          <primary>afppasswd</primary>
+        </indexterm><indexterm>
+          <primary>UAM</primary>
+
+          <secondary>User Authentication Module</secondary>
+        </indexterm></command>
+
+      <arg choice="opt">-acfn</arg>
+
+      <arg choice="opt"><arg choice="plain">-p
+      <replaceable>passwd</replaceable></arg><arg
+      choice="plain"><replaceable>file</replaceable></arg></arg>
+
+      <arg choice="opt"><arg choice="plain">-u
+      <replaceable>minimum</replaceable></arg><arg
+      choice="plain"><replaceable>uid</replaceable></arg></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>afppasswd</command> allows the maintenance of afppasswd
+    files created by netatalk for use by the uams_randnum.so UAM (providing
+    the "Randnum exchange" and "2-Way Randnum exchange" User Authentication
+    Modules).</para>
+
+    <para><command>afppasswd</command> can either be called by root with
+    parameters, or can be called by local system users with no parameters to
+    change their AFP passwords.</para>
+
+    <note>
+      <para>With this utility you can only change the passwords used by two
+      specific UAMs. As they provide only weak password encryption, the use of
+      the "Randnum exchange" and "2-Way Randnum exchange" UAMs is deprecated
+      unless one has to support very old AFP clients, that can not deal with
+      the more secure "DHCAST128" and "DHX2" UAM instead. Please compare with
+      the <link linkend="authentication">Authentication chapter</link> inside
+      Netatalk's documentation.</para>
+    </note>
+  </refsect1>
+
+  <refsect1>
+    <title>EXAMPLE</title>
+
+    <para>Local user changing their own password:</para>
+
+    <screen><prompt>example%</prompt> <userinput>afppasswd</userinput>
+<computeroutput>Enter NEW AFP password:</computeroutput> <userinput>(hidden)</userinput>
+<computeroutput>Enter NEW AFP password again:</computeroutput> <userinput>(hidden)</userinput>
+<computeroutput>afppasswd: updated password.</computeroutput>
+</screen>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-a</option></term>
+
+        <listitem>
+          <para>Add a new user to the <command>afppasswd</command>
+          file.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-c</option></term>
+
+        <listitem>
+          <para>Create and/or initialize <command>afppasswd</command> file or
+          specific user.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-f</option></term>
+
+        <listitem>
+          <para>Force the current action.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-p</option><replaceable> path</replaceable></term>
+
+        <listitem>
+          <para>Path to <command>afppasswd</command> file.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-n</option></term>
+
+        <listitem>
+          <para>If cracklib support is built into <emphasis
+          remap="B">netatalk</emphasis> this option will cause cracklib
+          checking to be disabled, if the superuser does not want to have the
+          password run against the cracklib dictionary.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-u</option><replaceable> minimum
+        uid</replaceable></term>
+
+        <listitem>
+          <para>This is the minimum <emphasis remap="I">user id</emphasis>
+          (uid) that <command>afppasswd</command> will use when creating
+          users.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
\ No newline at end of file
diff --git a/doc/manpages/man1/afpstats.1.xml b/doc/manpages/man1/afpstats.1.xml
new file mode 100644 (file)
index 0000000..c2e9799
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpstats.1">
+  <refmeta>
+    <refentrytitle>afpstats</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">24 Mar 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>afpstats</refname>
+
+    <refpurpose>List AFP statistics</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>afpstats<indexterm><primary>afpstats</primary></indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>afpstats</command> list AFP statistics via D-Bus IPC.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>NOTE</title>
+
+    <para><command>afpd</command> must support D-Bus. Check it by
+    "<command>afpd -V</command>".</para>
+
+    <para>"<option>afpstats = yes</option>" must be set in
+    <filename>@pkgconfdir@/afp.conf</filename>.</para>
+
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/apple_dump.1.xml b/doc/manpages/man1/apple_dump.1.xml
new file mode 100644 (file)
index 0000000..2457d1d
--- /dev/null
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="apple_dump.1">
+  <refmeta>
+    <refentrytitle>apple_dump</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">16 Jul 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>apple_dump</refname>
+
+    <refpurpose>Dump AppleSingle/AppleDouble format data</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="opt"><arg choice="plain">-a</arg></arg>
+
+      <group choice="opt">
+        <arg choice="plain"><replaceable>FILE</replaceable></arg>
+        <arg choice="plain"><replaceable>DIR</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-e</arg>
+
+      <group choice="plain">
+        <arg choice="plain"><replaceable>FILE</replaceable></arg>
+        <arg choice="plain"><replaceable>DIR</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-f</arg>
+
+      <arg choice="opt"><replaceable>FILE</replaceable></arg>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-d</arg>
+
+      <arg choice="opt"><replaceable>FILE</replaceable></arg>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-help</arg>
+        <arg choice="plain">--help</arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>apple_dump</command> is a perl script to dump
+    AppleSingle/AppleDouble format data. </para>
+    <para>This script can dump various AppleSingle/AppleDouble data created
+    by mailer, archiver, Mac OS X, Netatalk and so on.</para>
+    <para>With no <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable>, or when <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable> is -, read standard input.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-a</option> [<replaceable>FILE</replaceable>|<replaceable>DIR</replaceable>]</term>
+
+        <listitem>
+          <para>This is default.
+          Dump a AppleSingle/AppleDouble file for
+          <replaceable>FILE</replaceable> or <replaceable>DIR</replaceable>
+          automatically.
+          If FILE is not AppleSingle/AppleDouble format,
+          look for extended attribute, 
+          <replaceable>.AppleDouble/FILE</replaceable> and
+          <replaceable>._FILE</replaceable>.
+          If <replaceable>DIR</replaceable>, look for
+          extended attribute, 
+          <replaceable>DIR/.AppleDouble/.Parent</replaceable> and
+          <replaceable>._DIR</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-e</option> <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable></term>
+
+        <listitem>
+          <para>Dump extended attribute of<replaceable>FILE</replaceable> or <replaceable>DIR</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-f</option> [<replaceable>FILE</replaceable>]</term>
+
+        <listitem>
+          <para>Dump <replaceable>FILE</replaceable>. Assume FinderInfo to be FileInfo.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-d</option> [<replaceable>FILE</replaceable>]</term>
+
+        <listitem>
+          <para>Dump <replaceable>FILE</replaceable>. Assume FinderInfo to be DirInfo.</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -help, --help</option></term>
+
+        <listitem>
+          <para>Display the help and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>NOTE</title>
+
+    <para>There is no way to detect whether FinderInfo is FileInfo or DirInfo.
+    By default, apple_dump examines whether file or directory, a parent directory
+    is .AppleDouble, filename is ._*, filename is .Parent, and so on.</para>
+
+    <para>If setting option -e, -f or -d,  assume FinderInfo and doesn't look
+    for another file.</para>
+
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>ad</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>getfattr</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>attr</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>runat</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>getextattr</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>lsextattr</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/asip-status.pl.1.xml b/doc/manpages/man1/asip-status.pl.1.xml
new file mode 100644 (file)
index 0000000..859b063
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="asip-status.pl.1">
+  <refmeta>
+    <refentrytitle>asip-status.pl</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">24 Jul 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>asip-status.pl</refname>
+
+    <refpurpose>Queries AFP servers for their capabilities</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>asip-status.pl<indexterm>
+          <primary>asip-status.pl</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+      <arg choice="opt">-i</arg>
+      <arg choice="opt">-x</arg>
+
+      <arg choice="plain">HOSTNAME[:PORT]</arg>
+    </cmdsynopsis>
+
+    <sbr />
+
+    <cmdsynopsis>
+      <command>asip-status.pl<indexterm>
+          <primary>asip-status.pl</primary>
+        </indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+      </group>
+    </cmdsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><emphasis remap="B">asip-status.pl</emphasis> is a perl script that
+    sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and
+    displays the results, namely "Machine type", the server's name, supported
+    AFP versions, UAMs and AFP flags, the "server signature" and the network
+    addresses, the server provides AFP services on.</para>
+
+    <para>When you don't supply :PORT, then the default AFP port, 548, will be
+    used.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-d</option></term>
+
+        <listitem>
+          <para>Enable debug output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-i</option></term>
+
+        <listitem>
+          <para>Show icon if it exists.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-x</option></term>
+
+        <listitem>
+          <para>Enable hex dump output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version.</para>
+        </listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>EXAMPLES</title>
+
+    <para><programlisting><emphasis remap="B">asip-status.pl</emphasis> 192.168.1.15
+AFP reply from 192.168.1.15:548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Macintosh
+AFP versions: AFPVersion 1.1,AFPVersion 2.0,AFPVersion 2.1,AFP2.2
+UAMs: Cleartxt passwrd,Randnum exchange,2-Way Randnum exchange
+Volume Icon &amp; Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsChgPwd
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSuperClient
+Server name: bookchan
+Signature:
+04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  ..e#..e#..e#..e#
+                                                  
+Network address: 192.168.1.15:548 (TCP/IP address and port)
+Network address: 65280.128 (ddp address)
+</programlisting></para>
+
+    <para><programlisting><emphasis remap="B">asip-status.pl</emphasis> myserver:10548
+AFP reply from myserver:10548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Netatalk3.0
+AFP versions: AFP2.2,AFPX03,AFP3.1,AFP3.2,AFP3.3
+UAMs: DHX2,DHCAST128
+Volume Icon &amp; Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSrvrNotifications
+    SupportsOpenDirectory
+    SupportsUTF8Servername
+    SupportsUUIDs
+    SupportsExtSleep
+    SupportsSuperClient
+Server name: myserver
+Signature:
+8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  ...:...>o1....p.
+                                                  
+Network address: 192.168.1.154:10548 (TCP/IP address and port)
+UTF8 Servername: myserver
+</programlisting></para>
+  </refsect1>
+
+  <refsect1>
+    <title>REPORTING BUGS</title>
+
+    <para>Report bugs to the Netatalk-devel list
+    &lt;netatalk-devel@lists.sourceforge.net&gt;.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/dbd.1.xml b/doc/manpages/man1/dbd.1.xml
new file mode 100644 (file)
index 0000000..4675378
--- /dev/null
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="dbd.1">
+  <refmeta>
+    <refentrytitle>dbd</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">28 Dec 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>dbd</refname>
+
+    <refpurpose>CNID database maintenance</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>dbd<indexterm>
+          <primary>dbd</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-cfFstuvV</arg>
+
+      <arg choice="plain"><replaceable>volumepath</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>dbd</command> scans all file and directories of AFP
+    volumes, updating the CNID database of the volume. It must be run with
+    appropriate permissions i.e. as root..</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-c</term>
+
+        <listitem>
+          <para>convert from adouble:v2 to adouble:ea</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>delete and recreate CNID database</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-F</term>
+
+        <listitem>
+          <para>location of the afp.conf config file</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-s</term>
+
+        <listitem>
+          <para>scan volume: treat the volume as read only and don't perform
+          any filesystem modifications</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-t</term>
+
+        <listitem>
+          <para>show statistics while running</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-u</term>
+
+        <listitem>
+          <para>username for use with AFP volumes using user variable $u</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>verbose</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>display version info</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID background</title>
+
+    <para>The CNID backends maintains name to ID mappings. If you change a
+    filename outside afpd(8) (shell, samba), the CNID database will not
+    reflect that change. Netatalk tries to recover from such inconsistencies
+    as gracefully as possible.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_dbd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/macusers.1.xml b/doc/manpages/man1/macusers.1.xml
new file mode 100644 (file)
index 0000000..5cfee6f
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="macusers.1">
+  <refmeta>
+    <refentrytitle>macusers</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">13 Oct 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>macusers</refname>
+
+    <refpurpose>List the users connecting via AFP</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>macusers<indexterm><primary>macusers</primary></indexterm></command>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>macusers<indexterm><primary>macusers</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-help</arg>
+        <arg choice="plain">--help</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>macusers</command> list the users connecting via AFP.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -help, --help</option></term>
+
+        <listitem>
+          <para>Display the help and exit</para>
+        </listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/megatron.1.xml b/doc/manpages/man1/megatron.1.xml
new file mode 100644 (file)
index 0000000..20b6405
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="megatron.1">
+  <refmeta>
+    <refentrytitle>megatron</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">02 Sep 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>megatron</refname>
+
+    <refname>unhex</refname>
+
+    <refname>unbin</refname>
+
+    <refname>unsingle</refname>
+
+    <refname>hqx2bin</refname>
+
+    <refname>single2bin</refname>
+
+    <refname>macbinary</refname>
+
+    <refpurpose>Macintosh file format transformer</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>megatron<indexterm><primary>megatron</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unbin<indexterm><primary>unbin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unhex<indexterm><primary>unhex</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unsingle<indexterm><primary>unsingle</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>hqx2bin<indexterm><primary>hqx2bin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>single2bin<indexterm><primary>single2bin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>macbinary<indexterm><primary>macbinary</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>megatron</command> is used to transform files from BinHex,
+    MacBinary, AppleSingle, or <emphasis remap="B" role="bold">netatalk</emphasis> style
+    AppleDouble formats into MacBinary or <emphasis remap="B">netatalk</emphasis>
+    style AppleDouble formats. The <emphasis remap="B">netatalk</emphasis>
+    style AppleDouble format is the file format used by <emphasis remap="B">afpd,</emphasis>
+    the <emphasis remap="B">netatalk</emphasis> Apple Filing Protocol
+    (AppleShare) server. BinHex, MacBinary, and AppleSingle are commonly used
+    formats for transferring Macintosh files between machines via email or
+    file transfer protocols. <command>megatron</command> uses its name to
+    determine what type of transformation is being asked of it.</para>
+
+    <para>If <command>megatron</command> is called as <command>unhex</command>
+    , <command>unbin</command> or <command>unsingle</command>, it tries to
+    convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble
+    format. BinHex is the format most often used to send Macintosh files by
+    e-mail. Usually these files have an extension of &#34;.hqx&#34;. MacBinary
+    is the format most often used by terminal emulators &#34;on the fly&#34;
+    when transferring Macintosh files in binary mode. MacBinary files often
+    have an extension of &#34;.bin&#34;. Some Macintosh LAN-based email
+    packages use uuencoded AppleSingle format to &#34;attach&#34; or
+    &#34;enclose&#34; files in email. AppleSingle files don&#39;t have a
+    standard filename extension.</para>
+
+    <para>If <command>megatron</command> is called as <command>hqx2bin</command>,
+    <command>single2bin</command>, or <command>macbinary</command>, it will
+    try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into
+    MacBinary. This last translation may be useful in moving Macintosh files
+    from your <command>afpd</command> server to some other machine when you
+    can&#39;t copy them from the server using a Macintosh for some reason.</para>
+
+    <para>If <command>megatron</command> is called with any other name, it
+    uses the default translation, namely <command>unhex</command>.</para>
+
+    <para>If no source file is given, or if <emphasis remap="I">sourcefile</emphasis>
+    is `<emphasis remap="" role="bold">-</emphasis>&#39;, and if the
+    conversion is from a BinHex or MacBinary file, <command>megatron</command>
+    will read from standard input.</para>
+
+    <para>The filename used to store any output file is the filename that is
+    encoded in the source file. MacBinary files are created with a
+    &#34;.bin&#34; extension. In the case of conflicts, the old file is
+    overwritten!</para>
+  </refsect1>
+
+  <refsect1 id="options">
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, --version</option></term>
+
+        <listitem>
+          <para>Show version.</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/netatalk-config.1.xml b/doc/manpages/man1/netatalk-config.1.xml
new file mode 100644 (file)
index 0000000..69da13f
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="netatalkconfig.1">
+  <refmeta>
+    <refentrytitle>netatalk-config</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">09 June 2001</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+
+    <refmiscinfo class="manual">The Netatalk Project</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>netatalk-config</refname>
+
+    <refpurpose>script to get information about the installed version of
+    netatalk</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>netatalk-config<indexterm><primary>netatalk-config</primary></indexterm></command>
+
+      <arg choice="opt"><arg choice="plain">--prefix </arg><arg choice="opt"><replaceable>=DIR</replaceable></arg></arg>
+
+      <arg choice="opt"><arg choice="plain">--exec_prefix </arg><arg
+      choice="opt"><replaceable>=DIR</replaceable></arg></arg>
+
+      <arg choice="opt">--help</arg>
+
+      <arg choice="opt">--version</arg>
+
+      <arg choice="opt">--libs</arg>
+
+      <arg choice="opt">--libs-dirs</arg>
+
+      <arg choice="opt">--libs-names</arg>
+
+      <arg choice="opt">--cflags</arg>
+
+      <arg choice="opt">--macros</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>netatalk-config</command> is a tool that is used to
+    determine the compiler and linker flags that should be used
+    to compile and link programs that use the <emphasis remap="I">netatalk</emphasis>
+    run-time libraries.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para><command>netatalk-config</command> accepts the following options:</para>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>--help</option></term>
+
+        <listitem>
+          <para>Print a short help for this command and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--version</option></term>
+
+        <listitem>
+          <para>Print the currently installed version of <emphasis remap="I">netatalk</emphasis>
+          on the standard output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs</option></term>
+
+        <listitem>
+          <para>Print the linker flags that are necessary to link against the
+          <emphasis remap="I">netatalk</emphasis> run-time libraries.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs-dirs</option></term>
+
+        <listitem>
+          <para>Print only the -l/-R part of --libs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs-names</option></term>
+
+        <listitem>
+          <para>Print only the -l part of --libs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--cflags</option></term>
+
+        <listitem>
+          <para>Print the compiler flags that are necessary to compile a
+          program linked against the <emphasis remap="I">netatalk</emphasis>
+          run-time libraries.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--macros</option></term>
+
+        <listitem>
+          <para>Print the <emphasis remap="I">netatalk</emphasis> m4
+          directory.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--prefix=PREFIX</option></term>
+
+        <listitem>
+          <para>If specified, use PREFIX instead of the installation prefix
+          that <emphasis remap="I">netatalk</emphasis> was built with when
+          computing the output for the --cflags and --libs options. This
+          option is also used for the exec prefix if --exec-prefix was not
+          specified. This option must be specified before any --libs or
+          --cflags options.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--exec\_prefix=PREFIX</option></term>
+
+        <listitem>
+          <para>If specified, use PREFIX instead of the installation exec
+          prefix that <emphasis remap="I">netatalk</emphasis> was built with
+          when computing the output for the --cflags and --libs options. This
+          option must be specified before any --libs or --cflags options.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>COPYRIGHT</title>
+
+    <para>Copyright © 1998 Owen Taylor</para>
+
+    <para>Permission to use, copy, modify, and distribute this software and
+    its documentation for any purpose and without fee is hereby granted,
+    provided that the above copyright notice appear in all copies and that
+    both that copyright notice and this permission notice appear in supporting
+    documentation.</para>
+
+    <para>Man page adapted for <command>netatalk-config</command> by Sebastian
+    Rittau in 2001.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/uniconv.1.xml b/doc/manpages/man1/uniconv.1.xml
new file mode 100644 (file)
index 0000000..d563c28
--- /dev/null
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="uniconv.1">
+
+  <refmeta>
+    <refentrytitle>uniconv</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>uniconv</refname>
+
+    <refpurpose>convert Netatalk volume encoding</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>uniconv<indexterm><primary>uniconv</primary></indexterm></command>
+
+      <arg choice="opt">-ndv</arg>
+
+      <arg choice="plain">-c <replaceable>cnidbackend</replaceable></arg>
+
+      <arg choice="plain">-f <replaceable>fromcode</replaceable></arg>
+
+      <arg choice="plain">-t <replaceable>tocode</replaceable></arg>
+
+      <arg>-m <replaceable>maccode</replaceable></arg>
+
+      <arg choice="plain"><replaceable>volumepath</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>uniconv</command> converts the volume encoding of
+    <replaceable>volumepath</replaceable> from the <replaceable>fromcode</replaceable>
+    to the <replaceable>tocode</replaceable> encoding.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-c</term>
+
+        <listitem>
+          <para>CNID backend used on this volume, usually cdb or dbd. Should
+          match the backend selected with afpd for this volume. If not
+          specified, the default CNID backend "@DEFAULT_CNID_SCHEME@" is
+          used</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>don't HEX encode leading dots (:2e), equivalent to
+          <option>use dots = yes</option> in <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>encoding to convert from, use ASCII for HEX encoded volumes</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-h</term>
+
+        <listitem>
+          <para>display help</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-m</term>
+
+        <listitem>
+          <para>Macintosh client codepage, required for HEX encoded volumes.
+          Defaults to "MAC_ROMAN"</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>"dry run", don't do any real changes</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-t</term>
+
+        <listitem>
+          <para>volume encoding to convert to, e.g. UTF8</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>verbose output, use twice for maximum logging.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>print version and exit</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>WARNING</title>
+
+    <para>Setting the wrong options might render your data unusable!!! Make
+    sure you know what you are doing. Always backup your data first.</para>
+
+    <para>It is <emphasis role="bold">*strongly*</emphasis> recommended to do
+    a "dry run" first and to check the output for conversion errors.</para>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    should <emphasis>not</emphasis> be running while you change the volume
+    encoding. Remember to change <option>unix charset</option> or
+    <option>vol charset</option> in
+    <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    to the new codepage, before restarting afpd.</para>
+
+    <para>In case of <emphasis role="bold">MacChineseTraditional</emphasis>,
+    <emphasis role="bold">MacJapanese</emphasis> or
+    <emphasis role="bold">MacKorean</emphasis>,
+    uniconv cannot be used.</para>
+
+    <para><emphasis role="bold">USE AT YOUR OWN RISK!!!</emphasis></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Selectable charsets</title>
+
+    <para>Netatalk provides internal support for UTF-8 (pre- and decomposed)
+    and HEX. If you want to use other charsets, they must be provided by
+    <citerefentry><refentrytitle>iconv</refentrytitle><manvolnum>1</manvolnum></citerefentry></para>
+
+    <para><command>uniconv</command> also knows iso-8859.adapted, an old style
+    1.x NLS widely used. This is only intended for upgrading old volumes,
+    <citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    cannot handle iso-8859.adapted anymore.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID background</title>
+
+    <para>The CNID backends maintains name to ID mappings. If you change a
+    filename outside afpd(8) (shell, samba), the CNID db, i.e. the DIDNAME
+    index, gets inconsistent. Netatalk tries to recover from such
+    inconsistencies as gracefully as possible. The mechanisms to resolve such
+    inconsistencies may fail sometimes, though, as this is not an easy task to
+    accomplish. I.e. if several names in the path to the file or directory
+    have changed, things may go wrong.</para>
+
+    <para>If you change a lot of filenames at once, chances are higher that
+    the afpds fallback mechanisms fail, i.e. files will be assigned new IDs,
+    even though the file hasn't changed. <command>uniconv</command>
+    therefore updates the CNID entry for each file/directory directly after it
+    changes the name to avoid inconsistencies. The two supported backends for
+    volumes, dbd and cdb, use the same CNID db format. Therefore, you
+    <emphasis>could</emphasis> use <command>uniconv</command> with cdb and
+    <command>afpd</command> with dbd later.</para>
+
+    <para><emphasis role="bold">Warning</emphasis>: There must not be two
+    processes opening the CNID database using different backends at once! If a
+    volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
+    <command>uniconv</command> with cdb, the result will be a corrupted CNID
+    database, as the two backends use different locking schemes. You might run
+    into additional problems, e.g. if dbd is compiled with transactions, cdb
+    will not update the transaction logs.</para>
+
+    <para>In general, it is recommended to use the same backend for
+    <command>uniconv</command> you are using with
+    <citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>convert 1.x CAP encoded volume to UTF-8, clients used MacRoman
+    codepage, cnidscheme is dbd:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -c dbd -f ASCII -t UTF8 -m MAC_ROMAN /path/to/share</userinput></screen>
+
+    <para>convert iso8859-1 volume to UTF-8, cnidscheme is cdb:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -c cdb -f ISO-8859-1 -t UTF8 -m MAC_ROMAN /path/to/share</userinput></screen>
+
+    <para>convert 1.x volume using iso8859-1 adapted NLS to HEX encoding:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -f ISO-8859-ADAPTED -t ASCII -m MAC_ROMAN/path/to/share</userinput></screen>
+
+    <para>convert UTF-8 volume to HEX, for MacCyrillic clients:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -f UTF8 -t ASCII -m MAC_CYRILLIC /path/to/share</userinput></screen>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,<citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,<citerefentry><refentrytitle>iconv</refentrytitle><manvolnum>1</manvolnum></citerefentry>,<citerefentry><refentrytitle>cnid_metad</refentrytitle><manvolnum>8</manvolnum></citerefentry>,<citerefentry><refentrytitle>cnid_dbd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/.gitignore b/doc/manpages/man5/.gitignore
new file mode 100644 (file)
index 0000000..2fc1d34
--- /dev/null
@@ -0,0 +1 @@
+*\.5
diff --git a/doc/manpages/man5/Makefile.am b/doc/manpages/man5/Makefile.am
new file mode 100644 (file)
index 0000000..1af5734
--- /dev/null
@@ -0,0 +1,26 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       afp.conf.5 \
+       afp_signature.conf.5 \
+       afp_voluuid.conf.5 \
+       extmap.conf.5
+
+EXTRA_DIST = \
+       afp_signature.conf.5.xml \
+       afp_voluuid.conf.5.xml \
+       afp.conf.5.xml \
+       extmap.conf.5.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.5 : %.5.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man5/$@.in
+
+html-local: $(MAN_MANPAGES) $(MAN_STYLESHEET)
+endif
\ No newline at end of file
diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml
new file mode 100644 (file)
index 0000000..a8b4378
--- /dev/null
@@ -0,0 +1,2217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp.conf.5">
+  <refmeta>
+    <refentrytitle>afp.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">05 Jun 2014</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp.conf</refname>
+
+    <refpurpose>Netatalk configuration file <indexterm>
+        <primary>afp.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>SYNOPSIS</title>
+
+    <para>The <filename>afp.conf</filename> file is the configuration file for
+    the <emphasis role="bold">Netatalk</emphasis> AFP file server.</para>
+
+    <para>All AFP specific configuration and AFP volume definitions are done
+    via this file.</para>
+  </refsect1>
+
+  <refsect1 id="FILEFORMATSECT">
+    <title>FILE FORMAT</title>
+
+    <para>The file consists of sections and parameters. A section begins with
+    the name of the section in square brackets and continues until the next
+    section begins. Sections contain parameters of the form: <programlisting>
+    <replaceable>name</replaceable> = <replaceable>value </replaceable>
+    </programlisting></para>
+
+    <para>The file is line-based - that is, each newline-terminated line
+    represents either a comment, a section name or a parameter.</para>
+
+    <para>Section and parameter names are case sensitive.</para>
+
+    <para>Only the first equals sign in a parameter is significant. Whitespace
+    before or after the first equals sign is discarded. Leading, trailing and
+    internal whitespace in section and parameter names is irrelevant. Leading
+    and trailing whitespace in a parameter value is discarded. Internal
+    whitespace within a parameter value is retained verbatim.</para>
+
+    <para>Any line beginning with a semicolon (<quote>;</quote>) or a hash
+    (<quote>#</quote>) character is ignored, as are lines containing only
+    whitespace.</para>
+
+    <para>Any line ending in a <quote> <literal>\</literal> </quote> is
+    continued on the next line in the customary UNIX fashion.</para>
+
+    <para>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, 1/0
+    or true/false. Case is not significant in boolean values, but is preserved
+    in string values. Some items such as create masks are numeric.</para>
+
+    <para>The parameter <option>include =
+    <replaceable>path</replaceable></option> allows you to include one config
+    file inside another. The file is included literally, as though typed in
+    place. Nested includes are not supported.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SECTION DESCRIPTIONS</title>
+
+    <para>Each section in the configuration file (except for the [Global]
+    section) describes a shared resource (known as a <quote>volume</quote>).
+    The section name is the name of the volume and the parameters within the
+    section define the volume attributes and options.</para>
+
+    <para>There are two special sections, [Global] and [Homes], which are
+    described under <emphasis>special sections</emphasis>. The following notes
+    apply to ordinary section descriptions.</para>
+
+    <para>A volume consists of a directory to which access is being given plus
+    a description of the access rights which are granted to the user of the
+    service. For volumes the <option>path</option> option must specify the
+    directory to share.</para>
+
+    <para>Any volume section without <option>path</option> option is
+    considered a <emphasis>vol preset</emphasis> which can be selected in
+    other volume sections via the <option>vol preset</option> option and
+    constitutes defaults for the volume. For any option specified both in a
+    preset <emphasis>and</emphasis> in a volume section the volume section
+    setting completely substitutes the preset option.</para>
+
+    <para>The access rights granted by the server are masked by the access
+    rights granted to the specified or guest UNIX user by the host system. The
+    server does not grant more access than the host system grants.</para>
+
+    <para>The following sample section defines an AFP volume. The user has
+    full access to the path <filename>/foo/bar</filename>. The share is
+    accessed via the share name <literal>baz</literal>: <programlisting> [baz]
+    path = /foo/bar </programlisting></para>
+  </refsect1>
+
+  <refsect1>
+    <title>SPECIAL SECTIONS</title>
+
+    <refsect2>
+      <title>The [Global] section</title>
+
+      <para>Parameters in this section apply to the server as a whole.
+      Parameters denoted by a (G) below are must be set in this
+      section.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>The [Homes] section</title>
+
+      <para>This section enable sharing of the UNIX server user home
+      directories. Specifying an optional <option>path</option> parameter
+      means that not the whole user home will be shared but the subdirectory
+      <option>path</option>. It is necessary to define the <option>basedir
+      regex</option> option. It should be a regex which matches the parent
+      directory of the user homes. Parameters denoted by a (H) belong to
+      volume sections. The optional parameter <option>home name</option> can
+      be used to change the AFP volume name which <emphasis>$u's
+      home</emphasis> by default. See below under VARIABLE
+      SUBSTITUTIONS.</para>
+
+      <para>The following example illustrates this. Given all user home
+      directories are stored under <filename>/home</filename>:
+      <programlisting> [Homes]
+      path = afp-data
+      basedir regex = /home</programlisting> For a user
+      <emphasis>john</emphasis> this results in an AFP home volume with a path
+      of <filename>/home/john/afp-data</filename>.</para>
+
+      <para>If <option>basedir regex</option> contains symlink, set the
+      canonicalized absolute path. When <filename>/home</filename> links to
+      <filename>/usr/home</filename>: <programlisting> [Homes]
+      basedir regex = /usr/home</programlisting></para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>PARAMETERS</title>
+
+    <para>Parameters define the specific attributes of sections.</para>
+
+    <para>Some parameters are specific to the [Global] section (e.g.,
+    <emphasis>log type</emphasis>). All others are permissible only in volume
+    sections. The letter <emphasis>G</emphasis> in parentheses indicates that
+    a parameter is specific to the [Global] section. The letter
+    <emphasis>V</emphasis> indicates that a parameter can be specified in a
+    volume specific section.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>VARIABLE SUBSTITUTIONS</title>
+
+    <para>You can use variables in volume names. The use of variables in paths
+     is limited to $u.</para>
+
+    <orderedlist>
+      <listitem>
+        <para>if you specify an unknown variable, it will not get
+        converted.</para>
+      </listitem>
+
+      <listitem>
+        <para>if you specify a known variable, but that variable doesn't have
+        a value, it will get ignored.</para>
+      </listitem>
+    </orderedlist>
+
+    <para>The variables which can be used for substitutions are:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>$b</term>
+
+        <listitem>
+          <para>basename</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$c</term>
+
+        <listitem>
+          <para>client's ip address</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$d</term>
+
+        <listitem>
+          <para>volume pathname on server</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$f</term>
+
+        <listitem>
+          <para>full name (contents of the gecos field in the passwd
+          file)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$g</term>
+
+        <listitem>
+          <para>group name</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$h</term>
+
+        <listitem>
+          <para>hostname</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$i</term>
+
+        <listitem>
+          <para>client's ip, without port</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$s</term>
+
+        <listitem>
+          <para>server name (this can be the hostname)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$u</term>
+
+        <listitem>
+          <para>user name (if guest, it is the user that guest is running
+          as)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$v</term>
+
+        <listitem>
+          <para>volume name</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$$</term>
+
+        <listitem>
+          <para>prints dollar sign ($)</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>EXPLANATION OF GLOBAL PARAMETERS</title>
+
+    <refsect2>
+      <title>Authentication Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>ad domain = <parameter>DOMAIN</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Append @DOMAIN to username when authenticating. Useful in
+            Active Directory environments that otherwise would require the
+            user to enter the full user@domain string.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>admin auth user = <parameter>user</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifying eg "<option>admin auth user = root</option>"
+            whenever a normal user login fails, afpd will try to authenticate
+            as the specified <option>admin auth user</option>. If this
+            succeeds, a normal session is created for the original connecting
+            user. Said differently: if you know the password of <option>admin
+            auth user</option>, you can authenticate as any other user.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>k5 keytab = <replaceable>path</replaceable>
+          <type>(G)</type></term>
+
+          <term>k5 service = <replaceable>service</replaceable>
+          <type>(G)</type></term>
+
+          <term>k5 realm = <replaceable>realm</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>These are required if the server supports the Kerberos 5
+            authentication UAM.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>nt domain = <parameter>DOMAIN</parameter>
+          <type>(G)</type></term>
+
+          <term>nt separator = <parameter>SEPARATOR</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Use for eg. winbind authentication, prepends both strings
+            before the username from login and then tries to authenticate with
+            the result through the available and active UAM authentication
+            modules.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>save password = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables or disables the ability of clients to save passwords
+            locally.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>set password = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables or disables the ability of clients to change their
+            passwords via chooser or the "connect to server" dialog.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>uam list = <replaceable>uam list</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Space or comma separated list of UAMs. (The default is
+            "uams_dhx.so uams_dhx2.so").</para>
+
+            <para>The most commonly used UAMs are:</para>
+
+            <variablelist>
+              <varlistentry>
+                <term>uams_guest.so</term>
+
+                <listitem>
+                  <para>allows guest logins</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_clrtxt.so</term>
+
+                <listitem>
+                  <para>(uams_pam.so or uams_passwd.so) Allow logins with
+                  passwords transmitted in the clear. (legacy)</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_randum.so</term>
+
+                <listitem>
+                  <para>allows Random Number and Two-Way Random Number
+                  Exchange for authentication (requires a separate file
+                  containing the passwords, either @pkgconfdir@/afppasswd file or
+                  the one specified via "<option>passwd file</option>". See
+                  <citerefentry>
+                      <refentrytitle>afppasswd</refentrytitle>
+
+                      <manvolnum>1</manvolnum>
+                    </citerefentry> for details. (legacy)</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_dhx.so</term>
+
+                <listitem>
+                  <para>(uams_dhx_pam.so or uams_dhx_passwd.so) Allow
+                  Diffie-Hellman eXchange (DHX) for authentication.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_dhx2.so</term>
+
+                <listitem>
+                  <para>(uams_dhx2_pam.so or uams_dhx2_passwd.so) Allow
+                  Diffie-Hellman eXchange 2 (DHX2) for authentication.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uam_gss.so</term>
+
+                <listitem>
+                  <para>Allow Kerberos V for authentication (optional)</para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>uam path = <replaceable>path</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the default path for UAMs for this server (default is
+            @libdir@/netatalk).</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Charset Options</title>
+
+      <para>With OS X Apple introduced the AFP3 protocol. One of the big
+      changes was, that AFP3 uses Unicode names encoded as Decomposed UTF-8
+      (UTF8-MAC). Previous AFP/OS versions used charsets like MacRoman,
+      MacCentralEurope, etc.</para>
+
+      <para>To be able to serve AFP3 and older clients at the same time,
+      <command>afpd</command> needs to be able to convert between UTF-8 and
+      Mac charsets. Even OS X clients partly still rely on the mac charset. As
+      there's no way, <command>afpd</command> can detect the codepage a pre
+      AFP3 client uses, you have to specify it using the <option>mac
+      charset</option> option. The default is MacRoman, which should be fine
+      for most western users.</para>
+
+      <para>As <command>afpd</command> needs to interact with UNIX operating
+      system as well, it need's to be able to convert from UTF8-MAC / Mac
+      charset to the UNIX charset. By default <command>afpd</command> uses
+      <emphasis>UTF8</emphasis>. You can set the UNIX charset using the
+      <option>unix charset</option> option. If you're using extended
+      characters in the configuration files for <command>afpd</command>, make
+      sure your terminal matches the <option>unix charset</option>.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>mac charset = <parameter>CHARSET</parameter>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the Mac clients charset, e.g.
+            <emphasis>MAC_ROMAN</emphasis>. This is used to convert strings
+            and filenames to the clients codepage for OS9 and Classic, i.e.
+            for authentication and AFP messages (SIGUSR2 messaging). This will
+            also be the default for the volumes <option>mac charset</option>.
+            Defaults to <emphasis>MAC_ROMAN</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>unix charset = <parameter>CHARSET</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the servers unix charset, e.g.
+            <emphasis>ISO-8859-15</emphasis> or <emphasis>EUC-JP</emphasis>.
+            This is used to convert strings to/from the systems locale, e.g.
+            for authentication, server messages and volume names. If
+            <emphasis>LOCALE</emphasis> is set, the systems locale is used.
+            Defaults to <emphasis>UTF8</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol charset = <parameter>CHARSET</parameter>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the encoding of the volumes filesystem. By
+            default, it is the same as <option>unix charset</option>.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Password Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>passwd file = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to the Randnum UAM passwd file for this server
+            (default is @pkgconfdir@/afppasswd).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>passwd minlen = <parameter>number</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the minimum password length, if supported by the
+            UAM</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Network Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>advertise ssh = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows old Mac OS X clients (10.3.3-10.4) to automagically
+            establish a tunneled AFP connection through SSH. If this option is
+            set, the server's answers to client's FPGetSrvrInfo requests
+            contain an additional entry. It depends on both client's settings
+            and a correctly configured and running <citerefentry>
+                <refentrytitle>sshd</refentrytitle>
+
+                <manvolnum>8</manvolnum>
+              </citerefentry> on the server to let things work.</para>
+
+            <note>
+              <para>Setting this option is not recommended since globally
+              encrypting AFP connections via SSH will increase the server's
+              load significantly. On the other hand, Apple's client side
+              implementation of this feature in MacOS X versions prior to
+              10.3.4 contained a security flaw.</para>
+            </note>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp interfaces = <replaceable>name [name ...]</replaceable>
+            <type>(G)</type></term>
+          <listitem>
+            <para>Specifies the network interfaces that the server should
+            listens on. The default is advertise the first IP address of the
+            system, but to listen for any incoming request.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp listen = <replaceable>ip address[:port] [ip address[:port]
+          ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address that the server should advertise
+            <emphasis role="bold">and</emphasis> listens to. The default is
+            advertise the first IP address of the system, but to listen for
+            any incoming request. The network address may be specified either
+            in dotted-decimal format for IPv4 or in hexadecimal format for
+            IPv6.</para>
+            <para>IPv6 address + port combination must use URL the format
+            using square brackets [IPv6]:port</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp port = <replaceable>port number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows a different TCP port to be used for AFP. The default
+            is 548. Also sets the default port applied when none specified in
+            an <option>afp listen</option> option.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid listen = <replaceable>ip address[:port] [ip
+          address[:port] ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address that the CNID server should listen
+            on. The default is <emphasis
+            role="bold">localhost:4700</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>disconnect time = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Keep disconnected AFP sessions for
+            <parameter>number</parameter> hours before dropping them. Default
+            is 24 hours.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>dsireadbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Scale factor that determines the size of the DSI/TCP
+            readahead buffer, default is 12. This is multiplies with the DSI
+            server quantum (default ~300k) to give the size of the buffer.
+            Increasing this value might increase throughput in fast local
+            networks for volume to volume copies. <emphasis>Note</emphasis>:
+            This buffer is allocated per afpd child process, so specifying
+            large values will eat up large amount of memory (buffer size *
+            number of clients).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fqdn = <replaceable>name:port</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies a fully-qualified domain name, with an optional
+            port. This is discarded if the server cannot resolve it. This
+            option is not honored by AppleShare clients &lt;= 3.8.3. This
+            option is disabled by default. Use with caution as this will
+            involve a second name resolution step on the client side. Also
+            note that afpd will advertise this name:port combination but not
+            automatically listen to it.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hostname = <replaceable>name</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Use this instead of the result from calling hostname for
+            determining which IP address to advertise, therefore the hostname
+            is resolved to an IP which is the advertised. This is NOT used for
+            listening and it is also overwritten by <option>afp
+            listen</option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>max connections = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the maximum number of clients that can simultaneously
+            connect to the server (default is 200).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>server quantum = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>This specifies the DSI server quantum. The default value is
+            0x100000 (1 MiB). The maximum value is 0xFFFFFFFFF, the minimum is
+            32000. If you specify a value that is out of range, the default
+            value will be set. Do not change this value unless you're
+            absolutely sure, what you're doing</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>sleep time = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Keep sleeping AFP sessions for <parameter>number</parameter>
+            hours before disconnecting clients in sleep mode. Default is 10
+            hours.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>tcprcvbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Try to set TCP receive buffer using setsockpt(). Often OSes
+            impose restrictions on the applications ability to set this
+            value.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>tcpsndbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Try to set TCP send buffer using setsockpt(). Often OSes
+            impose restrictions on the applications ability to set this
+            value.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>recvfile = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to use splice() on Linux for receiving data.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>splice size = <replaceable>number</replaceable> (default:
+          <emphasis>64k</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Maximum number of bytes spliced.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>use sendfile = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to use sendfile<indexterm>
+                <primary>sendfile</primary>
+              </indexterm> syscall for sending file data to clients.</para>
+          </listitem>
+        </varlistentry>
+
+
+        <varlistentry>
+          <term>zeroconf = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to use automatic Zeroconf<indexterm>
+                <primary>Zeroconf</primary>
+
+                <secondary>Bonjour</secondary>
+              </indexterm> service registration if Avahi or mDNSResponder were
+            compiled in.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Miscellaneous Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>admin group = <replaceable>group</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows users of a certain group to be seen as the superuser
+            when they log in. This option is disabled by default.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp read locks = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to apply locks to the byte region read in FPRead
+            calls. The AFP spec mandates this, but it's not really in line
+            with UNIX semantics and is a performance hug.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afpstats = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to provide AFP runtime statistics (connected
+            users, open volumes) via dbus.</para>            
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>basedir regex = <replaceable>regex</replaceable>
+          <type>(H)</type></term>
+
+          <listitem>
+            <para>Regular expression which matches the parent directory of the
+            user homes. If <option>basedir regex</option> contains symlink,
+            you must set the canonicalized absolute path. In the simple case
+            this is just a path ie <option>basedir regex =
+            /home</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>chmod request = <replaceable>preserve (default) | ignore | simple</replaceable>
+          <type>(G/V)</type></term>
+
+          <listitem>
+            <para>Advanced permission control that deals with ACLs.</para>
+
+            <itemizedlist>
+              <listitem><para>
+                <option>ignore</option> - UNIX chmod() requests are completely ignored
+                     </para></listitem>
+              <listitem><para>
+                <option>preserve</option> - preserve ZFS ACEs for
+                named users and groups or POSIX ACL group mask
+                     </para></listitem>
+              <listitem><para>
+                <option>simple</option> - just to a chmod() as
+                requested without any extra steps
+                     </para></listitem>
+                   </itemizedlist>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>close vol = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to close volumes possibly opened by clients when
+            they're removed from the configuration and the configuration is
+            reloaded.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid mysql host = <replaceable>MySQL server address</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>name or address of a MySQL server for use with the mysql CNID
+            backend.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid mysql user = <replaceable>MySQL user</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>MySQL user for authentication with the server.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid mysql pw = <replaceable>password</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Password for MySQL server.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid mysql db = <replaceable>database name</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of an existing database for which the specified user
+            has full privileges.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid server = <replaceable>ipaddress[:port]</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address and port of a cnid_metad server,
+            required for CNID dbd backend. Defaults to localhost:4700. The
+            network address may be specified either in dotted-decimal format
+            for IPv4 or in hexadecimal format for IPv6.-</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>dbus daemon = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to dbus-daemon binary used by Spotlight feature.
+            The default is <filename>/bin/dbus-daemon</filename>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>dircachesize = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Maximum possible entries in the directory cache. The cache
+            stores directories and files. It is used to cache the full path to
+            directories and CNIDs which considerably speeds up directory
+            enumeration.</para>
+
+            <para>Default size is 8192, maximum size is 131072. Given value is
+            rounded up to nearest power of 2. Each entry takes about 100
+            bytes, which is not much, but remember that every afpd child
+            process for every connected user has its cache.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>extmap file = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to the file which defines file extension
+            type/creator mappings. (default is @pkgconfdir@/extmap.conf).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>guest account = <replaceable>name</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the user that guests should use (default is
+            "nobody"). The name should be quoted.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>home name = <replaceable>name</replaceable>
+          <type>(H)</type></term>
+
+          <listitem>
+            <para>AFP user home volume name. The default is <emphasis>user's
+            home</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ignored attributes = <replaceable>all | nowrite | nodelete | norename</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Speficy a set of file and directory attributes that shall
+            be ignored by the server, <option>all</option> includes all
+            the other options.</para>
+            <para>In OS X when the Finder sets a lock on a file/directory or you
+            set the BSD uchg flag in the Terminal, all three attributes are
+            used. Thus in order to ignore the Finder lock/BSD uchg flag, add
+            set <emphasis>ignored attributes = all</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>login message = <replaceable>message</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Sets a message to be displayed when clients logon to the
+            server. The message should be in <option>unix charset</option> and
+            should be quoted. Extended characters are allowed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>mimic model = <replaceable>model</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the icon model that appears on clients. Defaults
+            to off. Note that afpd must support Zeroconf.
+            Examples: RackMac (same as Xserve), PowerBook, PowerMac,
+            Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro,
+            AppleTV1,1, AirPort.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>signature = &lt;text&gt; <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify a server signature. The maximum length is 16
+            characters. This option is useful for clustered environments, to
+            provide fault isolation etc. By default, afpd generate signature
+            and saving it to
+            <filename>@localstatedir@/netatalk/afp_signature.conf</filename>
+            automatically (based on random number). See also
+            asip-status.pl(1).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>solaris share reservations =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Use share reservations on Solaris. Solaris CIFS server uses
+            this too, so this makes a lock coherent multi protocol
+            server.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>sparql results limit =
+          <replaceable>NUMBER</replaceable> (default:
+          <emphasis>UNLIMITED</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Impose a limit on the number of results queried from Tracker
+           via SPARQL queries.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>spotlight =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Whether to enable Spotlight searches. Note: once the global
+            option is enabled, any volume that is not enabled won't be
+            searchable at all. See also <emphasis>dbus daemon</emphasis>
+            option.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>spotlight attributes =
+          <replaceable>COMMA SEPERATED STRING</replaceable> (default:
+          <emphasis>EMPTY</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>A list of attributes that are allowed to be used in
+            Spotlight searches. By default all attributes can be
+            searched, passing a string limits attributes to elements
+            of the string. Example: <programlisting>spotlight
+            attributes = *,kMDItemTextContent</programlisting>
+           </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>spotlight expr =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to allow the use of logic expression in
+            searches.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>start dbus =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to start a dbus instance for use with Tracker.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>start tracker =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to start Tracker with 
+            <emphasis>tracker-control -s</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>veto message = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Send optional AFP messages for vetoed files. Then whenever a
+            client tries to access any file or directory with a vetoed name,
+            it will be sent an AFP message indicating the name and the
+            directory.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol dbpath = <replaceable>path</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Sets the database information to be stored in path. You have
+            to specify a writable location, even if the volume is read only.
+            The default is
+            <filename>@localstatedir@/netatalk/CNID/$v/</filename>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol dbnest = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Setting this option to true brings back Netatalk 2
+            behaviour of storing the CNID database in a folder called
+            .AppleDB inside the volume root of each share.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>volnamelen = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Max length of UTF8-MAC volume name for Mac OS X. Note that
+            Hangul is especially sensitive to this.</para>
+
+            <para><programlisting> 73: limit of Mac OS X 10.1 80: limit of Mac
+            OS X 10.4/10.5 (default) 255: limit of recent Mac OS
+            X</programlisting> Mac OS 9 and earlier are not influenced by
+            this, because Maccharset volume name is always limited to 27
+            bytes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol preset = <replaceable>name</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Use section <option>name</option> as option preset for all
+            volumes (when set in the [Global] section) or for one volume (when
+            set in that volume's section).</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Logging Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>log file = <replaceable>logfile</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>If not specified Netatalk logs to syslogs daemon facility.
+            Otherwise it logs to <option>logfile</option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>log level = <replaceable>type:level [type:level
+          ...]</replaceable> <type>(G)</type></term>
+
+          <term>log level = <replaceable>type:level,[type:level,
+          ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify that any message of a loglevel up to the given
+            <option>log level</option> should be logged.</para>
+
+            <para>By default afpd logs to syslog with a default logging setup
+            equivalent to <option>default:note</option></para>
+
+            <para>logtypes: default, afpdaemon, logger, uamsdaemon</para>
+
+            <para>loglevels: severe, error, warn, note, info, debug, debug6,
+            debug7, debug8, debug9, maxdebug</para>
+
+            <note>
+              <para>Both logtype and loglevels are case insensitive.</para>
+            </note>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2 id="fceconf">
+      <title>Filesystem Change Events (FCE<indexterm>
+          <primary>FCE</primary>
+        </indexterm>)</title>
+
+      <para>Netatalk includes a nifty filesystem change event mechanism where
+      afpd processes notify interested listeners about certain filesystem
+      event by UDP network datagrams.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>fce listener = <replaceable>host[:port]</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables sending FCE events to the specified
+            <parameter>host</parameter>, default <parameter>port</parameter>
+            is 12250 if not specified. Specifying multiple listeners is done
+            by having this option once for each of them.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce events =
+          <replaceable>fmod,fdel,ddel,fcre,dcre,tmsz</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies which FCE events are active, default is
+            <parameter>fmod,fdel,ddel,fcre,dcre</parameter>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce coalesce = <replaceable>all|delete|create</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Coalesce FCE events.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce holdfmod = <replaceable>seconds</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>This determines the time delay in seconds which is always
+            waited if another file modification for the same file is done by a
+            client before sending an FCE file modification event (fmod). For
+            example saving a file in Photoshop would generate multiple events
+            by itself because the application is opening, modifying and
+            closing a file multiple times for every "save". Default: 60
+            seconds.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Debug Parameters</title>
+
+      <para>These options are useful for debugging only.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>tickleval = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the tickle timeout interval (in seconds). Defaults to
+            30.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>timeout = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify the number of tickles to send before timing out a
+            connection. The default is 4, therefore a connection will timeout
+            after 2 minutes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>client polling = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>With this option enabled, afpd won't advertise that it is
+            capable of server notifications, so that connected clients poll
+            the server every 10 seconds to detect changes in opened server
+            windows. <emphasis>Note</emphasis>: Depending on the number of
+            simultaneously connected clients and the network's speed, this can
+            lead to a significant higher load on your network!</para>
+
+            <para>Do not use this option any longer as present Netatalk
+            correctly supports server notifications, allowing connected
+            clients to update folder listings in case another client changed
+            the contents.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2 id="acl_options">
+      <title>Options for ACL handling</title>
+
+      <para>By default, the effective permission of the authenticated user are
+      only mapped to the mentioned UARights permission structure, not the UNIX
+      mode. You can adjust this behaviour with the configuration option
+      <option>mac acls</option>:</para>
+
+      <variablelist id="map_acls">
+        <varlistentry>
+          <term>map acls = <parameter>none|rights|mode</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para><variablelist>
+                <varlistentry>
+                  <term>none</term>
+
+                  <listitem>
+                    <para>no mapping of ACLs </para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>rights</term>
+
+                  <listitem>
+                    <para>effective permissions are mapped to UARights
+                    structure. This is the default.</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>mode</term>
+
+                  <listitem>
+                    <para>ACLs are additionally mapped to the UNIX mode of the
+                    filesystem object.</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>If you want to be able to display ACLs on the client, you must
+      setup both client and server as part on a authentication domain
+      (directory service, eg LDAP, Open Directory, Active Directory). The
+      reason is, in OS X ACLs are bound to UUIDs, not just uid's or gid's.
+      Therefor Netatalk must be able to map every filesystem uid and gid to a
+      UUID so that it can return the server side ACLs which are bound to UNIX
+      uid and gid mapped to OS X UUIDs.</para>
+
+      <para>Netatalk can query a directory server using LDAP queries. Either
+      the directory server already provides an UUID attribute for user and
+      groups (Active Directory, Open Directory) or you reuse an unused
+      attribute (or add a new one) to you directory server (eg
+      OpenLDAP).</para>
+
+      <para>The following LDAP options must be configured for Netatalk:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>ldap auth method = <parameter>none|simple|sasl</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Authentication method: <option>none | simple |
+            sasl</option></para>
+
+            <para><variablelist>
+                <varlistentry>
+                  <term>none</term>
+
+                  <listitem>
+                    <para>anonymous LDAP bind</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>simple</term>
+
+                  <listitem>
+                    <para>simple LDAP bind</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>sasl</term>
+
+                  <listitem>
+                    <para>SASL. Not yet supported !</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap auth dn = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Distinguished Name of the user for simple bind.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap auth pw = <parameter>password</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Distinguished Name of the user for simple bind.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap server = <parameter>host</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name or IP address of your LDAP Server. This is only needed
+            for explicit ACL support in order to be able to query LDAP for
+            UUIDs.</para>
+
+            <para>You can use <citerefentry>
+                <refentrytitle>afpldaptest</refentrytitle>
+
+                <manvolnum>1</manvolnum>
+              </citerefentry> to syntactically check your config.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap userbase = <parameter>base dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>DN of the user container in LDAP.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap userscope = <parameter>scope</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Search scope for user search: <option>base | one |
+            sub</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap groupbase = <parameter>base dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>DN of the group container in LDAP.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap groupscope = <parameter>scope</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Search scope for user search: <option>base | one |
+            sub</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the UUIDs.</para>
+
+            <para>Note: this is used both for users and groups.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap name attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the users short name.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap group attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the groups short
+            name.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid string = <parameter>STRING</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Format of the uuid string in the directory. A series of x
+            and -, where every x denotes a value 0-9a-f and every - is a
+            separator.</para>
+
+            <para>Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid encoding = <parameter>string | ms-guid (default:
+          string)</parameter> <type>(G)</type></term>
+
+          <listitem>
+            <para>Format of the UUID of the LDAP attribute, allows usage of
+            the binary objectGUID fields from Active Directory. If left
+            unspecified, string is the default, which passes through the ASCII
+            UUID returned by most other LDAP stores. If set to ms-guid, the
+            internal UUID representation is converted to and from the binary
+            format used in the objectGUID attribute found on objects in Active
+            Directory when interacting with the server.</para>
+            <para>See also the options <option>ldap user filter</option> and
+            <option>ldap group filter</option>.</para>
+            <para><variablelist>
+                <varlistentry>
+                  <term>string</term>
+
+                  <listitem>
+                    <para>UUID is a string, use with eg OpenDirectory.</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>ms-guid</term>
+
+                  <listitem>
+                    <para>Binary objectGUID from Active Directory</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap user filter = <parameter>STRING (default: unused)</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Optional LDAP filter that matches user objects. This is necessary for Active Directory
+            environments where users and groups are stored in the same directory subtree.</para>
+            <para>Recommended setting for Active Directory: <parameter>objectClass=user</parameter>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap group filter = <parameter>STRING (default: unused)</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Optional LDAP filter that matches group objects. This is necessary for Active Directory
+            environments where users and groups are stored in the same directory subtree.</para>
+            <para>Recommended setting for Active Directory: <parameter>objectClass=group</parameter>.</para>
+          </listitem>
+        </varlistentry>
+
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>EXPLANATION OF VOLUME PARAMETERS</title>
+
+    <refsect2>
+      <title>Parameters</title>
+
+      <para>The section name defines the volume name.
+      No two volumes may have the same
+      name. The volume name cannot contain the <keycode>':'</keycode>
+      character. The volume name is mangled if it is very long. Mac charset
+      volume name is limited to 27 characters. UTF8-MAC volume name is limited
+      to volnamelen parameter.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>path = <replaceable>PATH</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>The path name must be a fully qualified path name.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>appledouble = <replaceable>ea|v2</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Specify the format of the metadata files, which are used for
+            saving Mac resource fork as well. Earlier versions used
+            AppleDouble v2, the new default format is <emphasis
+            role="bold">ea</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol size limit = <replaceable>size in MiB</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Useful for Time Machine: limits the reported volume size,
+            thus preventing Time Machine from using the whole real disk space
+            for backup. Example: "vol size limit = 1000" would limit the
+            reported disk space to 1 GB. <emphasis role="bold">IMPORTANT:
+            </emphasis> This is an approximated calculation taking into
+            account the contents of Time Machine sparsebundle images. Therefor
+            you MUST NOT use this volume to store other content when using
+            this option, because it would NOT be accounted. The calculation
+            works by reading the band size from the Info.plist XML file of the
+            sparsebundle, reading the bands/ directory counting the number of
+            band files, and then multiplying one with the other.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>valid users = <replaceable>user @group</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>The allow option allows the users and groups that access a
+            share to be specified. Users and groups are specified, delimited
+            by spaces or commas. Groups are designated by a @ prefix. Names
+            may be quoted in order to allow for spaces in names. Example:
+            <programlisting>valid users = user "user 2" @group “@group 2"</programlisting></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>invalid users = <replaceable>users/groups</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>The deny option specifies users and groups who are not
+            allowed access to the share. It follows the same format as the
+            "valid users" option.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hosts allow = <replaceable>IP host address/IP netmask bits [
+          ... ]</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>Only listed hosts and networks are allowed, all others are
+            rejected. The network address may be specified either in
+            dotted-decimal format for IPv4 or in hexadecimal format for
+            IPv6.</para>
+
+            <para>Example: hosts allow = 10.1.0.0/16 10.2.1.100
+            2001:0db8:1234::/48</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hosts deny = <replaceable>IP host address/IP netmask bits [
+          ... ]</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>Listed hosts and nets are rejected, all others are
+            allowed.</para>
+
+            <para>Example: hosts deny = 192.168.100/24 10.1.1.1
+            2001:db8::1428:57ab</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid scheme = <replaceable>backend</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>set the CNID backend to be used for the volume, default is
+            [@DEFAULT_CNID_SCHEME@] available schemes:
+            [@compiled_backends@]</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ea = <replaceable>none|auto|sys|ad</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Specify how Extended Attributes<indexterm>
+                <primary>Extended Attributes</primary>
+              </indexterm> are stored. <option>auto</option> is the
+            default.</para>
+
+            <variablelist>
+              <varlistentry>
+                <term>auto</term>
+
+                <listitem>
+                  <para>Try <option>sys</option> (by setting an EA on the
+                  shared directory itself), fallback to <option>ad</option>.
+                  Requires writable volume for performing test. "<option>read
+                  only = yes</option>" overwrites <option>auto</option> with
+                  <option>none</option>. Use explicit "<option>ea =
+                  sys|ad</option>" for read-only volumes where
+                  appropriate.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>sys</term>
+
+                <listitem>
+                  <para>Use filesystem Extended Attributes.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>ad</term>
+
+                <listitem>
+                  <para>Use files in <emphasis>.AppleDouble</emphasis>
+                  directories.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>none</term>
+
+                <listitem>
+                  <para>No Extended Attributes support.</para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>mac charset = <replaceable>CHARSET</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>specifies the Mac client charset for this Volume, e.g.
+            <emphasis>MAC_ROMAN</emphasis>, <emphasis>MAC_CYRILLIC</emphasis>.
+            If not specified the global setting is applied. This setting is
+            only required if you need volumes, where the Mac charset differs
+            from the one globally set in the [Global] section.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>casefold = <option>option</option> <type>(V)</type></term>
+
+          <listitem>
+            <para>The casefold option handles, if the case of filenames should
+            be changed. The available options are:</para>
+
+            <para><option>tolower</option> - Lowercases names in both
+            directions.</para>
+
+            <para><option>toupper</option> - Uppercases names in both
+            directions.</para>
+
+            <para><option>xlatelower</option> - Client sees lowercase, server
+            sees uppercase.</para>
+
+            <para><option>xlateupper</option> - Client sees uppercase, server
+            sees lowercase.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>password = <replaceable>password</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>This option allows you to set a volume password, which can
+            be a maximum of 8 characters long (using ASCII strongly
+            recommended at the time of this writing).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>file perm = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <term>directory perm = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Add(or) with the client requested permissions: <option>file
+            perm</option> is for files only, <option>directory perm</option>
+            is for directories only. Don't use with "<option>unix priv =
+            no</option>".</para>
+
+            <example>
+              <title>Volume for a collaborative workgroup</title>
+
+              <para><programlisting>file perm = 0660 directory perm =
+              0770</programlisting></para>
+            </example>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>umask = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>set perm mask. Don't use with "<option>unix priv =
+            no</option>".</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>preexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run when the volume is mounted, ignored for
+            user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>postexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run when the volume is closed, ignored for
+            user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root preexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run as root when the volume is mounted,
+            ignored for user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root postexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run as root when the volume is closed, ignored
+            for user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>rolist = <option>users/groups</option> <type>(V)</type></term>
+
+          <listitem>
+            <para>Allows certain users and groups to have read-only access to
+            a share. This follows the allow option format.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>rwlist = <replaceable>users/groups</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Allows certain users and groups to have read/write access to
+            a share. This follows the allow option format.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>veto files = <replaceable>vetoed names</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>hide files and directories,where the path matches one of the
+            '/' delimited vetoed names. The veto string must always be
+            terminated with a '/', eg. "veto files = veto1/", "veto files =
+            veto1/veto2/".</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Volume options</title>
+
+      <para>Boolean volume options.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>acls = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to flag volumes as supporting ACLs. If ACL support
+            is compiled in, this is yes by default.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid dev = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to use the device number in the CNID backends. Helps
+            when the device number is not constant across a reboot, eg
+            cluster, ...</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>convert appledouble = <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether automatic conversion from <option>appledouble =
+            v2</option> to <option>appledouble = ea</option> is performed when
+            accessing filesystems from clients. This is generally useful, but
+            costs some performance. It's recommendable to run
+            <command>dbd</command> on volumes and do the conversion with that.
+            Then this option can be set to no.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>delete veto files = <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>This option is used when Netatalk is attempting to delete a
+            directory that contains one or more vetoed files or directories
+            (see the veto files option). If this option is set to no (the
+            default) then if a directory contains any non-vetoed files or
+            directories then the directory delete will fail. This is usually
+            what you want.</para>
+            <para>If this option is set to yes, then Netatalk will attempt to
+            recursively delete any files and directories within the vetoed
+            directory.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>follow symlinks = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>The default setting is false thus symlinks are not followed
+            on the server. This is the same behaviour as OS X's AFP server.
+            Setting the option to true causes afpd to follow symlinks on the
+            server. symlinks may point outside of the AFP volume, currently
+            afpd doesn't do any checks for "wide symlinks".</para>
+            <note>
+              <para>This option will subtly break when the symlinks point
+              across filesystem boundaries.</para>
+            </note>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>invisible dots = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>make dot files invisible. WARNING: enabling this option will
+              lead to unwanted sideeffects were OS X applications when saving
+              files to a temporary file starting with a dot first, then renaming
+              the temp file to its final name, result in the saved file being
+              invisible. The only thing this option is useful for is making
+              files that start with a dot invisible on Mac OS 9. It's
+              completely useless on Mac OS X, as both in Finder and in Terminal
+              files starting with a dot are hidden anyway.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>network ids = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether the server support network ids. Setting this to
+            <emphasis>no</emphasis> will result in the client not using ACL
+            AFP functions.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>preexec close = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>A non-zero return code from preexec close the volume being
+            immediately, preventing clients to mount/see the volume in
+            question.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>read only = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Specifies the share as being read only for all users.
+            Overwrites <option>ea = auto</option> with <option>ea =
+            none</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root preexec close= <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>A non-zero return code from root_preexec closes the volume
+            immediately, preventing clients to mount/see the volume in
+            question.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>search db = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Use fast CNID database namesearch instead of slow recursive
+            filesystem search. Relies on a consistent CNID database, ie Samba
+            or local filesystem access lead to inaccurate or wrong results.
+            Works only for "dbd" CNID db volumes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>stat vol = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to stat volume path when enumerating volumes list,
+            useful for automounting or volumes created by a preexec
+            script.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>time machine = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to enable Time Machine support for this
+            volume.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>unix priv = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to use AFP3 UNIX privileges. This should be set for
+            OS X clients. See also: <option>file perm</option>,
+            <option>directory perm</option> and <option>umask</option>.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID backends</title>
+
+    <para>The AFP protocol mostly refers to files and directories by ID and
+    not by name. Netatalk needs a way to store these ID's in a persistent way,
+    to achieve this several different CNID backends are available. The CNID
+    Databases are by default located in the
+    <filename>@localstatedir@/netatalk/CNID/(volumename)/.AppleDB/</filename>
+    directory.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>cdb</term>
+
+        <listitem>
+          <para>"Concurrent database", backend is based on Oracle Berkley DB.
+          With this backend several <command>afpd</command> daemons access the
+          CNID database directly. Berkeley DB locking is used to synchronize
+          access, if more than one <command>afpd</command> process is active
+          for a volume. The drawback is, that the crash of a single
+          <command>afpd</command> process might corrupt the database.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>dbd</term>
+
+        <listitem>
+          <para>Access to the CNID database is restricted to the
+          <command>cnid_metad</command> daemon process.
+          <command>afpd</command> processes communicate with the daemon for
+          database reads and updates. If built with Berkeley DB transactions
+          the probability for database corruption is practically zero, but
+          performance can be slower than with <option>cdb</option></para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>last</term>
+
+        <listitem>
+          <para>This backend is an exception, in terms of ID persistency. ID's
+          are only valid for the current session. This is basically what
+          <command>afpd</command> did in the 1.5 (and 1.6) versions. This
+          backend is still available, as it is useful for e.g. sharing cdroms.
+          Starting with Netatalk 3.0, it becomes the <emphasis>read only
+          mode</emphasis> automatically.</para>
+
+          <para><emphasis role="bold">Warning</emphasis>: It is
+          <emphasis>NOT</emphasis> recommended to use this backend for volumes
+          anymore, as <command>afpd</command> now relies heavily on a
+          persistent ID database. Aliases will likely not work and filename
+          mangling is not supported.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Even though <command>./configure --help</command> might show that
+    there are other CNID backends available, be warned those are likely broken
+    or mainly used for testing. Don't use them unless you know what you're
+    doing, they may be removed without further notice from future
+    versions.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Charset options</title>
+
+    <para>With OS X Apple introduced the AFP3 protocol. One of the most
+    important changes was that AFP3 uses unicode names encoded as UTF-8
+    decomposed. Previous AFP/OS versions used codepages, like MacRoman,
+    MacCentralEurope, etc.</para>
+
+    <para><command>afpd</command> needs a way to preserve extended Macintosh
+    characters, or characters illegal in unix filenames, when saving files on
+    a unix filesystem. Earlier versions used the the so called CAP encoding.
+    An extended character (&gt;0x7F) would be converted to a :xx sequence,
+    e.g. the Apple Logo (MacRoman: 0xF0) was saved as <literal>:f0</literal>.
+    Some special characters will be converted as to :xx notation as well.
+    '<keycode>/</keycode>' will be encoded to <literal>:2f</literal>, if
+    <option>usedots</option> is not specified, a leading dot
+    '<keycode>.</keycode>' will be encoded as <literal>:2e</literal>.</para>
+
+    <para>This version now uses UTF-8 as the default encoding for names.
+    '<keycode>/</keycode>' will be converted to '<keycode>:</keycode>'.</para>
+
+    <para>The <option>vol charset</option> option will allow you to select
+    another volume encoding. E.g. for western users another useful setting
+    could be vol charset ISO-8859-15. <command>afpd</command> will accept any
+    <citerefentry>
+        <refentrytitle><command>iconv</command></refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry> provided charset. If a character cannot be converted
+    from the <option>mac charset</option> to the selected <option>vol
+    charset</option>, afpd will save it as a CAP encoded character. For AFP3
+    clients, <command>afpd</command> will convert the UTF-8<indexterm>
+        <primary>UTF8</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>UTF8-MAC</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>ISO-8859-15</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>ISO-8859-1</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm> character to <option>mac charset</option> first. If this
+    conversion fails, you'll receive a -50 error on the mac.</para>
+
+    <para><emphasis>Note</emphasis>: Whenever you can, please stick with the
+    default UTF-8 volume format.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afppasswd</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_signature.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>extmap.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/afp_signature.conf.5.xml b/doc/manpages/man5/afp_signature.conf.5.xml
new file mode 100644 (file)
index 0000000..287b6d7
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp_signature.conf.5">
+
+  <refmeta>
+    <refentrytitle>afp_signature.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp_signature.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to specify server
+        signature<indexterm>
+        <primary>afp_signature.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>@localstatedir@/netatalk/afp_signature.conf</filename> is the
+    configuration file used by <command>afpd</command> to specify
+    server signature automagically. The configuration lines are
+    composed like:</para>
+
+    <para><replaceable>"server name"</replaceable>
+    <replaceable>hexa-string</replaceable></para>
+
+    <para>The first field is server name. Server names must be quoted
+    if they contain spaces. The second field is the hexadecimal string
+    of 32 characters for 16-bytes server signature.</para>
+    <para>The leading spaces and tabs are ignored. Blank lines are ignored.
+    The lines prefixed with # are ignored. The illegal lines are ignored.
+    </para>
+
+    <note>
+        <para>Server Signature is unique 16-bytes identifier used to
+        prevent logging on to the same server twice. </para>
+        <para>Netatalk 2.0 and earlier generated server signature by using
+        gethostid(). There was a problem that another servers have the same
+        signature because the hostid is not unique enough.</para>
+        <para>Current netatalk generates the signature from random numbers and
+        saves it into afp_signature.conf. When starting next time, it
+        is read from this file. </para>
+        <para>This file should not be thoughtlessly edited and be copied
+        onto another server. If it wants to set the signature intentionally,
+        use the option "signature ="  in afp.conf. In this case,
+        afp_signature.conf is not used.</para>
+    </note>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>afp_signature.conf</title>
+
+      <programlisting># This is a comment.
+"My Server" 74A0BB94EC8C13988B2E75042347E528</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>asip-status.pl</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/afp_voluuid.conf.5.xml b/doc/manpages/man5/afp_voluuid.conf.5.xml
new file mode 100644 (file)
index 0000000..0956b47
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp_voluuid.conf.5">
+
+  <refmeta>
+    <refentrytitle>afp_voluuid.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp_voluuid.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to specify UUID
+        for Time Machine volume<indexterm>
+        <primary>afp_voluuid.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>@localstatedir@/netatalk/afp_voluuid.conf</filename> is the
+    configuration file used by <command>afpd</command> to specify
+    UUID of Time Machine volume automagically. The configuration
+    lines are composed like:</para>
+
+    <para><replaceable>"volume name"</replaceable>
+    <replaceable>uuid-string</replaceable></para>
+
+    <para>The first field is volume name. Volume names must be quoted
+    if they contain spaces. The second field is the 36 character
+    hexadecimal ASCII string representation of a UUID.</para>
+    <para>The leading spaces and tabs are ignored. Blank lines are ignored.
+    The lines prefixed with # are ignored. The illegal lines are ignored.
+    </para>
+
+    <note>
+        <para>This UUID is advertised by Zeroconf in order to provide
+        robust disambiguation of Time Machine volume.</para>
+        <para>The afpd generates the UUID from random numbers and saves it
+        into afp_voluuid.conf, only when setting "time machine = yes" option
+        in afp.conf.</para>
+        <para>This file should not be thoughtlessly edited and be copied
+        onto another server.</para>
+    </note>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>afp_voluuid.conf three TM volumes on one netatalk</title>
+
+      <programlisting># This is a comment.
+"Backup for John Smith" 1573974F-0ABD-69CC-C40A-8519B681A0E1
+"bob" 39A487F4-55AA-8240-E584-69AA01800FE9
+mary 6331E2D1-446C-B68C-3066-D685AADBE911</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>avahi-daemon</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>mDNSResponder</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/extmap.conf.5.xml b/doc/manpages/man5/extmap.conf.5.xml
new file mode 100644 (file)
index 0000000..4265928
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="extmap.conf.5">
+  <refmeta>
+    <refentrytitle>extmap.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>extmap.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to
+    specify file name extension mappings.<indexterm>
+        <primary>extmap.conf</primary>
+      </indexterm>
+      </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>@pkgconfdir@/extmap.conf<indexterm><primary>extmap.conf</primary></indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+    <filename>@pkgconfdir@/extmap.conf</filename> is the
+    configuration file used by <command>afpd</command> to
+    specify file name extension mappings.</para>
+
+    <para>The configuration lines are composed like:</para>
+
+    <para><filename>.extension</filename> <replaceable>[ type [
+    creator ] ]</replaceable></para>
+
+    <para>Any line beginning with a hash (“#”) character is ignored.
+    The leading-dot lines specify file name extension mappings.
+    The extension '.' sets the default creator and type for otherwise
+    untyped Unix files.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Extension is jpg. Type is "JPEG". Creator is "ogle".</title>
+
+            <programlisting>.jpg "JPEG" "ogle"</programlisting>
+          </example>
+
+    <example>
+      <title>Extension is lzh. Type is "LHA ". Creator is not defined.</title>
+
+            <programlisting>.lzh "LHA "</programlisting>
+          </example>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/.gitignore b/doc/manpages/man8/.gitignore
new file mode 100644 (file)
index 0000000..c756d8f
--- /dev/null
@@ -0,0 +1 @@
+*\.8
diff --git a/doc/manpages/man8/Makefile.am b/doc/manpages/man8/Makefile.am
new file mode 100644 (file)
index 0000000..465977c
--- /dev/null
@@ -0,0 +1,26 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       afpd.8 \
+       cnid_dbd.8 \
+       cnid_metad.8 \
+       netatalk.8
+
+EXTRA_DIST = \
+       afpd.8.xml \
+       cnid_dbd.8.xml \
+       cnid_metad.8.xml \
+       netatalk.8.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.8 : %.8.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man8/$@.in
+
+html-local: $(MAN_MANPAGES) $(MAN_STYLESHEET)
+endif
\ No newline at end of file
diff --git a/doc/manpages/man8/afpd.8.xml b/doc/manpages/man8/afpd.8.xml
new file mode 100644 (file)
index 0000000..fff2758
--- /dev/null
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpd.8">
+  <refmeta>
+    <refentrytitle>afpd</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afpd</refname>
+
+    <refpurpose>Apple Filing Protocol daemon</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>afpd<indexterm>
+          <primary>afpd</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+
+      <arg choice="opt">-F <replaceable>configfile</replaceable></arg>
+
+      <sbr />
+
+      <command>afpd<indexterm>
+          <primary>afpd</primary>
+        </indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-V</arg>
+        <arg choice="plain">-h</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>afpd</command> provides an Apple Filing Protocol (AFP)
+    interface to the Unix file system. It is normally started at boot time
+    by <command>netatalk</command>(8).</para>
+
+    <para><filename>@pkgconfdir@/afp.conf</filename> is the configuration file
+    used by <command>afpd</command> to determine the behavior and
+    configuration of a file server.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>Specifies that the daemon should not fork.</para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Print version information and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>Print verbose information and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-h</term>
+
+        <listitem>
+          <para>Print help and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-F <replaceable>configfile</replaceable></term>
+
+        <listitem>
+          <para>Specifies the configuration file to use. (Defaults to
+          <filename>@pkgconfdir@/afp.conf</filename>.)</para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SIGNALS</title>
+
+    <para>To shut down a user's <command>afpd</command> process it is
+      recommended that <command>SIGKILL (-9)</command>
+      <emphasis>NOT</emphasis> be used, except as a last resort, as this
+      may leave the CNID database in an inconsistent state. The safe way
+      to terminate an <command>afpd</command> is to send it a
+      <command>SIGTERM (-15)</command> signal and wait for it to die on
+      its own.</para>
+    <para>SIGTERM and SIGUSR1 signals that are sent to the main <command>afpd</command> process
+    are propagated to the children, so all will be affected.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>SIGTERM</term>
+        <listitem>
+          <para>Clean exit. Propagates from master to childs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGQUIT</term>
+        <listitem>
+          <para>Send this to the master <command>afpd</command>, it will
+          exit leaving all children running! Can be used to implement
+          AFP service without downtime.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGHUP</term>
+        <listitem>
+          <para>Sending a <command>SIGHUP</command> to afpd will cause it to
+          reload its configuration files.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGINT</term>
+        <listitem>
+          <para>Sending a <command>SIGINT</command> to a child
+          <command>afpd</command> enables <emphasis>max_debug</emphasis>
+          logging for this process. The log is sent to the file
+          <filename>/tmp/afpd.PID.XXXXXX</filename>. Sending another
+          <command>SIGINT</command> will revert to the original log settings.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGUSR1</term>
+        <listitem>
+          <para>The <command>afpd</command> process will send the message "The
+          server is going down for maintenance." to the client and shut itself
+          down in 5 minutes. New connections are not allowed. If this is sent
+          to a child afpd, the other children are not affected. However, the
+          main process will still exit, disabling all new connections.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGUSR2</term>
+        <listitem>
+          <para>The <command>afpd</command> process will look in the message
+          directory configured at build time for a file named message.pid. For
+          each one found, a the contents will be sent as a message to the
+          associated AFP client. The file is removed after the message is
+          sent. This should only be sent to a child
+          <command>afpd</command>.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><filename>@pkgconfdir@/afp.conf</filename></term>
+
+        <listitem>
+          <para>configuration file used by afpd</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@localstatedir@/netatalk/afp_signature.conf</filename></term>
+
+        <listitem>
+          <para>list of server signature</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@localstatedir@/netatalk/afp_voluuid.conf</filename></term>
+
+        <listitem>
+          <para>list of UUID for Time Machine volume</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@pkgconfdir@/extmap.conf</filename></term>
+
+        <listitem>
+          <para>file name extension mapping</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@pkgconfdir@/msg/message.pid</filename></term>
+
+        <listitem>
+          <para>contains messages to be sent to users.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>netatalk</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>hosts_access</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_signature.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_voluuid.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>extmap.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/cnid_dbd.8.xml b/doc/manpages/man8/cnid_dbd.8.xml
new file mode 100644 (file)
index 0000000..9162047
--- /dev/null
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="cnid_dbd.8">
+  <refmeta>
+    <refentrytitle>cnid_dbd</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">01 Jan 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>cnid_dbd</refname>
+
+    <refpurpose>implement access to CNID databases through a dedicated daemon
+    process</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>cnid_dbd<indexterm>
+          <primary>cnid_dbd</primary>
+        </indexterm><indexterm>
+          <primary>CNID backend</primary>
+        </indexterm><indexterm>
+          <primary>NFS</primary>
+
+          <secondary>Network File System</secondary>
+        </indexterm></command>
+
+      <sbr />
+
+      <command>cnid_dbd<indexterm>
+          <primary>cnid_dbd</primary>
+        </indexterm></command>
+
+        <group choice="plain">
+          <arg choice="plain">-v</arg>
+          <arg choice="plain">-V</arg>
+        </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>cnid_dbd</command> provides an interface for storage and
+    retrieval of catalog node IDs (CNIDs) and related information to the
+    <emphasis remap="B">afpd</emphasis> daemon. CNIDs are a component of
+    Macintosh based file systems with semantics that map not easily onto Unix
+    file systems. This makes separate storage in a database necessary.
+    <command>cnid_dbd</command> is part of the <emphasis remap="B">CNID
+    backend</emphasis> framework of <emphasis remap="B">afpd</emphasis> and
+    implements the <emphasis remap="B">dbd</emphasis> backend.</para>
+
+    <para><command>cnid_dbd</command> is never started via the command line or
+    system startup scripts but only by the <emphasis
+    remap="B">cnid_metad</emphasis> daemon. There is one instance of
+    <command>cnid_dbd</command> per netatalk volume.</para>
+
+    <para><command>cnid_dbd</command> uses the <emphasis remap="B">Berkeley
+    DB</emphasis> database library and uses transactionally protected updates.
+    The <emphasis remap="B">dbd</emphasis> backend with transactions will
+    avoid corruption of the CNID database even if the system crashes
+    unexpectedly.</para>
+
+    <para><command>cnid_dbd</command> inherits the effective userid and
+    groupid from <emphasis remap="B">cnid_metad</emphasis> on startup, which
+    is normally caused by <emphasis remap="B">afpd</emphasis> serving a
+    netatalk volume to a client. It changes to the <emphasis
+    remap="B">Berkeley DB</emphasis> database home directory <emphasis
+    remap="I">dbdir</emphasis> that is associated with the volume. If the
+    userid inherited from <emphasis remap="B">cnid_metad</emphasis> is 0
+    (root), <command>cnid_dbd</command> will change userid and groupid to the
+    owner and group of the database home directory. Otherwise, it will
+    continue to use the inherited values. <command>cnid_dbd</command> will
+    then attempt to open the database and start serving requests using
+    filedescriptor <emphasis remap="I">clntfd</emphasis>. Subsequent instances
+    of <emphasis remap="B">afpd</emphasis> that want to access the same volume
+    are redirected to the running <command>cnid_dbd</command> process by
+    <emphasis remap="B">cnid_metad</emphasis> via the filedescriptor <emphasis
+    remap="I">ctrlfd</emphasis>.</para>
+
+    <para><command>cnid_dbd</command> can be configured to run forever or to
+    exit after a period of inactivity. If <command>cnid_dbd</command> receives
+    a TERM or an INT signal it will exit cleanly after flushing dirty database
+    buffers to disk and closing <emphasis remap="B">Berkeley DB</emphasis>
+    database environments. It is safe to terminate <command>cnid_dbd</command>
+    this way, it will be restarted when necessary. Other signals are not
+    handled and will cause an immediate exit, possibly leaving the CNID
+    database in an inconsistent state (no transactions) or losing recent
+    updates during recovery (transactions).</para>
+
+    <para>The <emphasis remap="B">Berkeley DB</emphasis> database subsystem
+    will create files named log.xxxxxxxxxx in the database home directory
+    <emphasis remap="I">dbdir</emphasis>, where xxxxxxxxxx is a monotonically
+    increasing integer. These files contain the transactional database
+    changes. They will be removed regularly, unless the <emphasis
+    remap="B">logfile_autoremove</emphasis> option is specified in the
+    <emphasis remap="I">db_param</emphasis> configuration file (see
+    below) with a value of 0 (default 1).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, -V</option></term>
+       
+        <listitem>
+          <para>Show version and exit.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CONFIGURATION</title>
+
+    <para><command>cnid_dbd</command> reads configuration information from the
+    file <emphasis remap="I">db_param</emphasis> in the database directory
+    <emphasis remap="I">dbdir</emphasis> on startup. If the file does not
+    exist or a parameter is not listed, suitable default values are used. The
+    format for a single parameter is the parameter name, followed by one or
+    more spaces, followed by the parameter value, followed by a newline. The
+    following parameters are currently recognized:</para>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><emphasis remap="B">logfile_autoremove</emphasis></term>
+
+        <listitem>
+          <para>If set to 0, unused Berkeley DB transactional logfiles
+          (log.xxxxxxxxxx in the database home directory) are not removed on
+          startup of <command>cnid_dbd</command> and on a regular basis.
+          Default: 1.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">cachesize</emphasis></term>
+
+        <listitem>
+          <para>Determines the size of the Berkeley DB cache in kilobytes.
+          Default: 8192. Each <command>cnid_dbd</command> process grabs that
+          much memory on top of its normal memory footprint. It can be used to
+          tune database performance. The <emphasis
+          remap="B">db_stat</emphasis> utility with the <option>-m</option>
+          option that comes with Berkley DB can help you determine ether you
+          need to change this value. The default is pretty conservative so
+          that a large percentage of requests should be satisfied from the
+          cache directly. If memory is not a bottleneck on your system you
+          might want to leave it at that value. The <emphasis
+          remap="B">Berkeley DB Tutorial and Reference Guide</emphasis> has a
+          section <emphasis remap="B">Selecting a cache size</emphasis> that
+          gives more detailed information.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">flush_frequency</emphasis></term>
+
+        <term><emphasis remap="B">flush_interval</emphasis></term>
+
+        <listitem>
+          <para><emphasis remap="I">flush_frequency</emphasis> (Default: 1000)
+          and <emphasis remap="I">flush_interval</emphasis> (Default: 1800)
+          control how often changes to the database are checkpointed. Both of
+          these operations are performed if either i) more than <emphasis
+          remap="I">flush_frequency</emphasis> requests have been received or
+          ii) more than <emphasis remap="I">flush_interval</emphasis> seconds
+          have elapsed since the last save/checkpoint. Be careful to check
+          your harddisk configuration for on disk cache settings. Many IDE
+          disks just cache writes as the default behaviour, so even flushing
+          database files to disk will not have the desired effect.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">fd_table_size</emphasis></term>
+
+        <listitem>
+          <para>is the maximum number of connections (filedescriptors) that
+          can be open for <emphasis remap="B">afpd</emphasis> client processes
+          in <emphasis remap="B">cnid_dbd.</emphasis> Default: 512. If this
+          number is exceeded, one of the existing connections is closed and
+          reused. The affected <emphasis remap="B">afpd</emphasis> process
+          will transparently reconnect later, which causes slight overhead. On
+          the other hand, setting this parameter too high could affect
+          performance in <command>cnid_dbd</command> since all descriptors
+          have to be checked in a <function>select()</function> system call,
+          or worse, you might exceed the per process limit of open file
+          descriptors on your system. It is safe to set the value to 1 on
+          volumes where only one <emphasis remap="B">afpd</emphasis> client
+          process is expected to run, e.g. home directories.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">idle_timeout</emphasis></term>
+
+        <listitem>
+          <para>is the number of seconds of inactivity before an idle
+          <command>cnid_dbd</command> exits. Default: 600. Set this to 0 to
+          disable the timeout.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>UPDATING</title>
+
+    <para>Note that the first version to appear <emphasis>after</emphasis>
+    Netatalk 2.1 ie Netatalk 2.1.1, will support BerkeleyDB updates on the fly
+    without manual intervention. In other words Netatalk 2.1 does contain code
+    to prepare the BerkeleyDB database for upgrades and to upgrade it in case
+    it has been prepared before. That means it can't upgrade a 2.0.x version
+    because that one didn't prepare the database.</para>
+
+    <para>In order to update between older Netatalk releases using different
+    BerkeleyDB library versions, follow this steps:</para>
+
+    <itemizedlist>
+      <listitem>
+        <para>Stop the to be upgraded old version of Netatalk</para>
+      </listitem>
+
+      <listitem>
+        <para>Using the old BerkeleyDB utilities run <command>db_recover -h
+        &lt;path to .AppleDB&gt;</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Using the new BerkeleyDB utilities run <command>db_upgrade -v -h
+        &lt;path to .AppleDB&gt; -f cnid2.db</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Again using the new BerkeleyDB utilities run
+        <command>db_checkpoint -1 -h &lt;path to .AppleDB&gt;</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Start the the new version of Netatalk</para>
+      </listitem>
+    </itemizedlist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/cnid_metad.8.xml b/doc/manpages/man8/cnid_metad.8.xml
new file mode 100644 (file)
index 0000000..3bd833b
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="cnid_metad.8">
+  <refmeta>
+    <refentrytitle>cnid_metad</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>cnid_metad</refname>
+
+    <refpurpose>start cnid_dbd daemons on request</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>cnid_metad<indexterm>
+          <primary>cnid_metad</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+
+      <arg choice="opt"><arg choice="plain">-F </arg><arg
+      choice="plain"><replaceable>configuration file</replaceable></arg></arg>
+
+      <sbr />
+
+      <command>cnid_metad<indexterm>
+          <primary>cnid_metad</primary>
+        </indexterm></command>
+
+       <group choice="plain">
+         <arg choice="plain">-v</arg>
+         <arg choice="plain">-V</arg>
+       </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>cnid_metad</command> waits for requests from <emphasis
+    remap="B">afpd</emphasis> to start up instances of the <emphasis
+    remap="B">cnid_dbd</emphasis> daemon. It keeps track of the status of a
+    <emphasis remap="B">cnid_dbd</emphasis> instance once started and will
+    restart it if necessary. <command>cnid_metad</command> is normally started
+    at boot time by <command>netatalk</command>(8) and runs
+    until shutdown.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+     <varlistentry>
+        <term><option>-d</option></term>
+
+        <listitem>
+          <para><emphasis remap="B">cnid_metad will remain in the foreground
+          and</emphasis> will also leave the standard input, standard output
+          and standard error file descriptors open. Useful for
+          debugging.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-F</option> <replaceable>configuration file</replaceable></term>
+
+        <listitem>
+          <para>Use <emphasis remap="I">configuration file</emphasis> as the
+            configuration file. The default is
+            <emphasis remap="I">@pkgconfdir@/afp.conf</emphasis>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -V</option></term>
+
+        <listitem>
+          <para>Show version and exit.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CAVEATS</title>
+
+    <para><command>cnid_metad</command> does not block or catch any signals
+    apart from SIGPIPE. It will therefore exit on most signals received. This
+    will also cause all instances of <emphasis remap="B">cnid_dbd's</emphasis>
+    started by that <command>cnid_metad</command> to exit gracefully. Since
+    state about and IPC access to the subprocesses is only maintained in
+    memory by <command>cnid_metad</command> this is desired behaviour. As soon
+    as <command>cnid_metad</command> is restarted <emphasis
+    remap="B">afpd</emphasis> processes will transparently reconnect.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>netatalk</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_dbd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/netatalk.8.xml b/doc/manpages/man8/netatalk.8.xml
new file mode 100644 (file)
index 0000000..8c98011
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="netatalk.8">
+  <refmeta>
+    <refentrytitle>netatalk</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>netatalk</refname>
+
+    <refpurpose>Netatalk AFP server service controller daemon</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>netatalk<indexterm>
+          <primary>netatalk</primary>
+        </indexterm></command>
+
+      <sbr />
+
+      <command>netatalk<indexterm>
+          <primary>netatalk</primary>
+        </indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>netatalk</command> is the service controller daemon
+    responsible for starting and restarting the AFP daemon
+    <command>afpd</command> and the CNID daemon <command>cnid_metad</command>.
+    It is normally started at boot time from /etc/rc.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SIGNALS</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>SIGTERM</term>
+
+        <listitem>
+          <para>Stop Netatalk service, AFP and CNID daemons</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGHUP</term>
+
+        <listitem>
+          <para>Sending a <command>SIGHUP</command> will cause the AFP daemon
+          reload its configuration file.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><filename>@pkgconfdir@/afp.conf</filename></term>
+
+        <listitem>
+          <para>configuration file used by afpd and cnid_metad</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manual/.gitignore b/doc/manual/.gitignore
new file mode 100644 (file)
index 0000000..ab1918e
--- /dev/null
@@ -0,0 +1,2 @@
+manual.xml
+*.html
diff --git a/doc/manual/Makefile.am b/doc/manual/Makefile.am
new file mode 100644 (file)
index 0000000..64870a5
--- /dev/null
@@ -0,0 +1,63 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+HTML_STYLESHEET=$(top_srcdir)/doc/html.xsl
+CLEANFILES =
+
+XML_PAGES = \
+       manual.xml \
+       configuration.xml \
+       install.xml \
+       intro.xml \
+       upgrade.xml
+
+EXTRA_DIST = \
+       manual.xml.in \
+       configuration.xml \
+       install.xml \
+       intro.xml \
+       upgrade.xml \
+       netatalk.html
+
+HTML_PAGES = \
+       ad.1.html \
+       afp.conf.5.html \
+       afpd.8.html \
+       afpldaptest.1.html \
+       afppasswd.1.html \
+       afp_signature.conf.5.html  \
+       afpstats.1.html \
+       afp_voluuid.conf.5.html \
+       apple_dump.1.html \
+       asip-status.pl.1.html \
+       cnid_dbd.8.html \
+       cnid_metad.8.html \
+       configuration.html \
+       dbd.1.html \
+       example-toc.html \
+       extmap.conf.5.html \
+       index.html \
+       installation.html \
+       intro.html \
+       macusers.1.html \
+       man-pages.html \
+       manual-index.html \
+       megatron.1.html \
+       netatalk.8.html \
+       netatalkconfig.1.html \
+       pr01.html \
+       table-toc.html \
+       uniconv.1.html \
+       upgrade.html
+
+DISTCLEANFILES = manual.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(HTML_PAGES)
+
+html-local: $(XML_PAGES)
+       @xsltproc $(HTML_STYLESHEET) manual.xml
+
+html-upload: html-local
+       scp $(HTML_PAGES) $$USER,netatalk@web.sourceforge.net:/home/project-web/netatalk/htdocs/3.1/htmldocs/
+
+endif
diff --git a/doc/manual/configuration.xml b/doc/manual/configuration.xml
new file mode 100644 (file)
index 0000000..535dd5f
--- /dev/null
@@ -0,0 +1,1931 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="configuration">
+  <title>Setting up Netatalk</title>
+
+  <sect1>
+    <title>File Services<indexterm>
+        <primary>File Services</primary>
+
+        <secondary>Netatalk's File Services</secondary>
+      </indexterm></title>
+
+    <para>Netatalk supplies AFP<indexterm>
+        <primary>AFP</primary>
+
+        <secondary>Apple Filing Protocol</secondary>
+      </indexterm> services.</para>
+
+    <sect2>
+      <title>Setting up the AFP file server</title>
+
+      <para>AFP (the Apple Filing Protocol) is the protocol Apple Macintoshes
+      use for file services. The protocol has evolved over the years. The
+      latest changes to the protocol, called "AFP 3.3", were added with the
+      release of Snow Leopard<indexterm>
+          <primary>Snow Leopard</primary>
+
+          <secondary>Mac OS X 10.6</secondary>
+        </indexterm> (Mac OS X 10.6).</para>
+
+      <para>The afpd daemon offers the fileservices to Apple clients. The only
+      configuration file is <filename>afp.conf</filename>. It uses a ini style
+      configuration syntax.</para>
+
+      <para>Support for <link linkend="spotlight">Spotlight</link><indexterm>
+          <primary>Spotlight</primary>
+        </indexterm> has been added in Netatalk 3.1. See this <link
+      linkend="spotlight-compile">section</link> for information on how to
+      compile Netatalk with Spotlight support.</para>
+
+      <para>Mac OS X 10.5 (Leopard) added support for Time Machine backups
+      over AFP. Two new functions ensure that backups are written to spinning
+      disk, not just in the server's cache. Different host operating systems
+      honour this cache flushing differently. To make a volume a Time Machine
+      target use the volume option "<option>time machine =
+      yes</option>".</para>
+
+      <para>Starting with Netatalk 2.1 UNIX symlinks<indexterm>
+          <primary>symlink</primary>
+
+          <secondary>UNIX symlink</secondary>
+        </indexterm> can be used on the server. Semantics are the same as for
+      eg NFS, ie they are not resolved on the server side but instead it's
+      completely up to the client to resolve them, resulting in links that
+      point somewhere inside the clients filesystem view.</para>
+
+      <sect3>
+        <title>afp.conf</title>
+
+        <para><filename>afp.conf</filename> is the configuration file used by
+        afpd to determine the behaviour and configuration of the AFP file
+        serverand the AFP volume that it provides.</para>
+
+        <para>The <filename>afp.conf</filename> is divided into several
+        sections:<variablelist>
+            <varlistentry>
+              <term>[Global]</term>
+
+              <listitem>
+                <para>The global section defines general server options</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term>[Homes]</term>
+
+              <listitem>
+                <para>The homes section defines user home volumes</para>
+              </listitem>
+            </varlistentry>
+          </variablelist>Any section not called <option>Global</option> or
+        <option>Homes</option> is interpreted as an AFP volume.</para>
+
+        <para>For sharing user homes by defining a <option>Homes</option>
+        section you must specify the option <option>basedir regex</option>
+        which can be a simple string with the path to the parent directory of
+        all user homes or a regular expression.</para>
+
+        <para>Example:</para>
+
+        <para><programlisting>[Homes]
+basedir regex = /home
+</programlisting></para>
+
+        <para>Now any user logging into the AFP server will have a user volume
+        available whos path is <filename>/home/NAME</filename>.</para>
+
+        <para>A more complex setup would be a server with a large amount of
+        user homes which are split across eg two different
+        filesystems:<itemizedlist>
+            <listitem>
+              <para>/RAID1/homes</para>
+            </listitem>
+
+            <listitem>
+              <para>/RAID2/morehomes</para>
+            </listitem>
+          </itemizedlist>The following configuration is
+        required:<programlisting>[Homes]
+basedir regex = /RAID./.*homes
+</programlisting></para>
+
+        <para>If <option>basedir regex</option> contains symlink, set the
+        canonicalized absolute path. When <filename>/home</filename> links to
+        <filename>/usr/home</filename>: <programlisting>[Homes]
+basedir regex = /usr/home</programlisting></para>
+
+        <para>For a more detailed explanation of the available options, please
+        refer to the <citerefentry>
+            <refentrytitle>afp.conf</refentrytitle>
+
+            <manvolnum>5</manvolnum>
+          </citerefentry> man page.</para>
+      </sect3>
+    </sect2>
+
+    <sect2 id="CNID-backends">
+      <title>CNID<indexterm>
+          <primary>CNID</primary>
+
+          <secondary>Catalog Node ID</secondary>
+        </indexterm> backends<indexterm>
+          <primary>Backend</primary>
+
+          <secondary>CNID backend</secondary>
+        </indexterm></title>
+
+      <para>Unlike other protocols like SMB or NFS, the AFP protocol mostly
+      refers to files and directories by ID and not by a path (the IDs are
+      also called CNID, that means Catalog Node ID). A typical AFP request
+      uses a directory ID<indexterm>
+          <primary>DID</primary>
+
+          <secondary>Directory ID</secondary>
+        </indexterm> and a filename, something like <phrase>"server, please
+      open the file named 'Test' in the directory with id 167"</phrase>. For
+      example "Aliases" on the Mac basically work by ID (with a fallback to
+      the absolute path in more recent AFP clients. But this applies only to
+      Finder, not to applications).</para>
+
+      <para>Every file in an AFP volume has to have a unique file ID<indexterm>
+          <primary>FID</primary>
+
+          <secondary>File ID</secondary>
+        </indexterm>, IDs must, according to the specs, never be reused, and
+      IDs are 32 bit numbers (Directory IDs use the same ID pool). So, after
+      ~4 billion files/folders have been written to an AFP volume, the ID pool
+      is depleted and no new file can be written to the volume. No whining
+      please :-)</para>
+
+      <para>Netatalk needs to map IDs to files and folders in the host
+      filesystem. To achieve this, several different CNID backends<indexterm>
+          <primary>CNID backend</primary>
+        </indexterm> are available and can be choosed by the <option>cnid
+      scheme</option><indexterm>
+          <primary>cnidscheme</primary>
+
+          <secondary>specifying a CNID backend</secondary>
+        </indexterm> option in the <citerefentry>
+          <refentrytitle>afp.conf</refentrytitle>
+
+          <manvolnum>5</manvolnum>
+        </citerefentry> configuration file. A CNID backend is basically a
+      database storing ID &lt;-&gt; name mappings.</para>
+
+      <para>The CNID Databases are by default located in
+      <filename>/var/netatalk/CNID</filename>.</para>
+
+      <para>There is a command line utility called <command>dbd</command>
+      available which can be used to verify, repair and rebuild the CNID
+      database.</para>
+
+      <note>
+        <para>There are some CNID related things you should keep in mind when
+        working with netatalk:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Don't nest volumes<indexterm>
+                <primary>Nested volumes</primary>
+              </indexterm>.</para>
+          </listitem>
+
+          <listitem>
+            <para>CNID backends are databases, so they turn afpd into a file
+            server/database mix.</para>
+          </listitem>
+
+          <listitem>
+            <para>If there's no more space on the filesystem left, the
+            database will get corrupted. You can work around this by either
+            using the <option>vol dbpath</option> option and put the database
+            files into another location or, if you use quotas, make sure the
+            CNID database folder is owned by a user/group without a
+            quota<indexterm>
+                <primary>Quotas</primary>
+
+                <secondary>Disk usage quotas</secondary>
+              </indexterm>.</para>
+          </listitem>
+
+          <listitem>
+            <para>Be careful with CNID databases for volumes that are mounted
+            via NFS. That is a pretty audacious decision to make anyway, but
+            putting a database there as well is really asking for trouble,
+            i.e. database corruption. Use the <option>vol dbpath</option>
+            directive to put the databases onto a local disk if you must use
+            NFS<indexterm>
+                <primary>NFS</primary>
+
+                <secondary>Network File System</secondary>
+              </indexterm> mounted volumes.</para>
+          </listitem>
+        </itemizedlist>
+      </note>
+
+      <sect3>
+        <title>cdb<indexterm>
+            <primary>CDB</primary>
+
+            <secondary>"cdb" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>The "concurrent database" backend is based on Berkeley DB. With
+        this backend, several afpd daemons access the CNID database directly.
+        Berkeley DB locking is used to synchronize access, if more than one
+        afpd process is active for a volume. The drawback is, that the crash
+        of a single afpd process might corrupt the database. cdb should only
+        be used when sharing home directories for a larger number of users
+        <emphasis>and</emphasis> it has been determined that a large number of
+        <command>cnid_dbd</command> processes is problematic.</para>
+      </sect3>
+
+      <sect3>
+        <title>dbd<indexterm>
+            <primary>DBD</primary>
+
+            <secondary>"dbd" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>Access to the CNID database is restricted to the cnid_dbd daemon
+        process. afpd processes communicate with the daemon for database reads
+        and updates. The probability for database corruption is practically
+        zero.</para>
+
+        <para>This is the default backend since Netatalk 2.1.</para>
+      </sect3>
+
+      <sect3>
+        <title>tdb<indexterm>
+            <primary>tdb</primary>
+
+            <secondary>"tdb" CNID backend</secondary>
+          </indexterm></title>
+
+        <para><abbrev>tdb</abbrev> is another persistent CNID database, it's
+        Samba's <emphasis>Trivial Database</emphasis>. It could be used
+        instead of <abbrev>cdb</abbrev> for user volumes.<important>
+            <para>Only ever use it for volumes that are
+            <emphasis>not</emphasis> shared and accessed by multiple clients
+            at once !</para>
+          </important>This backend is also used internally (as in-memory CNID
+        database) as a fallback in case opening the primary database can't be
+        opened, because <abbrev>tdb</abbrev> can work as in-memory database.
+        This of course means upon restart the CNIDs are gone.</para>
+      </sect3>
+
+      <sect3>
+        <title>last<indexterm>
+            <primary>Last</primary>
+
+            <secondary>"last" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>The last backend is a in-memory tdb database. It is not
+        persistent. Starting with netatalk 3.0, it becomes the <emphasis> read
+        only mode</emphasis> automatically. This is useful e.g. for
+        CD-ROMs.</para>
+      </sect3>
+
+      <sect3>
+        <title>mysql<indexterm>
+            <primary>MySQL</primary>
+
+            <secondary>"mysql" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>CNID backend using a MySQL server.</para>
+      </sect3>
+    </sect2>
+
+    <sect2 id="charsets">
+      <title>Charsets<indexterm>
+          <primary>Charset</primary>
+
+          <secondary>character set</secondary>
+        </indexterm>/Unicode<indexterm>
+          <primary>Unicode</primary>
+        </indexterm></title>
+
+      <para></para>
+
+      <sect3>
+        <title>Why Unicode?</title>
+
+        <para>Internally, computers don't know anything about characters and
+        texts, they only know numbers. Therefore, each letter is assigned a
+        number. A character set, often referred to as
+        <emphasis>charset</emphasis> or
+        <emphasis>codepage</emphasis><indexterm>
+            <primary>Codepage</primary>
+          </indexterm>, defines the mappings between numbers and
+        letters.</para>
+
+        <para>If two or more computer systems need to communicate with each
+        other, the have to use the same character set. In the 1960s the
+        ASCII<indexterm>
+            <primary>ASCII</primary>
+
+            <secondary>American Standard Code for Information
+            Interchange</secondary>
+          </indexterm> (American Standard Code for Information Interchange)
+        character set was defined by the American Standards Association. The
+        original form of ASCII represented 128 characters, more than enough to
+        cover the English alphabet and numerals. Up to date, ASCII has been
+        the normative character scheme used by computers.</para>
+
+        <para>Later versions defined 256 characters to produce a more
+        international fluency and to include some slightly esoteric graphical
+        characters. Using this mode of encoding each character takes exactly
+        one byte. Obviously, 256 characters still wasn't enough to map all the
+        characters used in the various languages into one character
+        set.</para>
+
+        <para>As a result localized character sets were defined later, e.g the
+        ISO-8859 character sets. Most operating system vendors introduced
+        their own characters sets to satisfy their needs, e.g. IBM defined the
+        <emphasis>codepage 437 (DOSLatinUS)</emphasis>, Apple introduced the
+        <emphasis>MacRoman</emphasis><indexterm>
+            <primary>MacRoman</primary>
+
+            <secondary>MacRoman charset</secondary>
+          </indexterm> codepage and so on. The characters that were assigned
+        number larger than 127 were referred to as
+        <emphasis>extended</emphasis> characters. These character sets
+        conflict with another, as they use the same number for different
+        characters, or vice versa.</para>
+
+        <para>Almost all of those characters sets defined 256 characters,
+        where the first 128 (0-127) character mappings are identical to ASCII.
+        As a result, communication between systems using different codepages
+        was effectively limited to the ASCII charset.</para>
+
+        <para>To solve this problem new, larger character sets were defined.
+        To make room for more character mappings, these character sets use at
+        least 2 bytes to store a character. They are therefore referred to as
+        <emphasis>multibyte</emphasis> character sets.</para>
+
+        <para>One standardized multibyte charset encoding scheme is known as
+        <ulink url="http://www.unicode.org/">unicode</ulink>. A big advantage
+        of using a multibyte charset is that you only need one. There is no
+        need to make sure two computers use the same charset when they are
+        communicating.</para>
+      </sect3>
+
+      <sect3>
+        <title>character sets used by Apple</title>
+
+        <para>In the past, Apple clients used single-byte charsets to
+        communicate over the network. Over the years Apple defined a number of
+        codepages, western users will most likely be using the
+        <emphasis>MacRoman</emphasis> codepage.</para>
+
+        <para>Codepages defined by Apple include:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>MacArabic, MacFarsi</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCentralEurope</para>
+          </listitem>
+
+          <listitem>
+            <para>MacChineseSimple</para>
+          </listitem>
+
+          <listitem>
+            <para>MacChineseTraditional</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCroation</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCyrillic</para>
+          </listitem>
+
+          <listitem>
+            <para>MacDevanagari</para>
+          </listitem>
+
+          <listitem>
+            <para>MacGreek</para>
+          </listitem>
+
+          <listitem>
+            <para>MacHebrew</para>
+          </listitem>
+
+          <listitem>
+            <para>MacIcelandic</para>
+          </listitem>
+
+          <listitem>
+            <para>MacJapanese</para>
+          </listitem>
+
+          <listitem>
+            <para>MacKorean</para>
+          </listitem>
+
+          <listitem>
+            <para>MacRoman</para>
+          </listitem>
+
+          <listitem>
+            <para>MacRomanian</para>
+          </listitem>
+
+          <listitem>
+            <para>MacThai</para>
+          </listitem>
+
+          <listitem>
+            <para>MacTurkish</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>Starting with Mac OS X and AFP3, <ulink
+        url="http://www.utf-8.com/">UTF-8</ulink> is used. UTF-8 encodes
+        Unicode characters in an ASCII compatible way, each Unicode character
+        is encoded into 1-6 ASCII characters. UTF-8 is therefore not really a
+        charset itself, it's an encoding of the Unicode charset.</para>
+
+        <para>To complicate things, Unicode defines several <emphasis> <ulink
+        url="http://www.unicode.org/reports/tr15/index.html">normalization</ulink>
+        </emphasis> forms. While <ulink
+        url="http://www.samba.org">samba</ulink><indexterm>
+            <primary>Samba</primary>
+          </indexterm> uses <emphasis>precomposed</emphasis><indexterm>
+            <primary>Precomposed</primary>
+
+            <secondary>Precomposed Unicode normalization</secondary>
+          </indexterm> Unicode, which most Unix tools prefer as well, Apple
+        decided to use the <emphasis>decomposed</emphasis><indexterm>
+            <primary>Decomposed</primary>
+
+            <secondary>Decomposed Unicode normalization</secondary>
+          </indexterm> normalization.</para>
+
+        <para>For example lets take the German character
+        '<keycode>ä</keycode>'. Using the precomposed normalization, Unicode
+        maps this character to 0xE4. In decomposed normalization, 'ä' is
+        actually mapped to two characters, 0x61 and 0x308. 0x61 is the mapping
+        for an 'a', 0x308 is the mapping for a <emphasis>COMBINING
+        DIAERESIS</emphasis>.</para>
+
+        <para>Netatalk refers to precomposed UTF-8 as
+        <emphasis>UTF8</emphasis><indexterm>
+            <primary>UTF8</primary>
+
+            <secondary>Netatalk's precomposed UTF-8 encoding</secondary>
+          </indexterm> and to decomposed UTF-8 as
+        <emphasis>UTF8-MAC</emphasis><indexterm>
+            <primary>UTF8-MAC</primary>
+
+            <secondary>Netatalk's decomposed UTF-8 encoding</secondary>
+          </indexterm>.</para>
+      </sect3>
+
+      <sect3>
+        <title>afpd and character sets</title>
+
+        <para>To support new AFP 3.x and older AFP 2.x clients at the same
+        time, afpd needs to be able to convert between the various charsets
+        used. AFP 3.x clients always use UTF8-MAC, AFP 2.x clients use one of
+        the Apple codepages.</para>
+
+        <para>At the time of this writing, netatalk supports the following
+        Apple codepages:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>MAC_CENTRALEUROPE</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CHINESE_SIMP</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CHINESE_TRAD</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CYRILLIC</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_GREEK</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_HEBREW</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_JAPANESE</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_KOREAN</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_ROMAN</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_TURKISH</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>afpd handles three different character set options:</para>
+
+        <variablelist>
+          <varlistentry>
+            <term>unix charset<indexterm>
+                <primary>unix charset</primary>
+
+                <secondary>afpd's unix charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>This is the codepage used internally by your operating
+              system. If not specified, it defaults to <option>UTF8</option>.
+              If <option>LOCALE</option> is specified and your system support
+              Unix locales, afpd tries to detect the codepage. afpd uses this
+              codepage to read its configuration files, so you can use
+              extended characters for volume names, login messages, etc. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>mac charset<indexterm>
+                <primary>mac charset</primary>
+
+                <secondary>afpd's mac charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>As already mentioned, older Mac OS clients (up to AFP 2.2)
+              use codepages to communicate with afpd. However, there is no
+              support for negotiating the codepage used by the client in the
+              AFP protocol. If not specified otherwise, afpd assumes the
+              <emphasis>MacRoman</emphasis> codepage is used. In case you're
+              clients use another codepage, e.g.
+              <emphasis>MacCyrillic</emphasis>, you'll <emphasis
+              role="bold">have</emphasis> to explicitly configure this. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>vol charset<indexterm>
+                <primary>vol charset</primary>
+
+                <secondary>afpd's vol charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>This defines the charset afpd should use for filenames on
+              disk. By default, it is the same as <option>unix
+              charset</option>. If you have <ulink
+              url="http://www.gnu.org/software/libiconv/">iconv</ulink><indexterm>
+                  <primary>Iconv</primary>
+
+                  <secondary>iconv encoding conversion engine</secondary>
+                </indexterm> installed, you can use any iconv provided charset
+              as well.</para>
+
+              <para>afpd needs a way to preserve extended macintosh
+              characters, or characters illegal in unix filenames, when saving
+              files on a unix filesystem. Earlier versions used the the so
+              called CAP encoding<indexterm>
+                  <primary>CAP encoding</primary>
+
+                  <secondary>CAP style character encoding</secondary>
+                </indexterm>. An extended character (&gt;0x7F) would be
+              converted to a :xx hex sequence, e.g. the Apple Logo (MacRoman:
+              0xF0) was saved as :f0. Some special characters will be
+              converted as to :xx notation as well. '/' will be encoded to
+              :2f, if <option>usedots</option> was not specified, a leading
+              dot '.' will be encoded as :2e.</para>
+
+              <para>Even though this version now uses <option>UTF8</option> as
+              the default encoding for filenames, '/' will be converted to
+              ':'. For western users another useful setting could be
+              <option>vol charset = ISO-8859-15</option>.</para>
+
+              <para>If a character cannot be converted from the <option>mac
+              charset</option> to the selected <option>vol charset</option>,
+              afpd will save it as a CAP encoded character. For AFP3 clients,
+              afpd will convert the UTF8 character to <option>mac
+              charset</option> first. If this conversion fails, you'll receive
+              a -50 error on the mac. <emphasis>Note</emphasis>: Whenever you
+              can, please stick with the default UTF8 volume format. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </sect3>
+    </sect2>
+
+    <sect2 id="authentication">
+      <title>Authentication<indexterm>
+          <primary>Authentication</primary>
+
+          <secondary>between AFP client and server</secondary>
+        </indexterm></title>
+
+      <sect3>
+        <title>AFP authentication basics</title>
+
+        <para>Apple chose a flexible model called "User Authentication
+        Modules"<indexterm>
+            <primary>UAM</primary>
+
+            <secondary>User Authentication Module</secondary>
+          </indexterm> (UAMs) for authentication purposes between AFP client
+        and server. An AFP client initially connecting to an AFP server will
+        ask for the list of UAMs which the server provides, and will choose
+        the one with strongest encryption that the client supports.</para>
+
+        <para>Several UAMs have been developed by Apple over the time, some by
+        3rd-party developers.</para>
+      </sect3>
+
+      <sect3>
+        <title>UAMs supported by Netatalk</title>
+
+        <para>Netatalk supports the following ones by default:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>"No User Authent"<indexterm>
+                <primary>No User Authent</primary>
+
+                <secondary>"No User Authent" UAM (guest access)</secondary>
+              </indexterm> UAM (guest access without authentication)</para>
+          </listitem>
+
+          <listitem>
+            <para>"Cleartxt Passwrd"<indexterm>
+                <primary>Cleartxt Passwrd</primary>
+
+                <secondary>"Cleartxt Passwrd" UAM</secondary>
+              </indexterm> UAM (no password encryption)</para>
+          </listitem>
+
+          <listitem>
+            <para>"Randnum exchange"<indexterm>
+                <primary>Randnum exchange</primary>
+
+                <secondary>"Randnum exchange" UAM</secondary>
+              </indexterm>/"2-Way Randnum exchange"<indexterm>
+                <primary>2-Way Randnum exchange</primary>
+
+                <secondary>"2-Way Randnum exchange" UAM</secondary>
+              </indexterm> UAMs (weak password encryption, separate password
+            storage)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHCAST128"<indexterm>
+                <primary>DHCAST128</primary>
+
+                <secondary>"DHCAST128" UAM</secondary>
+              </indexterm> UAM (stronger password encryption)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHX2"<indexterm>
+                <primary>DHX2</primary>
+
+                <secondary>"DHX2" UAM</secondary>
+              </indexterm> UAM (successor of DHCAST128)</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>There exist other optional UAMs as well:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>"PGPuam 1.0"<indexterm>
+                <primary>PGPuam 1.0</primary>
+
+                <secondary>"PGPuam 1.0" UAM</secondary>
+              </indexterm><indexterm>
+                <primary>uams_pgp.so</primary>
+
+                <secondary>"PGPuam 1.0" UAM</secondary>
+              </indexterm> UAM (PGP-based authentication for pre-Mac OS X
+            clients. You'll also need the <ulink
+            url="http://www.vmeng.com/vinnie/papers/pgpuam.html">PGPuam
+            client</ulink> to let this work)</para>
+
+            <para>You'll have to add <filename>"--enable-pgp-uam"</filename>
+            to your configure switches to have this UAM available.</para>
+          </listitem>
+
+          <listitem>
+            <para>"Kerberos IV"<indexterm>
+                <primary>Kerberos IV</primary>
+
+                <secondary>"Kerberos IV" UAM</secondary>
+              </indexterm><indexterm>
+                <primary>uams_krb4.so</primary>
+
+                <secondary>"Kerberos IV" UAM</secondary>
+              </indexterm>/"AFS Kerberos"<indexterm>
+                <primary>AFS Kerberos</primary>
+
+                <secondary>"AFS Kerberos" UAM (Kerberos IV)</secondary>
+              </indexterm> UAMs (suitable to use <ulink
+            url="http://web.mit.edu/macdev/KfM/Common/Documentation/faq.html">Kerberos
+            v4 based authentication</ulink> and AFS file servers)</para>
+
+            <para>Use <filename>"--enable-krb4-uam"</filename> at compile time
+            to activate the build of this UAM.</para>
+          </listitem>
+
+          <listitem>
+            <para>"Client Krb v2"<indexterm>
+                <primary>Client Krb v2</primary>
+
+                <secondary>"Client Krb v2" UAM (Kerberos V)</secondary>
+              </indexterm> UAM (Kerberos V, suitable for "Single Sign On"
+            Scenarios with OS X clients -- see below)</para>
+
+            <para><filename>"--enable-krbV-uam"</filename> will provide you
+            with the ability to use this UAM.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>You can configure which UAMs should be activated by defining
+        "<option>uam list</option>" in <option>Global</option> section.
+        <command>afpd</command> will log which UAMs it's using and if problems
+        occur while activating them in either
+        <filename>netatalk.log</filename> or syslog at startup time.
+        <citerefentry>
+            <refentrytitle>asip-status.pl</refentrytitle>
+
+            <manvolnum>1</manvolnum>
+          </citerefentry> can be used to query the available UAMs of AFP
+        servers as well.</para>
+
+        <para>Having a specific UAM available at the server does not
+        automatically mean that a client can use it. Client-side support is
+        also necessary. For older Macintoshes running Mac OS &lt; X DHCAST128
+        support exists since AppleShare client 3.8.x.</para>
+
+        <para>On OS X, there exist some client-side techniques to make the
+        AFP-client more verbose, so one can have a look what's happening while
+        negotiating the UAMs to use. Compare with this <ulink
+        url="http://article.gmane.org/gmane.network.netatalk.devel/7383/">hint</ulink>.</para>
+      </sect3>
+
+      <sect3>
+        <title>Which UAMs to activate?</title>
+
+        <para>It depends primarily on your needs and on the kind of Mac OS
+        versions you have to support. Basically one should try to use
+        DHCAST128 and DHX2 where possible because of its strength of password
+        encryption.</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Unless you really have to supply guest access to your
+            server's volumes ensure that you disable "No User Authent" since
+            it might lead accidentally to unauthorized access. In case you
+            must enable guest access take care that you enforce this on a per
+            volume base using the access controls.</para>
+          </listitem>
+
+          <listitem>
+            <para>The "ClearTxt Passwrd" UAM is as bad as it sounds since
+            passwords go unencrypted over the wire. Try to avoid it at both
+            the server's side as well as on the client's. Note: If you want to
+            provide Mac OS 8/9 clients with NetBoot-services then you need
+            uams_cleartext.so since the AFP-client integrated into the Mac's
+            firmware can only deal with this basic form of
+            authentication.</para>
+          </listitem>
+
+          <listitem>
+            <para>Since "Randnum exchange"/"2-Way Randnum exchange" uses only
+            56 bit DES for encryption it should be avoided as well. Another
+            disadvantage is the fact that the passwords have to be stored in
+            cleartext on the server and that it doesn't integrate into both
+            PAM scenarios or classic /etc/shadow (you have to administrate
+            passwords separately by using the <citerefentry>
+                <refentrytitle>afppasswd</refentrytitle>
+
+                <manvolnum>1</manvolnum>
+              </citerefentry> utility, if clients should use these
+            UAMs)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHCAST128" or "DHX2" should be a good compromise for most
+            people since it combines stronger encryption with PAM
+            integration.</para>
+          </listitem>
+
+          <listitem>
+            <para>Using the Kerberos V<indexterm>
+                <primary>Kerberos V</primary>
+
+                <secondary>"Client Krb v2" UAM</secondary>
+              </indexterm> ("Client Krb v2") UAM, it's possible to implement
+            real single sign on scenarios using Kerberos tickets. The password
+            is not sent over the network. Instead, the user password is used
+            to decrypt a service ticket for the appleshare server. The service
+            ticket contains an encryption key for the client and some
+            encrypted data (which only the appleshare server can decrypt). The
+            encrypted portion of the service ticket is sent to the server and
+            used to authenticate the user. Because of the way that the afpd
+            service principal detection is implemented, this authentication
+            method is vulnerable to man-in-the-middle attacks.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>For a more detailed overview over the technical implications of
+        the different UAMs, please have a look at Apple's <ulink
+        url="http://developer.apple.com/library/mac/#documentation/Networking/Conceptual/AFP/AFPSecurity/AFPSecurity.html#//apple_ref/doc/uid/TP40000854-CH232-SW1">File
+        Server Security</ulink> pages.</para>
+      </sect3>
+
+      <sect3>
+        <title>Using different authentication sources with specific
+        UAMs</title>
+
+        <para>Some UAMs provide the ability to use different authentication
+        "backends", namely <filename>uams_cleartext.so</filename>,
+        <filename>uams_dhx.so</filename> and
+        <filename>uams_dhx2.so</filename>. They can use either classic Unix
+        passwords from <filename>/etc/passwd</filename>
+        (<filename>/etc/shadow</filename>) or PAM if the system supports that.
+        <filename>uams_cleartext.so</filename> can be symlinked to either
+        <filename>uams_passwd.so</filename> or
+        <filename>uams_pam.so</filename>, <filename>uams_dhx.so</filename> to
+        <filename>uams_dhx_passwd.so</filename> or
+        <filename>uams_dhx_pam.so</filename> and
+        <filename>uams_dhx2.so</filename> to
+        <filename>uams_dhx2_passwd.so</filename> or
+        <filename>uams_dhx2_pam.so</filename>.</para>
+
+        <para>So, if it looks like this in Netatalk's UAMs folder (per default
+        <filename>/etc/netatalk/uams/</filename>):<programlisting>uams_clrtxt.so -&gt; uams_pam.so
+uams_dhx.so -&gt; uams_dhx_pam.so
+uams_dhx2.so -&gt; uams_dhx2_pam.so</programlisting> then you're using PAM,
+        otherwise classic Unix passwords. The main advantage of using PAM is
+        that one can integrate Netatalk in centralized authentication
+        scenarios, eg. via LDAP, NIS and the like. Please always keep in mind
+        that the protection of your user's login credentials in such scenarios
+        also depends on the strength of encryption that the UAM in question
+        supplies. So think about eliminating weak UAMs like "ClearTxt Passwrd"
+        and "Randnum exchange" completely from your network.</para>
+      </sect3>
+
+      <sect3>
+        <title>Netatalk UAM overview table</title>
+
+        <para>A small overview of the most common used UAMs.</para>
+
+        <table orient="land">
+          <title>Netatalk UAM overview</title>
+
+          <tgroup align="center" cols="7">
+            <colspec colname="col1" colnum="1" colwidth="0.5*" />
+
+            <colspec colname="uam_guest" colnum="2" colwidth="1*" />
+
+            <colspec colname="uam_clrtxt" colnum="3" colwidth="1*" />
+
+            <colspec colname="uam_randnum" colnum="4" colwidth="1*" />
+
+            <colspec colname="uam_dhx" colnum="5" colwidth="1*" />
+
+            <colspec colname="uam_dhx2" colnum="6" colwidth="1*" />
+
+            <colspec colname="uam_gss" colnum="7" colwidth="1*" />
+
+            <tbody>
+              <row>
+                <entry align="center" rotate="0" valign="middle">UAM</entry>
+
+                <entry>No User Authent<indexterm>
+                    <primary>uams_guest.so</primary>
+
+                    <secondary>"No User Authent" UAM (guest
+                    access)</secondary>
+                  </indexterm></entry>
+
+                <entry>Cleartxt Passwrd<indexterm>
+                    <primary>uams_cleartxt.so</primary>
+
+                    <secondary>"Cleartxt Passwrd" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>(2-Way) Randnum exchange<indexterm>
+                    <primary>uams_randnum.so</primary>
+
+                    <secondary>"(2-Way) Randnum exchange" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>DHCAST128<indexterm>
+                    <primary>uams_dhx.so</primary>
+
+                    <secondary>"DHCAST128" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>DHX2<indexterm>
+                    <primary>uams_dhx2.so</primary>
+
+                    <secondary>"DHX2" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>Client Krb v2<indexterm>
+                    <primary>uams_gss.so</primary>
+
+                    <secondary>"Client Krb v2" UAM (Kerberos V)</secondary>
+                  </indexterm></entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">pssword
+                length</entry>
+
+                <entry>guest access</entry>
+
+                <entry>max. 8 characters</entry>
+
+                <entry>max. 8 characters</entry>
+
+                <entry>max. 64 characters</entry>
+
+                <entry>max. 256 characters</entry>
+
+                <entry>Kerberos tickets</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Client
+                support</entry>
+
+                <entry>built-in into all Mac OS versions</entry>
+
+                <entry>built-in in all Mac OS versions except 10.0. Has to be
+                activated explicitly in recent Mac OS X versions</entry>
+
+                <entry>built-in into almost all Mac OS versions</entry>
+
+                <entry>built-in since AppleShare client 3.8.4, available as a
+                plug-in for 3.8.3, integrated in Mac OS X' AFP client</entry>
+
+                <entry>built-in since Mac OS X 10.2</entry>
+
+                <entry>built-in since Mac OS X 10.2</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0"
+                valign="middle">Encryption</entry>
+
+                <entry>Enables guest access without authentication between
+                client and server.</entry>
+
+                <entry>Password will be sent in cleartext over the wire. Just
+                as bad as it sounds, therefore avoid at all if possible (note:
+                providing NetBoot services requires the ClearTxt UAM)</entry>
+
+                <entry>8-byte random numbers are sent over the wire,
+                comparable with DES, 56 bits. Vulnerable to offline dictionary
+                attack. Requires passwords in clear on the server.</entry>
+
+                <entry>Password will be encrypted with 128 bit SSL, user will
+                be authenticated against the server but not vice versa.
+                Therefor weak against man-in-the-middle attacks.</entry>
+
+                <entry>Password will be encrypted using libgcrypt with CAST
+                128 in CBC mode. User will be authenticated against the server
+                but not vice versa. Therefor weak against man-in-the-middle
+                attacks.</entry>
+
+                <entry>Password is not sent over the network. Due to the
+                service principal detection method, this authentication method
+                is vulnerable to man-in-the-middle attacks.</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Server
+                support</entry>
+
+                <entry align="center" valign="middle">uams_guest.so</entry>
+
+                <entry align="center" valign="middle">uams_cleartxt.so</entry>
+
+                <entry align="center" valign="middle">uams_randnum.so</entry>
+
+                <entry align="center" valign="middle">uams_dhx.so</entry>
+
+                <entry align="center" valign="middle">uams_dhx2.so</entry>
+
+                <entry align="center" valign="middle">uams_gss.so</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Password
+                storage method</entry>
+
+                <entry align="center" valign="middle">None</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">Passwords stored in
+                clear text in a separate text file</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">At the Kerberos Key
+                Distribution Center*</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
+        <para>* Have a look at this <ulink
+        url="http://cryptnet.net/fdp/admin/kerby-infra/en/kerby-infra.html">Kerberos
+        overview</ulink></para>
+      </sect3>
+
+      <sect3 id="sshtunnel">
+        <title>SSH tunneling</title>
+
+        <para>Tunneling and all sort of VPN stuff has nothing to do with AFP
+        authentication and UAMs in general. But since Apple introduced an
+        option called "Allow Secure Connections Using SSH" and many people
+        tend to confuse both things, we'll speak about that here too.</para>
+
+        <sect4 id="manualsshtunnel">
+          <title>Manually tunneling an AFP session</title>
+
+          <para>This works since the first AFP servers that spoke "AFP over
+          TCP" appeared in networks. One simply tunnels the remote server's
+          AFP port to a local port different than 548 and connects locally to
+          this port afterwards. On OS X this can be done by</para>
+
+          <programlisting>ssh -l $USER $SERVER -L 10548:127.0.0.1:548 sleep 3000</programlisting>
+
+          <para>After establishing the tunnel one will use
+          <filename>"afp://127.0.0.1:10548"</filename> in the "Connect to
+          server" dialog. All AFP traffic including the initial connection
+          attempts will be sent encrypted over the wire since the local AFP
+          client will connect to the Mac's local port 10548 which will be
+          forwarded to the remote server's AFP port (we used the default 548)
+          over SSH.</para>
+
+          <para>These sorts of tunnels are an ideal solution if you've to
+          access an AFP server providing weak authentications mechanisms
+          through the Internet without having the ability to use a "real" VPN.
+          Note that you can let <command>ssh</command> compress the data by
+          using its "-C" switch and that the tunnel endpoints can be different
+          from both AFP client and server (compare with the SSH documentation
+          for details).</para>
+        </sect4>
+
+        <sect4 id="autosshtunnel">
+          <title>Automatically establishing a tunneled AFP connection</title>
+
+          <para>From Mac OS X 10.2 to 10.4, Apple added an "Allow Secure
+          Connections Using SSH" checkbox to the "Connect to Server" dialog.
+          The idea behind: When the server signals that it can be contacted by
+          SSH then Mac OS X' AFP client tries to establish the tunnel and
+          automagically sends all AFP traffic through it.</para>
+
+          <para>But it took until the release of Mac OS X 10.3 that this
+          feature worked the first time... partly. In case, the SSH tunnel
+          can't be established the AFP client <emphasis
+          role="strong">silently</emphasis> fell back to an unencrypted AFP
+          connection attempt.</para>
+
+          <para>Netatalk's afpd will report that it is capable of handling SSH
+          tunneled AFP requests, when both "<option>advertise ssh</option>"
+          and "<option>fqdn</option>" options are set in
+          <option>Global</option> section (double check with <citerefentry>
+              <refentrytitle>asip-status.pl</refentrytitle>
+
+              <manvolnum>1</manvolnum>
+            </citerefentry> after you restarted afpd when you made changes to
+          the settings). But there are a couple of reasons why you don't want
+          to use this option at all:</para>
+
+          <itemizedlist>
+            <listitem>
+              <para>Tunneling TCP over TCP (as SSH does) is not the best idea.
+              There exist better solutions like VPNs based on the IP
+              layer.</para>
+            </listitem>
+
+            <listitem>
+              <para>Since this SSH kludge isn't a normal UAM that integrates
+              directly into the AFP authentication mechanisms but instead uses
+              a single flag signalling clients whether they can <emphasis
+              role="strong">try</emphasis> to establish a tunnel or not, it
+              makes life harder to see what's happening when things go
+              wrong.</para>
+            </listitem>
+
+            <listitem>
+              <para>You cannot control which machines are logged on by
+              Netatalk tools like a <command>macusers</command> since all
+              connection attempts seem to be made from localhost.</para>
+            </listitem>
+
+            <listitem>
+              <para>On the other side you've to limit access to afpd to
+              localhost only (TCP wrappers) when you want to ensure that all
+              AFP sessions are SSH encrypted or...</para>
+            </listitem>
+
+            <listitem>
+              <para>...when you're using 10.2 - 10.3.3 then you get the
+              opposite of what you'd expect: potentially unencrypted AFP
+              communication (including logon credentials) on the network
+              without a single notification that establishing the tunnel
+              failed. Apple fixed that not until Mac OS X 10.3.4.</para>
+            </listitem>
+
+            <listitem>
+              <para>Encrypting all AFP sessions via SSH can lead to a
+              significantly higher load on the Netatalk server</para>
+            </listitem>
+          </itemizedlist>
+        </sect4>
+      </sect3>
+    </sect2>
+
+    <sect2 id="acls">
+      <title>ACL Support<indexterm>
+          <primary>ACLs</primary>
+        </indexterm></title>
+
+      <para>ACL support for AFP is implemented for ZFS ACLs on Solaris and
+      derived platforms and for POSIX 1e ACLs on Linux.</para>
+
+      <sect3>
+        <title>Configuration</title>
+
+        <para>For a basic mode of operation there's nothing to configure.
+        Netatalk reads ACLs on the fly and calculates effective permissions
+        which are then send to the AFP client via the so called
+        UARights<indexterm>
+            <primary>UARights</primary>
+          </indexterm> permission bits. On a Mac, the Finder uses these bits
+        to adjust permission in Finder windows. For example folder whos UNIX
+        mode would only result in in read-only permissions for a user will not
+        be displayed with a read-only icon and the user will be able to write
+        to the folder given the folder has an ACL giving the user write
+        access.</para>
+
+        <para>By default, the effective permission of the authenticated user
+        are only mapped to the mentioned UARights<indexterm>
+            <primary>UARights</primary>
+          </indexterm>permission structure, not the UNIX mode. You can adjust
+        this behaviour with the configuration option <link
+        linkend="map_acls">map acls</link>.</para>
+
+        <para>However, neither in Finder "Get Info" windows nor in Terminal
+        will you be able to see the ACLs, that's a result of how ACLs in OS X
+        are designed. If you want to be able to display ACLs on the client,
+        things get more involved as you must then setup both client and server
+        to be part on a authentication domain (directory service, eg LDAP,
+        OpenDirectory). The reason is, that in OS X ACLs are bound to UUIDs,
+        not just uid's or gid's. Therefor afpd must be able to map every
+        filesystem uid and gid to a UUID so that it can return the server side
+        ACLs which are bound to UNIX uid and gid mapped to OS X UUIDs.</para>
+
+        <para>Netatalk can query a directory server using LDAP queries. Either
+        the directory server already provides an UUID attribute for user and
+        groups (Active Directory, Open Directory) or you reuse an unused
+        attribute (or add a new one) to you directory server (eg
+        OpenLDAP).</para>
+
+        <para>In detail:</para>
+
+        <orderedlist>
+          <listitem>
+            <para>For Solaris/ZFS: ZFS Volumes</para>
+
+            <para>You should configure a ZFS ACL know for any volume you want
+            to use with Netatalk:</para>
+
+            <screen>aclinherit = passthrough
+aclmode = passthrough</screen>
+
+            <para>For an explanation of what this knob does and how to apply
+            it, check your hosts ZFS documentation (eg man zfs).</para>
+          </listitem>
+
+          <listitem>
+            <para>Authentication Domain</para>
+
+            <para>Your server and the clients must be part of a security
+            association where identity data is coming from a common source.
+            ACLs in Darwin are based on UUIDs and so is the ACL specification
+            in AFP 3.2. Therefor your source of identity data has to provide
+            an attribute for every user and group where a UUID is stored as a
+            ASCII string. In other words:</para>
+
+            <itemizedlist>
+              <listitem>
+                <para>you need an Open Directory Server or an LDAP server
+                where you store UUIDs in some attribute</para>
+              </listitem>
+
+              <listitem>
+                <para>your clients must be configured to use this
+                server</para>
+              </listitem>
+
+              <listitem>
+                <para>your server should be configured to use this server via
+                nsswitch and PAM</para>
+              </listitem>
+
+              <listitem>
+                <para>configure Netatalk via the special <link
+                linkend="acl_options">LDAP options for ACLs</link> in <link
+                linkend="afp.conf.5">afp.conf</link> so that Netatalk is able
+                to retrieve the UUID for users and groups via LDAP search
+                queries</para>
+              </listitem>
+            </itemizedlist>
+          </listitem>
+        </orderedlist>
+      </sect3>
+
+      <sect3>
+        <title>OS X ACLs</title>
+
+        <para>With Access Control Lists (ACLs) Mac OS X offers a powerful
+        extension of the traditional UNIX permissions model. An ACL is an
+        ordered list of Access Control Entries (ACEs) explicitly granting or
+        denying a set of permissions to a given user or group.</para>
+
+        <para>Unlike UNIX permissions, which are bound to user or group IDs,
+        ACLs are tied to UUIDs. For this reason accessing an object's ACL
+        requires server and client to use a common directory service which
+        translates between UUIDs and user/group IDs.</para>
+
+        <para>ACLs and UNIX permissions interact in a rather simple way. As
+        ACLs are optional UNIX permissions act as a default mechanism for
+        access control. Changing an objects's UNIX permissions will leave it's
+        ACL intact and modifying an ACL will never change the object's UNIX
+        permissions. While doing access checks, OS X first examines an
+        object's ACL evaluating ACEs in order until all requested rights have
+        been granted, a requested right has been explicitly denied by an ACE
+        or the end of the list has been reached. In case there is no ACL or
+        the permissions granted by the ACL are not sufficient to fulfill the
+        request, OS X next evaluates the object's UNIX permissions. Therefore
+        ACLs always have precedence over UNIX permissions.</para>
+      </sect3>
+
+      <sect3>
+        <title>ZFS ACLs</title>
+
+        <para>ZFS ACLs closely match OS X ACLs. Both offer mostly identical
+        fine grained permissions and inheritance settings.</para>
+      </sect3>
+
+      <sect3>
+        <title>POSIX ACLs</title>
+
+        <sect4>
+          <title>Overview</title>
+
+          <para>Compared to OS X or NFSv4 ACLs, Posix ACLs represent a
+          different, less versatile approach to overcome the limitations of
+          the traditional UNIX permissions. Implementations are based on the
+          withdrawn Posix 1003.1e standard.</para>
+
+          <para>The standard defines two types of ACLs. Files and directories
+          can have access ACLs which are consulted for access checks.
+          Directories can also have default ACLs irrelevant to access checks.
+          When a new object is created inside a directory with a default ACL,
+          the default ACL is applied to the new object as it's access ACL.
+          Subdirectories inherit default ACLs from their parent. There are no
+          further mechanisms of inheritance control.</para>
+
+          <para>Architectural differences between Posix ACLs and OS X ACLs
+          especially involve:</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>No fine-granular permissions model. Like UNIX
+                permissions Posix ACLs only differentiate between read, write
+                and execute permissions.</para>
+              </listitem>
+
+              <listitem>
+                <para>Entries within an ACL are unordered.</para>
+              </listitem>
+
+              <listitem>
+                <para>Posix ACLs can only grant rights. There is no way to
+                explicitly deny rights by an entry.</para>
+              </listitem>
+
+              <listitem>
+                <para>UNIX permissions are integrated into an ACL as special
+                entries.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>Posix 1003.1e defines 6 different types of ACL entries. The
+          first three types are used to integrate standard UNIX permissions.
+          They form a minimal ACL, their presence is mandatory and only one
+          entry of each type is allowed within an ACL.</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>ACL_USER_OBJ: the owner's access rights.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_GROUP_OBJ: the owning group's access rights.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_OTHER: everybody's access rights.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>The remaining entry types expand the traditional permissions
+          model:</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>ACL_USER: grants access rights to a certain user.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_GROUP: grants access rights to a certain
+                group.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_MASK: limits the maximum access rights which can be
+                granted by entries of type ACL_GROUP_OBJ, ACL_USER and
+                ACL_GROUP. As the name suggests, this entry acts as a mask.
+                Only one ACL_MASK entry is allowed per ACL. If an ACL contains
+                ACL_USER or ACL_GROUP entries, an ACL_MASK entry must be
+                present too, otherwise it is optional.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>In order to maintain compatibility with applications not aware
+          of ACLs, Posix 1003.1e changes the semantics of system calls and
+          utilities which retrieve or manipulate an objects UNIX permissions.
+          In case an object only has a minimal ACL, the group permissions bits
+          of the UNIX permissions correspond to the value of the ACL_GROUP_OBJ
+          entry.</para>
+
+          <para>However, if the ACL also contains an ACL_MASK entry, the
+          behavior of those system calls and utilities is different. The group
+          permissions bits of the UNIX permissions correspond to the value of
+          the ACL_MASK entry, i. e. calling "chmod g-w" will not only revoke
+          write access for the group, but for all entities which have been
+          granted write access by ACL_USER or ACL_GROUP entries.</para>
+        </sect4>
+
+        <sect4>
+          <title>Mapping POSIX ACLs to OS X ACLs</title>
+
+          <para>When a client wants to read an object's ACL, afpd maps it's
+          Posix ACL onto an equivalent OS X ACL. Writing an object's ACL
+          requires afpd to map an OS X ACL onto a Posix ACL. Due to
+          architectural restrictions of Posix ACLs, it is usually impossible
+          to find an exact mapping so that the result of the mapping process
+          will be an approximation of the original ACL's semantic.</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>afpd silently discard entries which deny a set of
+                permissions because they they can't be represented within the
+                Posix architecture.</para>
+              </listitem>
+
+              <listitem>
+                <para>As entries within Posix ACLs are unordered, it is
+                impossible to preserve order.</para>
+              </listitem>
+
+              <listitem>
+                <para>Inheritance control is subject to severe limitations as
+                well:<itemizedlist>
+                    <listitem>
+                      <para>Entries with the only_inherit flag set will only
+                      become part of the directory's default ACL.</para>
+                    </listitem>
+
+                    <listitem>
+                      <para>Entries with at least one of the flags
+                      file_inherit, directory_inherit or limit_inherit set,
+                      will become part of the directory's access and default
+                      ACL, but the restrictions they impose on inheritance
+                      will be ignored.</para>
+                    </listitem>
+                  </itemizedlist></para>
+              </listitem>
+
+              <listitem>
+                <para>The lack of a fine-granular permission model on the
+                Posix side will normally result in an increase of granted
+                permissions.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>As OS X clients aren't aware of the Posix 1003.1e specific
+          relationship between UNIX permissions and ACL_MASK, afpd does not
+          expose this feature to the client to avoid compatibility issues and
+          handles *unix permissions and ACLs the same way as Apple's reference
+          implementation of AFP does. When an object's UNIX permissions are
+          requested, afpd calculates proper group rights and returns the
+          result together with the owner's and everybody's access rights to
+          the caller via "permissions" and "ua_permissions" members of the
+          FPUnixPrivs structure (see Apple Filing Protocol Reference, page
+          181). Changing an object's permissions, afpd always updates
+          ACL_USER_OBJ, ACL_GROUP_OBJ and ACL_OTHERS. If an ACL_MASK entry is
+          present too, afpd recalculates it's value so that the new group
+          rights become effective and existing entries of type ACL_USER or
+          ACL_GROUP stay intact.</para>
+        </sect4>
+      </sect3>
+    </sect2>
+
+    <sect2 id="fce">
+      <title>Filesystem Change Events<indexterm>
+          <primary>FCE</primary>
+        </indexterm></title>
+
+      <para>Netatalk includes a nifty filesystem change event mechanism where
+      afpd processes notfiy interested listeners about certain filesystem
+      event by UDP network datagrams.</para>
+
+      <para>For the format of the UDP packets and for an example C application
+      that demonstrates how to use these in a listener, take a look at the
+      Netatalk sourcefile <filename>bin/misc/fce.c</filename>.</para>
+
+      <para>The currently supported FCE events are<itemizedlist>
+          <listitem>
+            <para>file modification (fmod)</para>
+          </listitem>
+
+          <listitem>
+            <para>file deletion (fdel)</para>
+          </listitem>
+
+          <listitem>
+            <para>directory deletion (ddel)</para>
+          </listitem>
+
+          <listitem>
+            <para>file creation (fcre)</para>
+          </listitem>
+
+          <listitem>
+            <para>directory deletion (ddel)</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>For details on the available simple configuration options take a
+      look at <filename><link
+      linkend="fceconf">afp.conf</link></filename>.</para>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title id="spotlight">Spotlight<indexterm>
+        <primary>Spotlight</primary>
+      </indexterm></title>
+
+    <para>Starting with version 3.1 Netatalk supports Spotlight searching.
+    Netatalk uses Gnome <ulink url="https://projects.gnome.org/tracker/">Tracker</ulink> as metadata store,
+    indexer and search engine.</para>
+
+    <sect2>
+      <title>Configuration</title>
+
+      <para>You can enable Spotlight and indexing either globally or on a per
+      volume basis with the <option>spotlight</option> option.</para>
+
+      <warning>
+        <para>Once Spotlight is enable for a single volume, all other volumes
+        for which spotlight is disabled won't be searchable at all.</para>
+      </warning>
+
+      <para>In case the <command>dbus-daemon</command> binary is not installed
+      at the path <filename>/bin/dbus-daemon</filename>, you must use the
+      global option <option>dbus daemon</option> to point to the path, eg for
+      Solaris with Tracker from OpenCSW: <screen>dbus daemon = /opt/csw/bin/dbus-daemon</screen></para>
+    </sect2>
+
+    <sect2>
+      <title>Limitations and notes</title>
+
+      <itemizedlist>
+        <listitem>
+          <para>Large filesystems</para>
+
+          <para>Tracker on Linux uses the inotify Kernel filesystem change
+          event API for tracking filesystem changes. On large filesystems this
+          may be problematic since the inotify API doesn't offer recursive
+          directory watches but instead requires that for every subdirectoy
+          watches must be added individually.</para>
+
+          <para>On Solaris the FEN file event notification system is used. It
+          is unkown which limitations and ressource consumption this Solaris
+          subsystem may have.</para>
+
+          <para>We therefor recommend to disable live filesystem monitoring
+          and let Tracker periodically scan filesystems for changes instead,
+          see the Tracker configuration options <link
+          linkend="enable-monitors">enable-monitors</link> and <link
+          linkend="crawling-interval">crawling-interval</link> below.</para>
+        </listitem>
+      </itemizedlist>
+    </sect2>
+
+    <sect2>
+      <title>Using Tracker commandline tools on the server</title>
+
+      <para>Netatalk must be running, commands must be executed as root and
+      some environent variables must be set up (adjust PREFIX to point to
+      the base directory Netatalk in installed to):<screen>$ su
+# cat .tracker_profile
+PREFIX="/"
+export XDG_DATA_HOME="$PREFIX/var/netatalk/"
+export XDG_CACHE_HOME="$PREFIX/var/netatalk/"
+export DBUS_SESSION_BUS_ADDRESS="unix:path=$PREFIX/var/netatalk/spotlight.ipc"
+# . .tracker_profile
+#
+</screen></para>
+
+      <para>When using Tracker from OpenCSW you must also update your
+      PATH:<screen># export PATH=/opt/csw/bin:$PATH</screen></para>
+
+      <sect3>
+        <title>Starting and stopping Tracker</title>
+
+        <variablelist>
+          <varlistentry>
+            <term>Querying Tracker status</term>
+
+            <listitem>
+              <screen># tracker-control -S</screen>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>Stop Tracker</term>
+
+            <listitem>
+              <screen># tracker-control -t</screen>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>Start Tracker status</term>
+
+            <listitem>
+              <screen># tracker-control -s</screen>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </sect3>
+
+      <sect3>
+        <title>Reindex directory</title>
+
+        <screen># tracker-control -f PATH</screen>
+      </sect3>
+
+      <sect3>
+        <title>Query Tracker for information about a file or directory</title>
+
+        <screen># tracker-info PATH</screen>
+      </sect3>
+
+      <sect3>
+        <title>Search Tracker</title>
+
+        <screen># tracker-search QUERY</screen>
+      </sect3>
+    </sect2>
+
+    <sect2>
+      <title>Advanced Tracker command line configuration</title>
+
+      <para>Tracker stores its configuration via Gnome dconf backend which can
+      be modified with the command <command>gsettings</command>.</para>
+
+      <para>Gnome dconf settings are per-user settings, so, as Netatalk runs
+      the Tracker processes as root, the settings are stored in the root user
+      context and reading or changing these settings must be perfomed as root
+      and Netatalk must be running (and again the enviroment must be set up
+      as shown above).</para>
+
+      <para><screen># gsettings list-recursively | grep Tracker
+org.freedesktop.Tracker.Writeback verbosity 'debug'
+...</screen></para>
+
+      <para>The following list describes some important Tracker options and
+      their default settings.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>org.freedesktop.Tracker.Miner.Files
+          index-recursive-directories</term>
+
+          <listitem>
+            <para>This option controls which directories Tracker will index.
+            Don't change this option manually as it is automatically set by
+            Netatalk reflecting the setting of the <option>Spotlight</option>
+            option of Netatalk volumes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term id="enable-monitors">org.freedesktop.Tracker.Miner.Files
+          enable-monitors <parameter> true</parameter></term>
+
+          <listitem>
+            <para>The value controls whether Tracker watches all configured
+            paths for modification. Depending on the filesystem modification
+            backend (FAM on Linux, FEN on Solaris), this feature may not work
+            as reliable as one might wish, so it may be safer to disable it
+            and instead rely on periodic crawling of Tracker itself. See aslo
+            the option <option>crawling-interval </option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term id="crawling-interval">org.freedesktop.Tracker.Miner.Files
+          crawling-interval <parameter>-1</parameter></term>
+
+          <listitem>
+            <para>Interval in days to check the filesystem is up to date in
+            the database, maximum is 365, default is -1. -2 = crawling is
+            disabled entirely, -1 = crawling *may* occur on startup (if not
+            cleanly shutdown), 0 = crawling is forced</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </sect2>
+
+    <sect2>
+      <title>Supported metadata attributes</title>
+
+      <para>The following table lists the supported Spotlight metadata
+      attributes</para>
+
+      <table>
+        <title>Supported Spotlight metadata attributes</title>
+
+        <tgroup cols="2">
+          <thead>
+            <row>
+              <entry align="center">Description</entry>
+
+              <entry align="center">Spotlight Key</entry>
+            </row>
+          </thead>
+
+          <tbody>
+            <row>
+              <entry>Name</entry>
+
+              <entry>kMDItemDisplayName, kMDItemFSName</entry>
+            </row>
+
+            <row>
+              <entry>Document content (full text search)</entry>
+
+              <entry>kMDItemTextContent</entry>
+            </row>
+
+            <row>
+              <entry>File type</entry>
+
+              <entry>_kMDItemGroupId, kMDItemContentTypeTree</entry>
+            </row>
+
+            <row>
+              <entry>File modification date</entry>
+
+              <entry>kMDItemFSContentChangeDate,
+              kMDItemContentModificationDate,
+              kMDItemAttributeChangeDate</entry>
+            </row>
+
+            <row>
+              <entry>Content Creation date</entry>
+
+              <entry>kMDItemContentCreationDate</entry>
+            </row>
+
+            <row>
+              <entry>The author, or authors, of the contents of the
+              file</entry>
+
+              <entry>kMDItemAuthors, kMDItemCreator</entry>
+            </row>
+
+            <row>
+              <entry>The name of the country where the item was
+              created</entry>
+
+              <entry>kMDItemCountry</entry>
+            </row>
+
+            <row>
+              <entry>Duration</entry>
+
+              <entry>kMDItemDurationSeconds</entry>
+            </row>
+
+            <row>
+              <entry>Number of pages</entry>
+
+              <entry>kMDItemNumberOfPages</entry>
+            </row>
+
+            <row>
+              <entry>Document title</entry>
+
+              <entry>kMDItemTitle</entry>
+            </row>
+
+            <row>
+              <entry>The width, in pixels, of the contents. For example, the
+              image width or the video frame width</entry>
+
+              <entry>kMDItemPixelWidth</entry>
+            </row>
+
+            <row>
+              <entry>The height, in pixels, of the contents. For example, the
+              image height or the video frame height</entry>
+
+              <entry>kMDItemPixelHeight</entry>
+            </row>
+
+            <row>
+              <entry>The color space model used by the document
+              contents</entry>
+
+              <entry>kMDItemColorSpace</entry>
+            </row>
+
+            <row>
+              <entry>The number of bits per sample</entry>
+
+              <entry>kMDItemBitsPerSample</entry>
+            </row>
+
+            <row>
+              <entry>Focal length of the lens, in millimeters</entry>
+
+              <entry>kMDItemFocalLength</entry>
+            </row>
+
+            <row>
+              <entry>ISO speed</entry>
+
+              <entry>kMDItemISOSpeed</entry>
+            </row>
+
+            <row>
+              <entry>Orientation of the document. Possible values are 0
+              (landscape) and 1 (portrait)</entry>
+
+              <entry>kMDItemOrientation</entry>
+            </row>
+
+            <row>
+              <entry>Resolution width, in DPI</entry>
+
+              <entry>kMDItemResolutionWidthDPI</entry>
+            </row>
+
+            <row>
+              <entry>Resolution height, in DPI</entry>
+
+              <entry>kMDItemResolutionHeightDPI</entry>
+            </row>
+
+            <row>
+              <entry>Exposure time, in seconds</entry>
+
+              <entry>kMDItemExposureTimeSeconds</entry>
+            </row>
+
+            <row>
+              <entry>The composer of the music contained in the audio
+              file</entry>
+
+              <entry>kMDItemComposer</entry>
+            </row>
+
+            <row>
+              <entry>The musical genre of the song or composition</entry>
+
+              <entry>kMDItemMusicalGenre</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table>
+
+    </sect2>
+
+    <sect2>
+      <title>References</title>
+
+      <orderedlist>
+        <listitem>
+          <para><ulink
+          url="https://developer.apple.com/library/mac/#documentation/Carbon/Reference/MDItemRef/Reference/reference.html">MDItem</ulink></para>
+        </listitem>
+
+        <listitem>
+          <para><ulink
+          url="https://live.gnome.org/Tracker/Documentation">Tracker</ulink></para>
+        </listitem>
+      </orderedlist>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Starting and stopping Netatalk</title>
+
+    <para>The Netatalk distribution comes with several operating system
+    specific startup script templates that are tailored according to the
+    options given to the "configure" script before compiling. Currently,
+    templates are provided for RedHat (sysv style), RedHat (systemd style),
+    SUSE (sysv style), SUSE (systemd style), Gentoo, NetBSD, Debian and
+    Solaris. You can select to install the generated startup script(s)
+    <indexterm>
+        <primary>Startscript</primary>
+
+        <secondary>startup script</secondary>
+      </indexterm> by specifying a system type to "configure". To
+    automatically install startup scripts give one of the available
+    <option>--with-init-style</option> option to "configure".</para>
+
+    <para>Since new releases of Linux distributions appear all the time and
+    the startup procedure for the other systems mentioned above might change
+    as well, it is probably a good idea to not blindly install a startup
+    script but to look at it first to see if it will work on your system. If
+    you use Netatalk as part of a fixed setup, like a Linux distribution, an
+    RPM or a BSD package, things will probably have been arranged properly for
+    you. The following therefore applies mostly for people who have compiled
+    Netatalk themselves.</para>
+
+    <para>The following daemon need to be started by whatever startup script
+    mechanism is used:</para>
+
+    <itemizedlist>
+      <listitem>
+        <para>netatalk<indexterm>
+            <primary>netatalk</primary>
+          </indexterm></para>
+      </listitem>
+    </itemizedlist>
+
+    <para>Additionally, make sure that the configuration file
+    <filename>afp.conf</filename> is in the right place.</para>
+  </sect1>
+</chapter>
diff --git a/doc/manual/install.xml b/doc/manual/install.xml
new file mode 100644 (file)
index 0000000..74de545
--- /dev/null
@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="installation">
+  <chapterinfo>
+    <date>4.8.2013</date>
+  </chapterinfo>
+
+  <title>Installation</title>
+
+  <warning>
+    <para>If you have previously used an older version of Netatalk, please
+    read the chapter about <link linkend="upgrade">upgrading</link> first
+    !!!</para>
+  </warning>
+
+  <sect1>
+    <title>How to obtain Netatalk</title>
+
+    <para>Please have a look at the netatalk page on sourceforge for the most
+    recent informations on this issue.</para>
+
+    <para><ulink
+    url="http://sourceforge.net/projects/netatalk/">http://sourceforge.net/projects/netatalk/</ulink></para>
+
+    <sect2>
+      <title>Binary packages</title>
+
+      <para>Binary packages of Netatalk are included in some Linux and UNIX
+      distributions. You might want to have a look at the usual locations,
+      too.</para>
+
+      <para>Ubuntu package: <ulink
+      url="https://launchpad.net/ubuntu">https://launchpad.net/ubuntu
+      </ulink></para>
+
+      <para>Debian package: <ulink
+      url="http://packages.debian.org/">http://packages.debian.org/
+      </ulink></para>
+
+      <para>various RPM package: <ulink
+      url="http://rpmfind.net/">http://rpmfind.net/ </ulink></para>
+
+      <para>Fedora/RHEL package: <ulink
+      url="http://koji.fedoraproject.org/koji/search">http://koji.fedoraproject.org/koji/search
+      </ulink></para>
+
+      <para>Gentoo package: <ulink
+      url="http://packages.gentoo.org/">http://packages.gentoo.org/
+      </ulink></para>
+
+      <para>openSUSE package: <ulink
+      url="http://software.opensuse.org/">http://software.opensuse.org/
+      </ulink></para>
+
+      <para>Solaris package: <ulink
+      url="http://www.opencsw.org/packages/CSWnetatalk/">http://www.opencsw.org/</ulink></para>
+
+      <para>FreeBSD ports: <ulink
+      url="http://www.freebsd.org/ports/index.html">http://www.freebsd.org/ports/index.html
+      </ulink></para>
+
+      <para>NetBSD pkgsrc: <ulink
+      url="http://pkgsrc.se/search.php">http://pkgsrc.se/search.php
+      </ulink></para>
+
+      <para>OpenBSD ports:<ulink
+      url="http://openports.se/search.php">http://openports.se/search.php
+      </ulink></para>
+
+      <para>etc.<indexterm>
+          <primary>RPM</primary>
+
+          <secondary>Red Hat Package Manager package</secondary>
+        </indexterm><indexterm>
+          <primary>Deb</primary>
+
+          <secondary>Debian package</secondary>
+        </indexterm><indexterm>
+          <primary>Ports</primary>
+
+          <secondary>FreeBSD port</secondary>
+        </indexterm></para>
+    </sect2>
+
+    <sect2>
+      <title>Source packages</title>
+
+      <sect3>
+        <title>Tarballs</title>
+
+        <para>Prepacked tarballs in .tar.gz and tar.bz2 format are available
+        on the netatalk page on <ulink
+        url="http://netatalk.sourceforge.net/">sourceforge</ulink>.</para>
+      </sect3>
+
+      <sect3>
+        <title>Git</title>
+
+        <para>Downloading the Git repository can be done quickly and
+        easily:</para>
+
+        <orderedlist>
+          <listitem>
+            <para>Make sure you have Git installed. <command>which
+            git</command> should produce a path to git.</para>
+
+            <screen><prompt>$ </prompt><userinput>which git</userinput>
+<computeroutput>/usr/bin/git</computeroutput></screen>
+          </listitem>
+
+          <listitem>
+            <para>Now get the source:</para>
+
+            <screen><prompt>$</prompt> <userinput>git clone -b develop git://git.code.sf.net/p/netatalk/code netatalk-code
+</userinput><computeroutput>Initialized empty Git repository in /path/to/new/source/dir/netatalk/.git/
+remote: Counting objects: 2503, done.
+...
+</computeroutput></screen>
+
+            <para>This will create a local directory called
+            <filename>netatalk-code</filename> containing a complete and fresh
+            copy of the whole Netatalk source from the Git repository.</para>
+          </listitem>
+
+          <listitem>
+            <para>In order to keep your repository copy updated, occasionally
+            run:</para>
+
+            <screen><prompt>$</prompt> <userinput>git pull</userinput></screen>
+          </listitem>
+
+          <listitem>
+            <para>Now <command>cd</command> to the netatalk directory and run
+            <command>./bootstrap</command>. This will create the
+            <filename>configure</filename> script required in the next
+            step.</para>
+
+            <screen><prompt>$</prompt> <userinput>./bootstrap</userinput></screen>
+          </listitem>
+        </orderedlist>
+
+        <para>For futher information refer to this <ulink
+        url="http://netatalk.sourceforge.net/wiki/index.php/Developer_Infos">wiki</ulink>
+        page.</para>
+      </sect3>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Compiling Netatalk</title>
+
+    <sect2>
+      <title>Prerequisites</title>
+
+      <sect3>
+        <title>Required third party software</title>
+
+        <itemizedlist>
+          <listitem>
+            <para>Berkeley DB<indexterm>
+                <primary>BDB</primary>
+                <secondary>Berkeley DB</secondary>
+              </indexterm>.</para>
+            <para>At the time of writing you need at least version 4.6.</para>
+          </listitem>
+
+          <listitem>
+            <para>Libgcrypt</para>
+
+            <para>Required for OS X 10.7 and later. Libgcrypt is needed for
+            DHX2.</para>
+
+            <para>Libgcrypt can be downloaded from: <ulink
+            url="http://directory.fsf.org/wiki/Libgcrypt">
+            http://directory.fsf.org/wiki/Libgcrypt</ulink>.</para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+
+      <sect3>
+        <title>Optional third party software</title>
+
+        <para>Netatalk can use the following third party software to enhance
+        it's functionality.</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Tracker for Spotlight<indexterm>
+                <primary>Spotlight</primary>
+              </indexterm> support</para>
+
+            <para>Netatalk uses <ulink
+            url="http://projects.gnome.org/tracker/">Tracker</ulink> as the
+            metadata backend. Recent Linux distributions will provide the
+            libtracker-sparql library which is available since Tracker version
+            0.7.</para>
+          </listitem>
+
+          <listitem>
+            <para>mDNSresponderPOSIX or Avahi for Bonjour (aka
+            Zeroconf)</para>
+
+            <para>Mac OS X 10.2 and later use Bonjour (aka Zeroconf) for
+            service discovery.</para>
+
+            <para>Avahi must be build with DBUS support (
+            <userinput>--enable-dbus</userinput>).</para>
+          </listitem>
+
+          <listitem>
+            <para>TCP wrappers</para>
+
+            <para>Wietse Venema's network logger, also known as TCPD or
+            LOG_TCP.</para>
+
+            <para>Security options are: access control per host, domain and/or
+            service; detection of host name spoofing or host address spoofing;
+            booby traps to implement an early-warning system.</para>
+          </listitem>
+
+          <listitem>
+            <para>PAM<indexterm>
+                <primary>PAM</primary>
+
+                <secondary>Pluggable Authentication Modules</secondary>
+              </indexterm></para>
+
+            <para>PAM provides a flexible mechanism for authenticating users.
+            PAM was invented by SUN<indexterm>
+                <primary>SUN</primary>
+
+                <secondary>Sun Microsystems</secondary>
+              </indexterm> Microsystems. Linux-PAM is a suite of shared
+            libraries that enable the local system administrator to choose how
+            applications authenticate users.</para>
+          </listitem>
+
+          <listitem>
+            <para>iconv</para>
+
+            <para>iconv provides conversion routines for many character
+            encodings. Netatalk uses it to provide charsets it does not have
+            built in conversions for, like ISO-8859-1. On glibc systems,
+            Netatalk can use the glibc provided iconv implementation.
+            Otherwise you can use the GNU libiconv implementation.</para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+    </sect2>
+
+    <sect2 id="compiling-netatalk">
+      <title>Compiling<indexterm>
+          <primary>Compile</primary>
+
+          <secondary>Compiling Netatalk from Source</secondary>
+        </indexterm> Netatalk</title>
+
+      <sect3>
+        <title>Configuring the build</title>
+
+        <para>To build the binaries, first run the program
+        <command>./configure</command> in the source directory. This should
+        automatically configure Netatalk for your operating system. If you
+        have unusual needs, then you may wish to run</para>
+
+        <screen>$ <userinput>./configure --help</userinput></screen>
+
+        <para>to see what special options you can enable.</para>
+
+        <para>The most used configure options are:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para><option>--with-init-style</option>=redhat-sysv|redhat-systemd|suse-sysv|suse-systemd|gentoo|netbsd|debian|solaris|systemd</para>
+
+            <para>This option helps netatalk to determine where to install the
+            start scripts.</para>
+          </listitem>
+
+          <listitem>
+            <para><option>--with-bdb</option>=<replaceable>/path/to/bdb/installation/</replaceable></para>
+
+            <para>In case you installed Berkeley DB in a non-standard
+            location, you will <emphasis>have</emphasis> to give the install
+            location to netatalk, using this switch.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>Now run configure with any options you need</para>
+
+        <screen><prompt>$</prompt> <userinput>./configure [arguments]</userinput></screen>
+
+        <para>Configure will end up in an overview showing the settings the
+        Netatalk Makefiles have been created with.</para>
+      </sect3>
+
+      <sect3 id="spotlight-compile">
+        <title>Spotlight<indexterm>
+            <primary>Spotlight</primary>
+          </indexterm></title>
+
+        <para>Netatalk uses Gnome <ulink url="https://projects.gnome.org/tracker/">Tracker</ulink> as the
+        metadata backend. The minimum required version is 0.7 as that's the
+        first version to support <ulink url="https://wiki.gnome.org/Tracker/Documentation">SPARQL</ulink>.</para>
+
+        <para>If not already installed, install the packages
+        <emphasis>tracker</emphasis> and <emphasis>tracker-devel</emphasis>,
+        on Solaris install <ulink url="http://www.opencsw.org/">OpenCSW</ulink> and then install
+        the Tracker package from the OpenCSW unstable repository.</para>
+
+        <para>The tracker packages are found via pkg-config, you may have to
+        pass the version suffix as you may have a newer version installed then
+        the default 0.12, eg</para>
+
+        <screen><prompt>$ </prompt><userinput>pkg-config --list-all | grep tracker
+</userinput>tracker-extract-0.16  tracker-extract - Tracker : A library to develop metadata extractors for 3rd party file types.
+tracker-sparql-0.16   tracker-sparql - Tracker : A library to perform SPARQL queries and updates in the              Tracker Store
+tracker-miner-0.16    tracker-miner - A library to develop tracker data miners</screen>
+
+        <para>So:</para>
+
+        <screen><prompt>$ </prompt><userinput>./configure --with-tracker-pkgconfig-version=0.16 ...</userinput></screen>
+
+        <para>If you're using Solaris and Tracker from OpenCSW, then you need
+        to set the PKG_CONFIG_PATH environment variable, add the
+        --with-tracker-prefix configure option and add
+        LDFLAGS="-R/opt/csw/lib"</para>
+
+        <screen>PKG_CONFIG_PATH=/opt/csw/lib/pkgconfig LDFLAGS="-R/opt/csw/lib" ./configure --with-tracker-prefix=/opt/csw --with-tracker-pkgconfig-version=0.16 ...</screen>
+
+        <para>Check the configure output whether the Tracker libs were
+        found:</para>
+
+        <screen>checking for TRACKER... yes
+checking for TRACKER_MINER... yes
+...
+Configure summary:
+...
+  AFP:
+    Spotlight: yes
+...</screen>
+      </sect3>
+
+      <sect3>
+        <title>Compile and install</title>
+
+        <para>Next, running</para>
+
+        <screen><prompt>$</prompt> <userinput>make</userinput></screen>
+
+        <para>should produce the Netatalk binaries (this step can take several
+        minutes to complete).</para>
+
+        <para>When the process finished you can use</para>
+
+        <screen><prompt>$</prompt> <userinput>make install</userinput></screen>
+
+        <para>to install the binaries and documentation (must be done as
+        "root" when using default locations).</para>
+      </sect3>
+    </sect2>
+  </sect1>
+</chapter>
diff --git a/doc/manual/intro.xml b/doc/manual/intro.xml
new file mode 100644 (file)
index 0000000..0b70a9a
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="intro">
+  <title>Introduction to Netatalk</title>
+
+  <para>Netatalk is an OpenSource software package, that can be used to turn
+  a *NIX machine into an extremely high-performance and
+  reliable file server for Macintosh computers.</para>
+
+  <para>Using Netatalk's AFP 3.3 compliant file-server leads to significantly
+  higher transmission speeds compared with Macs accessing a server via
+  SaMBa/NFS while providing clients with the best possible user experience
+  (full support for Macintosh metadata, flawlessly supporting mixed
+  environments of classic Mac OS and OS X clients)</para>
+
+</chapter>
diff --git a/doc/manual/manual.xml.in b/doc/manual/manual.xml.in
new file mode 100644 (file)
index 0000000..7c067ef
--- /dev/null
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+
+<!ENTITY Intro SYSTEM "intro.xml">
+<!ENTITY Install SYSTEM "install.xml">
+<!ENTITY Upgrade SYSTEM "upgrade.xml">
+<!ENTITY Configuration SYSTEM "configuration.xml">
+
+<!ENTITY ad.1 SYSTEM "../manpages/man1/ad.1.xml">
+<!ENTITY afpd.8 SYSTEM "../manpages//man8/afpd.8.xml">
+<!ENTITY cnid_dbd.8 SYSTEM "../manpages//man8/cnid_dbd.8.xml">
+<!ENTITY cnid_metad.8 SYSTEM "../manpages//man8/cnid_metad.8.xml">
+<!ENTITY afp.conf.5 SYSTEM "../manpages//man5/afp.conf.5.xml">
+<!ENTITY afp_signature.conf.5 SYSTEM "../manpages//man5/afp_signature.conf.5.xml">
+<!ENTITY afp_voluuid.conf.5 SYSTEM "../manpages//man5/afp_voluuid.conf.5.xml">
+<!ENTITY afpldaptest.1 SYSTEM "../manpages//man1/afpldaptest.1.xml">
+<!ENTITY afppasswd.1 SYSTEM "../manpages//man1/afppasswd.1.xml">
+<!ENTITY afpstats.1 SYSTEM "../manpages//man1/afpstats.1.xml">
+<!ENTITY apple_dump.1 SYSTEM "../manpages//man1/apple_dump.1.xml">
+<!ENTITY extmap.conf.5 SYSTEM "../manpages//man5/extmap.conf.5.xml">
+<!ENTITY macusers.1 SYSTEM "../manpages//man1/macusers.1.xml">
+<!ENTITY megatron.1 SYSTEM "../manpages//man1/megatron.1.xml">
+<!ENTITY netatalk.8 SYSTEM "../manpages//man8/netatalk.8.xml">
+<!ENTITY netatalk-config.1 SYSTEM "../manpages//man1/netatalk-config.1.xml">
+<!ENTITY uniconv.1 SYSTEM "../manpages//man1/uniconv.1.xml">
+<!ENTITY asip-status.pl.1 SYSTEM "../manpages//man1/asip-status.pl.1.xml">
+<!ENTITY dbd.1 SYSTEM "../manpages//man1/dbd.1.xml">
+]>
+<book id="netatalk-manual">
+  <title>Netatalk 3.1 Manual</title>
+  
+  <bookinfo>
+    <date>07-01-2013</date>
+    <releaseinfo>@NETATALK_VERSION@</releaseinfo>
+  </bookinfo>
+
+ <?latex \setcounter{page}{3} ?>
+<preface>
+        <title>Legal Notice</title>
+<para>
+This documentation is distributed under the GNU General Public License (GPL) version 2.  
+A copy of the license is included in this documentation, as well as within the Netatalk source
+distribution.  An on-line copy can be found at <ulink
+url="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</ulink>
+</para>
+</preface>
+
+  <?latex \cleardoublepage ?>
+  <!-- Contents -->
+  <toc/>
+  <?latex \cleardoublepage ?>
+  <?latex \pagenumbering{arabic} ?>
+
+  &Intro;
+  <?latex \cleardoublepage ?>
+
+  &Install;
+  <?latex \cleardoublepage ?>
+
+  &Configuration;
+  <?latex \cleardoublepage ?>
+
+  &Upgrade;
+  <?latex \cleardoublepage ?>
+
+  <chapter id="man-pages">
+    <title>Manual Pages</title>
+
+    <para>This is a collection of the man pages delivered with Netatalk.</para>
+
+    &ad.1;
+
+    &afp.conf.5;
+
+    &afp_signature.conf.5;
+
+    &afp_voluuid.conf.5;
+
+    &afpd.8;
+
+    &afpldaptest.1;
+
+    &afppasswd.1;
+
+    &afpstats.1;
+
+    &apple_dump.1;
+
+    &asip-status.pl.1;
+
+    &cnid_dbd.8;
+
+    &cnid_metad.8;
+
+    &dbd.1;
+
+    &extmap.conf.5;
+
+    &macusers.1;
+
+    &megatron.1;
+
+    &netatalk.8;
+
+    &netatalk-config.1;
+
+    &uniconv.1;
+  </chapter>
+  <?latex \cleardoublepage ?>
+
+  <?latex \include{gpl}?>
+  <?latex \cleardoublepage ?>
+
+  <index id="manual-index"><title>Index</title></index>
+</book>
diff --git a/doc/manual/netatalk.html b/doc/manual/netatalk.html
new file mode 100644 (file)
index 0000000..0c7e337
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+    <div id="header">
+        <div id="logo"></div>
+        <div id="menlinks">
+          <a href="/" title="Return to Netatalk home">[main]</a>
+          <a href="http://netatalk.sourceforge.net/wiki/" title="Netatalk Wiki">[wiki]</a>
+          <a href="/3.1/htmldocs" title="Netatalk Manual">[documentation]</a>
+          <a href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Download Netatalk from sourceforge">[downloads]</a>
+          <a href="/support.php" title="Support">[support]</a>
+          <a href="/links.php" title="Netatalk related links">[links]</a>
+          <img src="/gfx/end.gif" alt="" width="125" height="7" />
+        </div>
+    </div>
+</html>
diff --git a/doc/manual/upgrade.xml b/doc/manual/upgrade.xml
new file mode 100644 (file)
index 0000000..2d85e74
--- /dev/null
@@ -0,0 +1,1540 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="upgrade">
+  <chapterinfo>
+    <date>9.6.2013</date>
+
+    <author>
+      <firstname>Frank</firstname>
+
+      <surname>Lahm</surname>
+    </author>
+
+    <pubdate>6 Sep, 2013</pubdate>
+  </chapterinfo>
+
+  <title>Upgrading from Netatalk 2</title>
+
+  <sect1>
+    <title>Overview</title>
+
+    <para>There are two major changes in Netatalk:<orderedlist>
+        <listitem>
+          <para>New configuration file <filename><link
+          linkend="afp.conf.5">afp.conf</link></filename>, obsoleting all
+          previous configuration files</para>
+        </listitem>
+
+        <listitem>
+          <para>New AppleDouble backend "<option>appledouble = ea</option>"
+          which stores Mac metadata and resource forks in extended attributes
+          of the filesystem</para>
+        </listitem>
+      </orderedlist></para>
+
+    <sect2>
+      <title>New configuration</title>
+
+      <para><itemizedlist>
+          <listitem>
+            <para>ini style syntax (like Samba’s smb.conf)</para>
+          </listitem>
+
+          <listitem>
+            <para>one to rule them all: configure AFP settings and volumes in
+            one file</para>
+          </listitem>
+
+          <listitem>
+            <para>obsoletes <filename>afpd.conf</filename>,
+            <filename>netatalk.conf</filename>,
+            <filename>AppleVolumes.default</filename> and
+            <filename>afp_ldap.conf</filename></para>
+          </listitem>
+        </itemizedlist><warning>
+          <para>most option names have changed, read the full manpage <link
+          linkend="afp.conf.5">afp.conf</link> for details</para>
+        </warning></para>
+    </sect2>
+
+    <sect2>
+      <title>New AppleDouble backend</title>
+
+      <para>New AppleDouble backend "<option>appledouble = ea</option>" which
+      stores Mac metadata and resource forks in extended attributes of the
+      filesystem.<itemizedlist>
+          <listitem>
+            <para>default backend (!)</para>
+          </listitem>
+
+          <listitem>
+            <para>requires a filesystem with Extended Attributes, fallback is
+            "<option>appledouble = v2</option>"</para>
+          </listitem>
+
+          <listitem>
+            <para>converts filesystems from "<option>appledouble = v2</option>"
+            to "<option>appledouble = ea</option>" on the fly when accessed
+            (can be disabled)</para>
+          </listitem>
+
+          <listitem>
+            <para><command><link linkend="dbd.1">dbd</link></command> can be
+            used to do conversion in one shot</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>Implementation details:<itemizedlist>
+          <listitem>
+            <para>stores Mac Metadata (eg FinderInfo, AFP Flags, Comment,
+            CNID) in an Extended Attributed named
+            “<filename>org.netatalk.Metadata</filename>”</para>
+          </listitem>
+
+          <listitem>
+            <para>stores Mac ResourceFork either in<itemizedlist>
+                <listitem>
+                  <para>an Extended Attribute named
+                  “<filename>org.netatalk.ResourceFork</filename>”
+                  on Solaris w. ZFS, or in</para>
+                </listitem>
+
+                <listitem>
+                  <para>an extra AppleDouble file named “<filename>._file</filename>” for a file
+                  named “<filename>file</filename>”</para>
+                </listitem>
+              </itemizedlist></para>
+          </listitem>
+
+          <listitem>
+            <para>the format of the ._ file is exactly as the Mac’s CIFS
+            client expects it when accessing the same filesystem via a CIFS
+            server (Samba), thus you can have parallel access from Macs to the
+            same dataset via AFP and CIFS without the risk of loosing data
+            (resources or metadata). Accessing the same dataset with CIFS
+            from Windows clients will still break the coupling of
+            “<filename>file</filename>” and “<filename>._file</filename>”
+            on non ZFS filesystems (see above), so for this we still
+            need an enhanced Samba VFS module (in the works).</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>As these days the only applications making use of Resource Forks
+      are Adobe Photoshop (image preview) and Postscript Type 1 fonts, even on
+      eg Linux you’ll get rid of 99% of any extra Netatalk AppleDouble files
+      (and folders).</para>
+    </sect2>
+
+    <sect2>
+      <title>Other major changes</title>
+
+      <para><itemizedlist>
+          <listitem>
+            <para>New service controller daemon <link
+            linkend="netatalk.8">netatalk</link> which is responsible for
+            starting and restarting the AFP and CNID daemons. All bundled
+            start scripts have been updated, make sure to update yours!</para>
+          </listitem>
+
+          <listitem>
+            <para>The CNID databases are now stored under
+              <filename>/var/netatalk/CNID/</filename>
+              by default. You can use configure --localstatedir=PATH at
+              compile time to change the location.</para>
+          </listitem>
+
+          <listitem>
+            <para>Netatalk 2.x volume options “usedots” and “upriv” now
+            enabled by default</para>
+          </listitem>
+
+          <listitem>
+            <para>Removed SLP and AFP proxy support</para>
+          </listitem>
+
+          <listitem>
+            <para>Removed type/creator extension mapping
+            support</para>
+          </listitem>
+        </itemizedlist></para>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Upgrading</title>
+
+    <para><orderedlist>
+        <listitem>
+          <para>Stop Netatalk 2.x</para>
+        </listitem>
+
+        <listitem>
+          <para>Install Netatalk 3</para>
+        </listitem>
+
+        <listitem>
+          <para>Manually recreate configuration in
+          <option>afp.conf</option> and <option>extmap.conf</option></para>
+        </listitem>
+
+        <listitem>
+          <para>Update your Netatalk start script (SMF, systemd, whatever...)
+          to only start <link linkend="netatalk.8">netatalk</link></para>
+        </listitem>
+
+        <listitem>
+          <para>Move <filename>afp_voluuid.conf</filename> and
+          <filename>afp_signature.conf</filename> to the localstate directory (default
+          <filename>/var/netatalk/</filename>), you can use <command>afpd -v</command>
+          in order to find the correct path</para>
+        </listitem>
+
+        <listitem>
+          <para>Start Netatalk 3</para>
+        </listitem>
+      </orderedlist></para>
+  </sect1>
+
+  <sect1>
+    <title>Table with old and new configuration file names</title>
+    <para><table frame="all">
+        <title>old and new configuration file names</title>
+        <tgroup cols="3">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old File Name</entry>
+              <entry>New File Name</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>-</entry>
+              <entry><filename>etc/afp.conf</filename></entry>
+              <entry>new ini-style format</entry>
+            </row>
+            <row>
+              <entry>-</entry>
+              <entry><filename>etc/extmap.conf</filename></entry>
+              <entry>starting with netatalk 3.0.2</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_signature.conf</filename></entry>
+              <entry><filename>var/netatalk/afp_signature.conf</filename></entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_voluuid.conf</filename></entry>
+              <entry><filename>var/netatalk/afp_voluuid.conf</filename></entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/netatalk.conf</filename>
+              (<filename>/etc/default/netatalk</filename>)</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afpd.conf</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_ldap.conf</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/AppleVolumes.default</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/AppleVolumes.system</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>~/.AppleVolumes</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table></para>
+  </sect1>
+
+  <sect1>
+    <title>Table with old and new option names</title>
+    <para><table frame="all">
+        <title>from netatalk.conf (/etc/default/netatalk) to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old netatalk.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Default Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>ATALK_NAME</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>ATALK_UNIX_CHARSET</entry>
+              <entry>unix charset</entry>
+              <entry><emphasis role="bold">LOCALE</emphasis></entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ATALK_MAC_CHARSET</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_METAD_RUN</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>controlled by netatalk(8)</entry>
+            </row>
+            <row>
+              <entry>AFPD_RUN</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>controlled by netatalk(8)</entry>
+            </row>
+            <row>
+              <entry>AFPD_MAX_CLIENTS</entry>
+              <entry>max connections</entry>
+              <entry><emphasis role="bold">20</emphasis></entry>
+              <entry><emphasis role="bold">200</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>AFPD_UAMLIST</entry>
+              <entry>uam list</entry>
+              <entry>-U uams_dhx.so,uams_dhx2.so</entry>
+              <entry>uams_dhx.so uams_dhx2.so</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>AFPD_GUEST</entry>
+              <entry>guest account</entry>
+              <entry>nobody</entry>
+              <entry>nobody</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_CONFIG</entry>
+              <entry>log level</entry>
+              <entry>-l log_note</entry>
+              <entry>cnid:note</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_CONFIG</entry>
+              <entry>log file</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ATALKD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>PAPD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>TIMELORD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>A2BOOT_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>ATALK_BGROUND</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>ATALK_ZONE</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from afpd.conf to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old afpd.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Default Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>1st field ("-" or "server name")</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>-uamlist</entry>
+              <entry>uam list</entry>
+              <entry>-U uams_dhx.so,uams_dhx2.so</entry>
+              <entry>uams_dhx.so uams_dhx2.so</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-nozeroconf</entry>
+              <entry>zeroconf</entry>
+              <entry>-</entry>
+              <entry>yes (if supported)</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-advertise_ssh</entry>
+              <entry>advertise ssh</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]savepassword</entry>
+              <entry>save password</entry>
+              <entry>-savepassword</entry>
+              <entry>yes</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]setpassword</entry>
+              <entry>set password</entry>
+              <entry>-nosetpassword</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-client_polling</entry>
+              <entry>client polling</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-hostname</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>-loginmesg</entry>
+              <entry>login message</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-guestname</entry>
+              <entry>guest account</entry>
+              <entry>nobody</entry>
+              <entry>nobody</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-passwdfile</entry>
+              <entry>passwd file</entry>
+              <entry>afppasswd</entry>
+              <entry>afppasswd</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-passwdminlen</entry>
+              <entry>passwd minlen</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-tickleval</entry>
+              <entry>tickleval</entry>
+              <entry>30</entry>
+              <entry>30</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-timeout</entry>
+              <entry>timeout</entry>
+              <entry>4</entry>
+              <entry>4</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-sleep</entry>
+              <entry>sleep time</entry>
+              <entry>10</entry>
+              <entry>10</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-dsireadbuf</entry>
+              <entry>dsireadbuf</entry>
+              <entry>12</entry>
+              <entry>12</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-server_quantum</entry>
+              <entry>server quantum</entry>
+              <entry><emphasis role="bold">303840</emphasis></entry>
+              <entry><emphasis role="bold">1048576</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-volnamelen</entry>
+              <entry>volnamelen</entry>
+              <entry>80</entry>
+              <entry>80</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-setuplog</entry>
+              <entry>log level</entry>
+              <entry>default log_note</entry>
+              <entry>default:note</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-setuplog</entry>
+              <entry>log file</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-admingroup</entry>
+              <entry>admingroup</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5service</entry>
+              <entry>k5 service</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5realm</entry>
+              <entry>k5 realm</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5keytab</entry>
+              <entry>k5 keytab</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-uampath</entry>
+              <entry>uam path</entry>
+              <entry><emphasis role="bold">etc/netatalk/uams/</emphasis></entry>
+              <entry><emphasis role="bold">lib/netatalk/</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>moved to $libdir</entry>
+            </row>
+            <row>
+              <entry>-ipaddr</entry>
+              <entry>afp listen</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-cnidserver</entry>
+              <entry>cnid server</entry>
+              <entry>localhost:4700</entry>
+              <entry>localhost:4700</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-port</entry>
+              <entry>port</entry>
+              <entry>548</entry>
+              <entry>548</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-signature</entry>
+              <entry>signature</entry>
+              <entry>auto</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fqdn</entry>
+              <entry>fqdn</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-unixcodepage</entry>
+              <entry>unix charset</entry>
+              <entry><emphasis role="bold">LOCALE</emphasis></entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-maccodepage</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-closevol</entry>
+              <entry>close vol</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-ntdomain</entry>
+              <entry>nt domain</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-ntseparator</entry>
+              <entry>nt separator</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-dircachesize</entry>
+              <entry>dircachesize</entry>
+              <entry>8192</entry>
+              <entry>8192</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-tcpsndbuf</entry>
+              <entry>tcpsndbuf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>OS default</entry>
+            </row>
+            <row>
+              <entry>-tcprcvbuf</entry>
+              <entry>tcprcvbuf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>OS default</entry>
+            </row>
+            <row>
+              <entry>-fcelistener</entry>
+              <entry>fce listener</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fcecoalesce</entry>
+              <entry>fce coalesce</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fceevents</entry>
+              <entry>fce events</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fceholdfmod</entry>
+              <entry>fce holdfmod</entry>
+              <entry>60</entry>
+              <entry>60</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-mimicmodel</entry>
+              <entry>mimic model</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-adminauthuser</entry>
+              <entry>admin auth user</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-noacl2maccess</entry>
+              <entry>map acls</entry>
+              <entry>-</entry>
+              <entry>rights</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]tcp</entry>
+              <entry>-</entry>
+              <entry>-tcp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>always TCP only</entry>
+            </row>
+            <row>
+              <entry>-[no]ddp</entry>
+              <entry>-</entry>
+              <entry>-noddp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]transall</entry>
+              <entry>-</entry>
+              <entry>-tcp -noddp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>always TCP only</entry>
+            </row>
+            <row>
+              <entry>-nodebug</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-[no]slp</entry>
+              <entry>-</entry>
+              <entry>-noslp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>SLP support is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]uservolfirst</entry>
+              <entry>-</entry>
+              <entry>-nouservolfirst</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>uservol is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]uservol</entry>
+              <entry>-</entry>
+              <entry>-uservol</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>uservol is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-proxy</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-defaultvol</entry>
+              <entry>-</entry>
+              <entry>AppleVolumes.default</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>afp.conf only</entry>
+            </row>
+            <row>
+              <entry>-systemvol</entry>
+              <entry>-</entry>
+              <entry>AppleVolumes.system</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>afp.conf only</entry>
+            </row>
+            <row>
+              <entry>-loginmaxfail</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>not supported from the biginning</entry>
+            </row>
+            <row>
+              <entry>-unsetuplog</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-authprintdir</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-ddpaddr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]icon</entry>
+              <entry>-</entry>
+              <entry>-noicon</entry>
+              <entry></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-keepsessions</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete. Use kill -HUP.</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from afp_ldap.conf to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old afp_ldap.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Defalut Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>ldap_server</entry>
+              <entry>ldap server</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_method</entry>
+              <entry>ldap auth method</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_dn</entry>
+              <entry>ldap auth dn</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_pw</entry>
+              <entry>ldap auth pw</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_userbase</entry>
+              <entry>ldap userbase</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_userscope</entry>
+              <entry>ldap userscope</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_groupbase</entry>
+              <entry>ldap groupbase</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_groupscope</entry>
+              <entry>ldap groupscope</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_uuid_attr</entry>
+              <entry>ldap uuid attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_uuid_string</entry>
+              <entry>ldap uuid string</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_name_attr</entry>
+              <entry>ldap name attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry> ldap_group_attr</entry>
+              <entry>ldap group attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from AppleVolumes.* to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old AppleVolumes.*</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Defalut Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>(leading-dot lines)</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>move to extmap.conf</entry>
+            </row>
+            <row>
+              <entry>:DEFAULT:</entry>
+              <entry>-</entry>
+              <entry>options:upriv,usedots</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use "vol preset ="</entry>
+            </row>
+            <row>
+              <entry>1st field ("~")</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use [Homes] section</entry>
+            </row>
+            <row>
+              <entry>1st field ("/path")</entry>
+              <entry>path</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>2nd field</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use section name</entry>
+            </row>
+            <row>
+              <entry>allow:</entry>
+              <entry>valid users</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>deny:</entry>
+              <entry>invalid users</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>rwlist:</entry>
+              <entry>rwlist</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>rolist:</entry>
+              <entry>rolist</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>volcharset:</entry>
+              <entry>vol charset</entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry><emphasis role="bold">(same as unix charset)</emphasis></entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>maccharset:</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>veto:</entry>
+              <entry>veto files</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>cnidscheme:</entry>
+              <entry>cnid scheme</entry>
+              <entry>dbd</entry>
+              <entry>dbd</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>casefold:</entry>
+              <entry>casefold</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>adouble:</entry>
+              <entry>appledouble</entry>
+              <entry><emphasis role="bold">v2</emphasis></entry>
+              <entry><emphasis role="bold">ea</emphasis></entry>
+              <entry>(V)</entry>
+              <entry>v1, osx and sfm are obsoleted</entry>
+            </row>
+            <row>
+              <entry>cnidserver:</entry>
+              <entry>cnid server</entry>
+              <entry>localhost:4700</entry>
+              <entry>localhost:4700</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>dbpath:</entry>
+              <entry>vol dbpath</entry>
+              <entry><emphasis role="bold">(volume directory)</emphasis></entry>
+              <entry><emphasis role="bold">var/netatalk/CNID/</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry>umask:</entry>
+              <entry>umask</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>dperm:</entry>
+              <entry>directory perm</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>fperm:</entry>
+              <entry>file perm</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>password:</entry>
+              <entry>password</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>root_preexec:</entry>
+              <entry>root preexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>preexec:</entry>
+              <entry>preexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>root_postexec:</entry>
+              <entry>root postexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>postexec:</entry>
+              <entry>postexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>allowed_hosts:</entry>
+              <entry>hosts allow</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>denied_hosts:</entry>
+              <entry>hosts deny</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ea:</entry>
+              <entry>ea</entry>
+              <entry>auto</entry>
+              <entry>auto</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>volsizelimit:</entry>
+              <entry>vol size limit</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>perm:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>Use "directory perm" and "file perm"</entry>
+            </row>
+            <row>
+              <entry>forceuid:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>forcegid:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:ro</entry>
+              <entry>read only</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:invisibledots</entry>
+              <entry>invisible dots</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nostat</entry>
+              <entry>stat vol</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:preexec_close</entry>
+              <entry>preexec close</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:root_preexec_close</entry>
+              <entry>root preexec close</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:upriv</entry>
+              <entry>unix priv</entry>
+              <entry>-</entry>
+              <entry><emphasis role="bold">yes</emphasis></entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nodev</entry>
+              <entry>cnid dev</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:illegalseq</entry>
+              <entry>illegal seq</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:tm</entry>
+              <entry>time machine</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:searchdb</entry>
+              <entry>search db</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nonetids</entry>
+              <entry>network ids</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:noacls</entry>
+              <entry>acls</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:followsymlinks</entry>
+              <entry>follow symlinks</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nohex</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>auto-convert from ":2f" to ":"</entry>
+            </row>
+            <row>
+              <entry>options:usedots</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>auto-convert from ":2e" to "."</entry>
+            </row>
+            <row>
+              <entry>options:nofileid</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:prodos</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:mswindows</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:crlf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:noadouble</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:limitsize</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:dropbox</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:dropkludge</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:nocnidcache</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:caseinsensitive</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table></para>
+  </sect1>
+
+  <sect1>
+    <title>To Do</title>
+
+    <para><itemizedlist>
+        <listitem>
+          <para>test <command>ad</command> utils with <option>appledouble =
+          ea</option></para>
+        </listitem>
+      </itemizedlist></para>
+  </sect1>
+</chapter>
diff --git a/doc/www/.gitignore b/doc/www/.gitignore
new file mode 100644 (file)
index 0000000..402bcc5
--- /dev/null
@@ -0,0 +1 @@
+ReleaseNotes.html
diff --git a/doc/www/ReleaseNotes b/doc/www/ReleaseNotes
new file mode 100644 (file)
index 0000000..df491b7
--- /dev/null
@@ -0,0 +1,120 @@
+Netatalk 3.1.3
+==============
+
+The Netatalk development team is proud to announce latest release of
+the Netatalk 3.1 release series. Users are encouraged to update their
+servers to the 3.1 release series which is the stable and supported
+version for production systems.
+
+Netatalk is a freely-available Open Source AFP fileserver.
+A *NIX/*BSD system running Netatalk is capable of serving many Macintosh
+clients simultaneously as an AppleShare file server (AFP).
+
+The suite contains:
+
+* netatalk   - the main server service controller
+* afpd       - the AFP file server daemin
+* cnid_metad - the CNID database multiplexing daemon
+* cnid_dbd   - the CNID database daemon serving CNIDs for AFP volumes
+* various supporting programs and utilities
+
+Summary of major new features and enhancements in 3.1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* AFP Spotlight Support with Gnome Tracker:
+  https://projects.gnome.org/tracker/
+
+Please refer to the online manual for details about compiling Netatalk
+with Spotlight support and how to configure:
+
+http://netatalk.sourceforge.net/3.1/htmldocs/installation.html#compiling-netatalk
+
+http://netatalk.sourceforge.net/3.1/htmldocs/configuration.html
+
+Please make sure to read the upgrading section in the Netatalk online
+manual before trying to upgrade your system from version 2:
+
+http://netatalk.sourceforge.net/3.1/htmldocs/upgrade.html
+
+License
+~~~~~~~
+
+Netatalk is a Free/Open Source Software project and is released under
+the GNU General Public License (GPLv2).  The full license text is available
+at:
+
+http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+Changes in 3.1.3
+~~~~~~~~~~~~~~~~
+* UPD: Spotlight: more SPARQL query optimisations
+* UPD: Spotlight: new options "sparql results limit", "spotlight
+       attributes" and "spotlight expr"
+* FIX: afpd: Unarchiving certain ZIP archives fails, bug #569
+* UPD: Update Unicode support to version 7.0.0
+* FIX: Memory overflow caused by 'basedir regex', bug #567
+* NEW: afpd: delete empty resource forks, from FR #92
+* FIX: afpd: fix a crash when accessing ._ AppleDouble files created
+       by OS X via SMB, bug #564
+* FIX: afpd and dbd: Converting from AppleDouble v2 to ea may corrupt
+       the resource fork. In some circumstances an offset calculation
+       is wrong resulting in corrupt resource forks after the
+       conversion. Bug #568.
+* FIX: ad: fix for bug #563 broke ad file utilities, bug #570.
+* NEW: afpd: new advanced option controlling permissions and ACLs,
+       from FR #93
+
+Supported Platforms
+~~~~~~~~~~~~~~~~~~~
+
+As of Netatalk 3.0 the following operating systems are supported:
+
+ * FreeBSD
+ * Linux
+ * OpenBSD
+ * NetBSD
+ * Solaris and derivates
+
+Netatalk may compile and run on other operating systems as well, but
+it is not well-tested on those.  We welcome patches and suggestions
+for enhancing the portability of Netatalk as well as success and failure
+stories.  Please write to netatalk-devel@lists.sourceforge.net.
+
+Availability
+~~~~~~~~~~~~
+
+Netatalk tar-balls can be found at:
+
+http://sourceforge.net/project/showfiles.php?group_id=8642
+
+Netatalk is also available via anonymous git.  See the SourceForge project
+site for anonymous git instructions. 
+
+Contact
+~~~~~~~
+
+For more information about Netatalk, see its web page at:
+
+http://netatalk.sourceforge.net/
+
+The project is hosted at SourceForge.  The SourceForge project page is
+located at:
+
+http://sourceforge.net/projects/netatalk/
+
+The Netatalk development team can be reached via the mailing list
+netatalk-devel@lists.sourceforge.net.  For subscription information and
+archives see Netatalk's SourceForge project page.
+
+netatalk-admins@lists.sourceforge.net is a mailing list for Netatalk
+system administrators.  For subscription information and archives see
+the Netatalk web page.
+
+Acknowledgements
+~~~~~~~~~~~~~~~~
+
+We would like to thank all contributors to the Netatalk project for
+their commitment.  Without the many suggestions, bug and problem reports,
+patches, and reviews this project wouldn't be where it is.
+
+ - The Netatalk Development Team, July 2014
diff --git a/doc/www/asciidoc.conf b/doc/www/asciidoc.conf
new file mode 100644 (file)
index 0000000..85b3df4
--- /dev/null
@@ -0,0 +1,610 @@
+#
+# asciidoc.conf
+#
+# Asciidoc global configuration file.
+# Contains backend independent configuration settings that are applied to all
+# AsciiDoc documents.
+#
+
+[miscellaneous]
+tabsize=8
+textwidth=70
+newline=\r\n
+
+[attributes]
+backend-alias-html=xhtml11
+backend-alias-docbook=docbook45
+toclevels=2
+sectids=
+iconsdir=./images/icons
+encoding=UTF-8
+# Uncomment to use xhtml11 quirks mode CSS.
+#quirks=
+# Uncomment to use the Pygments source highlighter instead of GNU highlighter.
+#pygments=
+# Uncomment to use deprecated quote attributes.
+#deprecated-quotes=
+empty=
+# Attribute and AttributeList element patterns.
+attributeentry-pattern=^:(?P<attrname>\w[^.]*?)(\.(?P<attrname2>.*?))?:(\s+(?P<attrvalue>.*))?$
+attributelist-pattern=(?u)(^\[\[(?P<id>[\w_:][\w_:.-]*)(,(?P<reftext>.*?))?\]\]$)|(^\[(?P<attrlist>.*)\]$)
+# Substitution attributes for escaping AsciiDoc processing.
+amp=&
+lt=<
+gt=>
+brvbar=|
+nbsp=&#160;
+zwsp=&#8203;
+wj=&#8288;
+deg=&#176;
+backslash=\
+two-colons=::
+two-semicolons=;;
+# DEPRECATED: underscore attribute names.
+two_colons=::
+two_semicolons=;;
+# Left and right single and double quote characters.
+# See http://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks
+lsquo=&#8216;
+rsquo=&#8217;
+ldquo=&#8220;
+rdquo=&#8221;
+
+[titles]
+subs=specialcharacters,quotes,replacements,macros,attributes,replacements2
+# Double-line title pattern and underlines.
+sectiontitle=^(?P<title>.*?)$
+underlines="==","--","~~","^^","++"
+# Single-line title patterns.
+sect0=^= +(?P<title>[\S].*?)( +=)?$
+sect1=^== +(?P<title>[\S].*?)( +==)?$
+sect2=^=== +(?P<title>[\S].*?)( +===)?$
+sect3=^==== +(?P<title>[\S].*?)( +====)?$
+sect4=^===== +(?P<title>[\S].*?)( +=====)?$
+blocktitle=^\.(?P<title>([^.\s].*)|(\.[^.\s].*))$
+
+[specialcharacters]
+&=&amp;
+<=&lt;
+>=&gt;
+
+[quotes]
+# The order is important, quotes are processed in conf file order.
+**=#strong
+*=strong
+``|''=doublequoted
+'=emphasis
+`|'=singlequoted
+ifdef::no-inline-literal[]
+`=monospaced
+endif::no-inline-literal[]
+# +++ and $$ quoting is applied to the +++ and $$ inline passthrough
+# macros to allow quoted attributes to be used.
+# This trick only works with inline passthrough macros.
++++=#unquoted
+$$=#unquoted
+++=#monospaced
++=monospaced
+__=#emphasis
+_=emphasis
+\##=#unquoted
+\#=unquoted
+^=#superscript
+~=#subscript
+
+[specialwords]
+emphasizedwords=
+strongwords=
+monospacedwords=
+
+[replacements]
+# Replacements performed in order of configuration file entry.  The first entry
+# of each replacement pair performs the (non-escaped) replacement, the second
+# strips the backslash from the escaped replacement.
+
+# (C) Copyright (entity reference &copy;)
+(?<!\\)\(C\)=&#169;
+\\\(C\)=(C)
+
+# (R) registered trade mark (entity reference &reg;
+(?<!\\)\(R\)=&#174;
+\\\(R\)=(R)
+
+# (TM) Trademark (entity reference &trade;)
+(?<!\\)\(TM\)=&#8482;
+\\\(TM\)=(TM)
+
+# -- Spaced and unspaced em dashes (entity reference &mdash;).
+# Space on both sides is translated to thin space characters.
+(^-- )=&#8212;&#8201;
+(\n-- )|( -- )|( --\n)=&#8201;&#8212;&#8201;
+(\w)--(\w)=\1&#8212;\2
+\\--(?!-)=--
+
+# Replace vertical typewriter apostrophe with punctuation apostrophe.
+(\w)'(\w)=\1&#8217;\2
+(\w)\\'(\w)=\1'\2
+
+# ... Ellipsis (entity reference &hellip;)
+(?<!\\)\.\.\.=&#8230;
+\\\.\.\.=...
+
+# Arrows from the Arrows block of Unicode.
+# -> right arrow
+(?<!\\)-&gt;=&#8594;
+\\-&gt;=-&gt;
+# => right double arrow
+(?<!\\)\=&gt;=&#8658;
+\\\=&gt;==&gt;
+# <- left arrow
+(?<!\\)&lt;-=&#8592;
+\\&lt;-=&lt;-
+# <= left double arrow
+(?<!\\)&lt;\==&#8656;
+\\&lt;\==&lt;=
+
+# Arbitrary entity references.
+(?<!\\)&amp;([:_#a-zA-Z][:_.\-\w]*?;)=&\1
+\\(&amp;[:_#a-zA-Z][:_.\-\w]*?;)=\1
+
+#-----------
+# Paragraphs
+#-----------
+[paradef-default]
+delimiter=(?s)(?P<text>\S.*)
+posattrs=style
+style=normal
+template::[paragraph-styles]
+
+[paradef-literal]
+delimiter=(?s)(?P<text>\s+.*)
+options=listelement
+posattrs=style
+style=literal
+template::[paragraph-styles]
+
+[paradef-admonition]
+delimiter=(?s)^\s*(?P<style>NOTE|TIP|IMPORTANT|WARNING|CAUTION):\s+(?P<text>.+)
+template::[paragraph-styles]
+
+[paragraph-styles]
+normal-style=template="paragraph"
+verse-style=template="verseparagraph",posattrs=["style","attribution","citetitle"]
+quote-style=template="quoteparagraph",posattrs=["style","attribution","citetitle"]
+literal-style=template="literalparagraph",subs=["verbatim"]
+listing-style=template="listingparagraph",subs=["verbatim"]
+NOTE-style=template="admonitionparagraph",name="note",caption="{note-caption}"
+TIP-style=template="admonitionparagraph",name="tip",caption="{tip-caption}"
+IMPORTANT-style=template="admonitionparagraph",name="important",caption="{important-caption}"
+WARNING-style=template="admonitionparagraph",name="warning",caption="{warning-caption}"
+CAUTION-style=template="admonitionparagraph",name="caution",caption="{caution-caption}"
+
+[literalparagraph]
+template::[literalblock]
+
+[verseparagraph]
+template::[verseblock]
+
+[quoteparagraph]
+template::[quoteblock]
+
+[listingparagraph]
+template::[listingblock]
+
+[macros]
+#--------------
+# Inline macros
+#--------------
+# Backslash prefix required for escape processing.
+# (?s) re flag for line spanning.
+
+# Macros using default syntax.
+(?su)(?<!\w)[\\]?(?P<name>http|https|ftp|file|irc|mailto|callto|image|link|anchor|xref|indexterm):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+# These URL types don't require any special attribute list formatting.
+(?su)(?<!\S)[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])=
+# Allow a leading parenthesis and square bracket.
+(?su)(?<\=[([])[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])=
+# Allow <> brackets.
+(?su)[\\]?&lt;(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])&gt;=
+
+# Email addresses don't require special attribute list formatting.
+# The before ">: and after "< character exclusions stop multiple substitution.
+(?su)(?<![">:\w._/-])[\\]?(?P<target>\w[\w._-]*@[\w._-]*\w)(?!["<\w_-])=mailto
+
+# Allow footnote macros hard up against the preceding word so the footnote mark
+# can be placed against the noted text without an intervening space
+# (http://groups.google.com/group/asciidoc/browse_frm/thread/e1dcb7ee0efc17b5).
+(?su)[\\]?(?P<name>footnote|footnoteref):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+# Anchor: [[[id]]]. Bibliographic anchor.
+(?su)[\\]?\[\[\[(?P<attrlist>[\w_:][\w_:.-]*?)\]\]\]=anchor3
+# Anchor: [[id,xreflabel]]
+(?su)[\\]?\[\[(?P<attrlist>[\w"_:].*?)\]\]=anchor2
+# Link: <<id,text>>
+(?su)[\\]?&lt;&lt;(?P<attrlist>[\w"_:].*?)&gt;&gt;=xref2
+
+ifdef::asciidoc7compatible[]
+# Index term: ++primary,secondary,tertiary++
+(?su)(?<!\S)[\\]?\+\+(?P<attrlist>[^+].*?)\+\+(?!\+)=indexterm
+# Index term: +primary+
+# Follows ++...++ macro otherwise it will match them.
+(?<!\S)[\\]?\+(?P<attrlist>[^\s\+][^+].*?)\+(?!\+)=indexterm2
+endif::asciidoc7compatible[]
+
+ifndef::asciidoc7compatible[]
+# Index term: (((primary,secondary,tertiary)))
+(?su)(?<!\()[\\]?\(\(\((?P<attrlist>[^(].*?)\)\)\)(?!\))=indexterm
+# Index term: ((primary))
+# Follows (((...))) macro otherwise it will match them.
+(?<!\()[\\]?\(\((?P<attrlist>[^\s\(][^(].*?)\)\)(?!\))=indexterm2
+endif::asciidoc7compatible[]
+
+# Callout
+[\\]?&lt;(?P<index>\d+)&gt;=callout
+
+# Passthrough macros.
+(?su)[\\]?(?P<name>pass):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[]
+
+# Triple-plus and double-dollar inline passthroughs.
+(?su)[\\]?\+\+\+(?P<passtext>.*?)\+\+\+=pass[]
+(?su)[\\]?\$\$(?P<passtext>.*?)\$\$=pass[specialcharacters]
+
+# Inline literal.
+ifndef::no-inline-literal[]
+(?su)(?<![`\w])([\\]?`(?P<passtext>[^`\s]|[^`\s].*?\S)`)(?![`\w])=literal[specialcharacters]
+endif::no-inline-literal[]
+
+# Inline comment.
+(?mu)^[\\]?//(?P<passtext>[^/].*|)$=comment[specialcharacters]
+
+# Default (catchall) inline macro is not implemented so there is no ambiguity
+# with previous definition that could result in double substitution of escaped
+# references.
+#(?su)[\\]?(?P<name>\w(\w|-)*?):(?P<target>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=
+
+#-------------
+# Block macros
+#-------------
+# Macros using default syntax.
+(?u)^(?P<name>image|unfloat)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
+
+# Passthrough macros.
+(?u)^(?P<name>pass)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#
+
+^'{3,}$=#ruler
+^<{3,}$=#pagebreak
+^//(?P<passtext>[^/].*|)$=#comment[specialcharacters]
+
+[unfloat-blockmacro]
+# Implemented in HTML backends.
+
+#-----------------
+# Delimited blocks
+#-----------------
+[blockdef-comment]
+delimiter=^/{4,}$
+options=skip
+
+[blockdef-sidebar]
+delimiter=^\*{4,}$
+template=sidebarblock
+options=sectionbody
+posattrs=style
+# DEPRECATED: Use Openblock instead.
+abstract-style=template="abstractblock"
+
+[blockdef-open]
+# A block without opening or closing tags.
+delimiter=^--$
+template=openblock
+options=sectionbody
+posattrs=style
+abstract-style=template="abstractblock"
+partintro-style=template="partintroblock"
+
+[blockdef-pass]
+delimiter=^\+{4,}$
+template=passblock
+# Default subs choosen for backward compatibility.
+subs=attributes,macros
+posattrs=style
+pass-style=template="passblock",subs=[]
+
+[blockdef-listing]
+delimiter=^-{4,}$
+template=listingblock
+subs=verbatim
+posattrs=style
+
+[blockdef-literal]
+delimiter=^\.{4,}$
+template=literalblock
+subs=verbatim
+posattrs=style
+listing-style=template="listingblock"
+# DEPRECATED: Use verse style on quote blocks instead.
+verse-style=template="verseblock",subs="normal"
+
+[blockdef-quote]
+delimiter=^_{4,}$
+subs=normal
+style=quote
+posattrs=style,attribution,citetitle
+quote-style=template="quoteblock",options=("sectionbody",)
+verse-style=template="verseblock"
+
+[blockdef-example]
+delimiter=^={4,}$
+template=exampleblock
+options=sectionbody
+posattrs=style
+NOTE-style=template="admonitionblock",name="note",caption="{note-caption}"
+TIP-style=template="admonitionblock",name="tip",caption="{tip-caption}"
+IMPORTANT-style=template="admonitionblock",name="important",caption="{important-caption}"
+WARNING-style=template="admonitionblock",name="warning",caption="{warning-caption}"
+CAUTION-style=template="admonitionblock",name="caution",caption="{caution-caption}"
+
+# For use by custom filters.
+# DEPRECATED: No longer used, a styled listing block (blockdef-listing) is preferable.
+[blockdef-filter]
+delimiter=^~{4,}$
+template=listingblock
+subs=none
+posattrs=style
+
+#-------
+# Lists
+#-------
+[listdef-bulleted]
+# - bullets.
+delimiter=^\s*- +(?P<text>.+)$
+posattrs=style
+type=bulleted
+tags=bulleted
+callout-style=tags="callout"
+bibliography-style=tags="bibliography"
+
+[listdef-bulleted1]
+# * bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\* +(?P<text>.+)$
+
+[listdef-bulleted2]
+# ** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{2} +(?P<text>.+)$
+
+[listdef-bulleted3]
+# *** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{3} +(?P<text>.+)$
+
+[listdef-bulleted4]
+# **** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{4} +(?P<text>.+)$
+
+[listdef-bulleted5]
+# ***** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{5} +(?P<text>.+)$
+
+[listdef-arabic]
+# Arabic numbering.
+delimiter=^\s*(?P<index>\d+\.) +(?P<text>.+)$
+posattrs=style
+type=numbered
+tags=numbered
+style=arabic
+
+[listdef-loweralpha]
+# Lower alpha numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[a-z]\.) +(?P<text>.+)$
+style=loweralpha
+
+[listdef-upperalpha]
+# Upper alpha numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[A-Z]\.) +(?P<text>.+)$
+style=upperalpha
+
+[listdef-lowerroman]
+# Lower roman numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[ivx]+\)) +(?P<text>.+)$
+style=lowerroman
+
+[listdef-upperroman]
+# Upper roman numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[IVX]+\)) +(?P<text>.+)$
+style=upperroman
+
+[listdef-numbered1]
+# . numbering.
+template::[listdef-arabic]
+delimiter=^\s*\. +(?P<text>.+)$
+
+[listdef-numbered2]
+# .. numbering.
+template::[listdef-loweralpha]
+delimiter=^\s*\.{2} +(?P<text>.+)$
+
+[listdef-numbered3]
+# ... numbering.
+template::[listdef-lowerroman]
+delimiter=^\s*\.{3} +(?P<text>.+)$
+
+[listdef-numbered4]
+# .... numbering.
+template::[listdef-upperalpha]
+delimiter=^\s*\.{4} +(?P<text>.+)$
+
+[listdef-numbered5]
+# ..... numbering.
+template::[listdef-upperroman]
+delimiter=^\s*\.{5} +(?P<text>.+)$
+
+[listdef-labeled]
+# label:: item.
+delimiter=^\s*(?P<label>.*[^:])::(\s+(?P<text>.+))?$
+posattrs=style
+type=labeled
+tags=labeled
+vertical-style=tags="labeled"
+horizontal-style=tags="horizontal"
+glossary-style=tags="glossary"
+qanda-style=tags="qanda"
+
+[listdef-labeled2]
+# label;; item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^;]);;(\s+(?P<text>.+))?$
+
+[listdef-labeled3]
+# label::: item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^:]):{3}(\s+(?P<text>.+))?$
+
+[listdef-labeled4]
+# label:::: item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^:]):{4}(\s+(?P<text>.+))?$
+
+[listdef-callout]
+posattrs=style
+delimiter=^<?(?P<index>\d*>) +(?P<text>.+)$
+type=callout
+tags=callout
+style=arabic
+
+# DEPRECATED: Old list syntax.
+[listdef-qanda]
+posattrs=style
+delimiter=^\s*(?P<label>.*\S)\?\?$
+type=labeled
+tags=qanda
+
+# DEPRECATED: Old list syntax.
+[listdef-bibliography]
+posattrs=style
+delimiter=^\+ +(?P<text>.+)$
+type=bulleted
+tags=bibliography
+
+# DEPRECATED: Old list syntax.
+[listdef-glossary]
+delimiter=^(?P<label>.*\S):-$
+posattrs=style
+type=labeled
+tags=glossary
+
+#-------
+# Tables
+#-------
+[tabledef-default]
+delimiter=^\|={3,}$
+posattrs=style
+template=table
+default-style=tags="default"
+verse-style=tags="verse"
+literal-style=tags="literal",subs=["specialcharacters"]
+emphasis-style=tags="emphasis"
+strong-style=tags="strong"
+monospaced-style=tags="monospaced"
+header-style=tags="header"
+asciidoc-style=tags="asciidoc",subs=[],filter='python "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"}{icons? -a icons -a "iconsdir={iconsdir}"}{imagesdir? -a "imagesdir={imagesdir}"}{data-uri? -a data-uri} -a "indir={indir}"{trace? -a "trace={trace}"} -s -'
+
+[tabledef-nested]
+# Same as [tabledef-default] but with different delimiter and separator.
+delimiter=^!={3,}$
+separator=((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<style>[a-z])?)?!
+posattrs=style
+template=table
+verse-style=tags="verse"
+literal-style=tags="literal",subs=["specialcharacters"]
+emphasis-style=tags="emphasis"
+strong-style=tags="strong"
+monospaced-style=tags="monospaced"
+header-style=tags="header"
+asciidoc-style=tags="asciidoc",subs=[],filter='python "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"} -s -'
+
+#----------------------------------------
+# Common block and macro markup templates
+#----------------------------------------
+[comment-inlinemacro]
+# Outputs nothing.
+
+[comment-blockmacro]
+# Outputs nothing.
+
+[pass-blockmacro]
+{passtext}
+
+[pass-inlinemacro]
+template::[pass-blockmacro]
+
+[passblock]
+|
+
+[filter-image-blockmacro]
+# Synthesize missing target attribute for filter generated file names.
+# The tag split | ensures missing target file names are auto-generated
+# before the filter is executed, the remainder (the [image-blockmacro])
+# is excuted after the filter to ensure data URI encoding comes after
+# the image is created.
+{target%}{counter2:target-number}
+{target%}{set2:target:{docname}__{target-number}.png}
+|
+template::[image-blockmacro]
+
+#----------------------------------
+# Default special section templates
+#----------------------------------
+[abstract]
+template::[sect1]
+
+[colophon]
+template::[sect1]
+
+[dedication]
+template::[sect1]
+
+[preface]
+template::[sect1]
+
+[appendix]
+template::[sect1]
+
+[glossary]
+template::[sect1]
+
+[bibliography]
+template::[sect1]
+
+[index]
+template::[sect1]
+
+[synopsis]
+template::[sect1]
+
+#--------------------------------------------------------------------
+# Deprecated old table definitions.
+#
+
+[old_tabledef-default]
+fillchar=-
+format=fixed
+
+[old_tabledef-csv]
+fillchar=~
+format=csv
+
+[old_tabledef-dsv]
+fillchar=_
+format=dsv
+
+# End of deprecated old table definitions.
+#--------------------------------------------------------------------
diff --git a/doc/www/asciidoc.py b/doc/www/asciidoc.py
new file mode 100755 (executable)
index 0000000..7846de3
--- /dev/null
@@ -0,0 +1,5902 @@
+#!/usr/bin/env python
+"""
+asciidoc - converts an AsciiDoc text file to HTML or DocBook
+
+Copyright (C) 2002-2010 Stuart Rackham. Free use of this software is granted
+under the terms of the GNU General Public License (GPL).
+"""
+
+import sys, os, re, time, traceback, tempfile, subprocess, codecs, locale, unicodedata
+
+### Used by asciidocapi.py ###
+VERSION = '8.6.5'           # See CHANGLOG file for version history.
+
+MIN_PYTHON_VERSION = 2.4    # Require this version of Python or better.
+
+#---------------------------------------------------------------------------
+# Program constants.
+#---------------------------------------------------------------------------
+DEFAULT_BACKEND = 'html'
+DEFAULT_DOCTYPE = 'article'
+# Allowed substitution options for List, Paragraph and DelimitedBlock
+# definition subs entry.
+SUBS_OPTIONS = ('specialcharacters','quotes','specialwords',
+    'replacements', 'attributes','macros','callouts','normal','verbatim',
+    'none','replacements2')
+# Default value for unspecified subs and presubs configuration file entries.
+SUBS_NORMAL = ('specialcharacters','quotes','attributes',
+    'specialwords','replacements','macros','replacements2')
+SUBS_VERBATIM = ('specialcharacters','callouts')
+
+NAME_RE = r'(?u)[^\W\d][-\w]*'  # Valid section or attribute name.
+OR, AND = ',', '+'              # Attribute list separators.
+
+
+#---------------------------------------------------------------------------
+# Utility functions and classes.
+#---------------------------------------------------------------------------
+
+class EAsciiDoc(Exception): pass
+
+class OrderedDict(dict):
+    """
+    Dictionary ordered by insertion order.
+    Python Cookbook: Ordered Dictionary, Submitter: David Benjamin.
+    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+    """
+    def __init__(self, d=None, **kwargs):
+        self._keys = []
+        if d is None: d = kwargs
+        dict.__init__(self, d)
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+    def __setitem__(self, key, item):
+        dict.__setitem__(self, key, item)
+        if key not in self._keys: self._keys.append(key)
+    def clear(self):
+        dict.clear(self)
+        self._keys = []
+    def copy(self):
+        d = dict.copy(self)
+        d._keys = self._keys[:]
+        return d
+    def items(self):
+        return zip(self._keys, self.values())
+    def keys(self):
+        return self._keys
+    def popitem(self):
+        try:
+            key = self._keys[-1]
+        except IndexError:
+            raise KeyError('dictionary is empty')
+        val = self[key]
+        del self[key]
+        return (key, val)
+    def setdefault(self, key, failobj = None):
+        dict.setdefault(self, key, failobj)
+        if key not in self._keys: self._keys.append(key)
+    def update(self, d=None, **kwargs):
+        if d is None:
+            d = kwargs
+        dict.update(self, d)
+        for key in d.keys():
+            if key not in self._keys: self._keys.append(key)
+    def values(self):
+        return map(self.get, self._keys)
+
+class AttrDict(dict):
+    """
+    Like a dictionary except values can be accessed as attributes i.e. obj.foo
+    can be used in addition to obj['foo'].
+    If an item is not present None is returned.
+    """
+    def __getattr__(self, key):
+        try: return self[key]
+        except KeyError: return None
+    def __setattr__(self, key, value):
+        self[key] = value
+    def __delattr__(self, key):
+        try: del self[key]
+        except KeyError, k: raise AttributeError, k
+    def __repr__(self):
+        return '<AttrDict ' + dict.__repr__(self) + '>'
+    def __getstate__(self):
+        return dict(self)
+    def __setstate__(self,value):
+        for k,v in value.items(): self[k]=v
+
+class InsensitiveDict(dict):
+    """
+    Like a dictionary except key access is case insensitive.
+    Keys are stored in lower case.
+    """
+    def __getitem__(self, key):
+        return dict.__getitem__(self, key.lower())
+    def __setitem__(self, key, value):
+        dict.__setitem__(self, key.lower(), value)
+    def has_key(self, key):
+        return dict.has_key(self,key.lower())
+    def get(self, key, default=None):
+        return dict.get(self, key.lower(), default)
+    def update(self, dict):
+        for k,v in dict.items():
+            self[k] = v
+    def setdefault(self, key, default = None):
+        return dict.setdefault(self, key.lower(), default)
+
+
+class Trace(object):
+    """
+    Used in conjunction with the 'trace' attribute to generate diagnostic
+    output. There is a single global instance of this class named trace.
+    """
+    SUBS_NAMES = ('specialcharacters','quotes','specialwords',
+                  'replacements', 'attributes','macros','callouts',
+                  'replacements2')
+    def __init__(self):
+        self.name_re = ''        # Regexp pattern to match trace names.
+        self.linenos = True
+        self.offset = 0
+    def __call__(self, name, before, after=None):
+        """
+        Print trace message if tracing is on and the trace 'name' matches the
+        document 'trace' attribute (treated as a regexp).
+        'before' is the source text before substitution; 'after' text is the
+        source text after substitutuion.
+        The 'before' and 'after' messages are only printed if they differ.
+        """
+        name_re = document.attributes.get('trace')
+        if name_re == 'subs':    # Alias for all the inline substitutions.
+            name_re = '|'.join(self.SUBS_NAMES)
+        self.name_re = name_re
+        if self.name_re is not None:
+            msg = message.format(name, 'TRACE: ', self.linenos, offset=self.offset)
+            if before != after and re.match(self.name_re,name):
+                if is_array(before):
+                    before = '\n'.join(before)
+                if after is None:
+                    msg += '\n%s\n' % before
+                else:
+                    if is_array(after):
+                        after = '\n'.join(after)
+                    msg += '\n<<<\n%s\n>>>\n%s\n' % (before,after)
+                message.stderr(msg)
+
+class Message:
+    """
+    Message functions.
+    """
+    PROG = os.path.basename(os.path.splitext(__file__)[0])
+
+    def __init__(self):
+        # Set to True or False to globally override line numbers method
+        # argument. Has no effect when set to None.
+        self.linenos = None
+        self.messages = []
+
+    def stdout(self,msg):
+        print msg
+
+    def stderr(self,msg=''):
+        self.messages.append(msg)
+        if __name__ == '__main__':
+            sys.stderr.write('%s: %s%s' % (self.PROG, msg, os.linesep))
+
+    def verbose(self, msg,linenos=True):
+        if config.verbose:
+            msg = self.format(msg,linenos=linenos)
+            self.stderr(msg)
+
+    def warning(self, msg,linenos=True,offset=0):
+        msg = self.format(msg,'WARNING: ',linenos,offset=offset)
+        document.has_warnings = True
+        self.stderr(msg)
+
+    def deprecated(self, msg, linenos=True):
+        msg = self.format(msg, 'DEPRECATED: ', linenos)
+        self.stderr(msg)
+
+    def format(self, msg, prefix='', linenos=True, cursor=None, offset=0):
+        """Return formatted message string."""
+        if self.linenos is not False and ((linenos or self.linenos) and reader.cursor):
+            if cursor is None:
+                cursor = reader.cursor
+            prefix += '%s: line %d: ' % (os.path.basename(cursor[0]),cursor[1]+offset)
+        return prefix + msg
+
+    def error(self, msg, cursor=None, halt=False):
+        """
+        Report fatal error.
+        If halt=True raise EAsciiDoc exception.
+        If halt=False don't exit application, continue in the hope of reporting
+        all fatal errors finishing with a non-zero exit code.
+        """
+        if halt:
+            raise EAsciiDoc, self.format(msg,linenos=False,cursor=cursor)
+        else:
+            msg = self.format(msg,'ERROR: ',cursor=cursor)
+            self.stderr(msg)
+            document.has_errors = True
+
+    def unsafe(self, msg):
+        self.error('unsafe: '+msg)
+
+
+def userdir():
+    """
+    Return user's home directory or None if it is not defined.
+    """
+    result = os.path.expanduser('~')
+    if result == '~':
+        result = None
+    return result
+
+def localapp():
+    """
+    Return True if we are not executing the system wide version
+    i.e. the configuration is in the executable's directory.
+    """
+    return os.path.isfile(os.path.join(APP_DIR, 'asciidoc.conf'))
+
+def file_in(fname, directory):
+    """Return True if file fname resides inside directory."""
+    assert os.path.isfile(fname)
+    # Empty directory (not to be confused with None) is the current directory.
+    if directory == '':
+        directory = os.getcwd()
+    else:
+        assert os.path.isdir(directory)
+        directory = os.path.realpath(directory)
+    fname = os.path.realpath(fname)
+    return os.path.commonprefix((directory, fname)) == directory
+
+def safe():
+    return document.safe
+
+def is_safe_file(fname, directory=None):
+    # A safe file must reside in directory directory (defaults to the source
+    # file directory).
+    if directory is None:
+        if document.infile == '<stdin>':
+           return not safe()
+        directory = os.path.dirname(document.infile)
+    elif directory == '':
+        directory = '.'
+    return (
+        not safe()
+        or file_in(fname, directory)
+        or file_in(fname, APP_DIR)
+        or file_in(fname, CONF_DIR)
+    )
+
+def safe_filename(fname, parentdir):
+    """
+    Return file name which must reside in the parent file directory.
+    Return None if file is not found or not safe.
+    """
+    if not os.path.isabs(fname):
+        # Include files are relative to parent document
+        # directory.
+        fname = os.path.normpath(os.path.join(parentdir,fname))
+    if not os.path.isfile(fname):
+        message.warning('include file not found: %s' % fname)
+        return None
+    if not is_safe_file(fname, parentdir):
+        message.unsafe('include file: %s' % fname)
+        return None
+    return fname
+
+def assign(dst,src):
+    """Assign all attributes from 'src' object to 'dst' object."""
+    for a,v in src.__dict__.items():
+        setattr(dst,a,v)
+
+def strip_quotes(s):
+    """Trim white space and, if necessary, quote characters from s."""
+    s = s.strip()
+    # Strip quotation mark characters from quoted strings.
+    if len(s) >= 3 and s[0] == '"' and s[-1] == '"':
+        s = s[1:-1]
+    return s
+
+def is_re(s):
+    """Return True if s is a valid regular expression else return False."""
+    try: re.compile(s)
+    except: return False
+    else: return True
+
+def re_join(relist):
+    """Join list of regular expressions re1,re2,... to single regular
+    expression (re1)|(re2)|..."""
+    if len(relist) == 0:
+        return None
+    result = []
+    # Delete named groups to avoid ambiguity.
+    for s in relist:
+        result.append(re.sub(r'\?P<\S+?>','',s))
+    result = ')|('.join(result)
+    result = '('+result+')'
+    return result
+
+def validate(value,rule,errmsg):
+    """Validate value against rule expression. Throw EAsciiDoc exception with
+    errmsg if validation fails."""
+    try:
+        if not eval(rule.replace('$',str(value))):
+            raise EAsciiDoc,errmsg
+    except Exception:
+        raise EAsciiDoc,errmsg
+    return value
+
+def lstrip_list(s):
+    """
+    Return list with empty items from start of list removed.
+    """
+    for i in range(len(s)):
+        if s[i]: break
+    else:
+        return []
+    return s[i:]
+
+def rstrip_list(s):
+    """
+    Return list with empty items from end of list removed.
+    """
+    for i in range(len(s)-1,-1,-1):
+        if s[i]: break
+    else:
+        return []
+    return s[:i+1]
+
+def strip_list(s):
+    """
+    Return list with empty items from start and end of list removed.
+    """
+    s = lstrip_list(s)
+    s = rstrip_list(s)
+    return s
+
+def is_array(obj):
+    """
+    Return True if object is list or tuple type.
+    """
+    return isinstance(obj,list) or isinstance(obj,tuple)
+
+def dovetail(lines1, lines2):
+    """
+    Append list or tuple of strings 'lines2' to list 'lines1'.  Join the last
+    non-blank item in 'lines1' with the first non-blank item in 'lines2' into a
+    single string.
+    """
+    assert is_array(lines1)
+    assert is_array(lines2)
+    lines1 = strip_list(lines1)
+    lines2 = strip_list(lines2)
+    if not lines1 or not lines2:
+        return list(lines1) + list(lines2)
+    result = list(lines1[:-1])
+    result.append(lines1[-1] + lines2[0])
+    result += list(lines2[1:])
+    return result
+
+def dovetail_tags(stag,content,etag):
+    """Merge the end tag with the first content line and the last
+    content line with the end tag. This ensures verbatim elements don't
+    include extraneous opening and closing line breaks."""
+    return dovetail(dovetail(stag,content), etag)
+
+def parse_attributes(attrs,dict):
+    """Update a dictionary with name/value attributes from the attrs string.
+    The attrs string is a comma separated list of values and keyword name=value
+    pairs. Values must preceed keywords and are named '1','2'... The entire
+    attributes list is named '0'. If keywords are specified string values must
+    be quoted. Examples:
+
+    attrs: ''
+    dict: {}
+
+    attrs: 'hello,world'
+    dict: {'2': 'world', '0': 'hello,world', '1': 'hello'}
+
+    attrs: '"hello", planet="earth"'
+    dict: {'planet': 'earth', '0': '"hello",planet="earth"', '1': 'hello'}
+    """
+    def f(*args,**keywords):
+        # Name and add aguments '1','2'... to keywords.
+        for i in range(len(args)):
+            if not str(i+1) in keywords:
+                keywords[str(i+1)] = args[i]
+        return keywords
+
+    if not attrs:
+        return
+    dict['0'] = attrs
+    # Replace line separators with spaces so line spanning works.
+    s = re.sub(r'\s', ' ', attrs)
+    try:
+        d = eval('f('+s+')')
+        # Attributes must evaluate to strings, numbers or None.
+        for v in d.values():
+            if not (isinstance(v,str) or isinstance(v,int) or isinstance(v,float) or v is None):
+                raise Exception
+    except Exception:
+        s = s.replace('"','\\"')
+        s = s.split(',')
+        s = map(lambda x: '"' + x.strip() + '"', s)
+        s = ','.join(s)
+        try:
+            d = eval('f('+s+')')
+        except Exception:
+            return  # If there's a syntax error leave with {0}=attrs.
+        for k in d.keys():  # Drop any empty positional arguments.
+            if d[k] == '': del d[k]
+    dict.update(d)
+    assert len(d) > 0
+
+def parse_named_attributes(s,attrs):
+    """Update a attrs dictionary with name="value" attributes from the s string.
+    Returns False if invalid syntax.
+    Example:
+    attrs: 'star="sun",planet="earth"'
+    dict: {'planet':'earth', 'star':'sun'}
+    """
+    def f(**keywords): return keywords
+
+    try:
+        d = eval('f('+s+')')
+        attrs.update(d)
+        return True
+    except Exception:
+        return False
+
+def parse_list(s):
+    """Parse comma separated string of Python literals. Return a tuple of of
+    parsed values."""
+    try:
+        result = eval('tuple(['+s+'])')
+    except Exception:
+        raise EAsciiDoc,'malformed list: '+s
+    return result
+
+def parse_options(options,allowed,errmsg):
+    """Parse comma separated string of unquoted option names and return as a
+    tuple of valid options. 'allowed' is a list of allowed option values.
+    If allowed=() then all legitimate names are allowed.
+    'errmsg' is an error message prefix if an illegal option error is thrown."""
+    result = []
+    if options:
+        for s in re.split(r'\s*,\s*',options):
+            if (allowed and s not in allowed) or not is_name(s):
+                raise EAsciiDoc,'%s: %s' % (errmsg,s)
+            result.append(s)
+    return tuple(result)
+
+def symbolize(s):
+    """Drop non-symbol characters and convert to lowercase."""
+    return re.sub(r'(?u)[^\w\-_]', '', s).lower()
+
+def is_name(s):
+    """Return True if s is valid attribute, macro or tag name
+    (starts with alpha containing alphanumeric and dashes only)."""
+    return re.match(r'^'+NAME_RE+r'$',s) is not None
+
+def subs_quotes(text):
+    """Quoted text is marked up and the resulting text is
+    returned."""
+    keys = config.quotes.keys()
+    for q in keys:
+        i = q.find('|')
+        if i != -1 and q != '|' and q != '||':
+            lq = q[:i]      # Left quote.
+            rq = q[i+1:]    # Right quote.
+        else:
+            lq = rq = q
+        tag = config.quotes[q]
+        if not tag: continue
+        # Unconstrained quotes prefix the tag name with a hash.
+        if tag[0] == '#':
+            tag = tag[1:]
+            # Unconstrained quotes can appear anywhere.
+            reo = re.compile(r'(?msu)(^|.)(\[(?P<attrlist>[^[\]]+?)\])?' \
+                    + r'(?:' + re.escape(lq) + r')' \
+                    + r'(?P<content>.+?)(?:'+re.escape(rq)+r')')
+        else:
+            # The text within constrained quotes must be bounded by white space.
+            # Non-word (\W) characters are allowed at boundaries to accomodate
+            # enveloping quotes and punctuation e.g. a='x', ('x'), 'x', ['x'].
+            reo = re.compile(r'(?msu)(^|[^\w;:}])(\[(?P<attrlist>[^[\]]+?)\])?' \
+                + r'(?:' + re.escape(lq) + r')' \
+                + r'(?P<content>\S|\S.*?\S)(?:'+re.escape(rq)+r')(?=\W|$)')
+        pos = 0
+        while True:
+            mo = reo.search(text,pos)
+            if not mo: break
+            if text[mo.start()] == '\\':
+                # Delete leading backslash.
+                text = text[:mo.start()] + text[mo.start()+1:]
+                # Skip past start of match.
+                pos = mo.start() + 1
+            else:
+                attrlist = {}
+                parse_attributes(mo.group('attrlist'), attrlist)
+                stag,etag = config.tag(tag, attrlist)
+                s = mo.group(1) + stag + mo.group('content') + etag
+                text = text[:mo.start()] + s + text[mo.end():]
+                pos = mo.start() + len(s)
+    return text
+
+def subs_tag(tag,dict={}):
+    """Perform attribute substitution and split tag string returning start, end
+    tag tuple (c.f. Config.tag())."""
+    if not tag:
+        return [None,None]
+    s = subs_attrs(tag,dict)
+    if not s:
+        message.warning('tag \'%s\' dropped: contains undefined attribute' % tag)
+        return [None,None]
+    result = s.split('|')
+    if len(result) == 1:
+        return result+[None]
+    elif len(result) == 2:
+        return result
+    else:
+        raise EAsciiDoc,'malformed tag: %s' % tag
+
+def parse_entry(entry, dict=None, unquote=False, unique_values=False,
+        allow_name_only=False, escape_delimiter=True):
+    """Parse name=value entry to dictionary 'dict'. Return tuple (name,value)
+    or None if illegal entry.
+    If name= then value is set to ''.
+    If name and allow_name_only=True then value is set to ''.
+    If name! and allow_name_only=True then value is set to None.
+    Leading and trailing white space is striped from 'name' and 'value'.
+    'name' can contain any printable characters.
+    If the '=' delimiter character is allowed in  the 'name' then
+    it must be escaped with a backslash and escape_delimiter must be True.
+    If 'unquote' is True leading and trailing double-quotes are stripped from
+    'name' and 'value'.
+    If unique_values' is True then dictionary entries with the same value are
+    removed before the parsed entry is added."""
+    if escape_delimiter:
+        mo = re.search(r'(?:[^\\](=))',entry)
+    else:
+        mo = re.search(r'(=)',entry)
+    if mo:  # name=value entry.
+        if mo.group(1):
+            name = entry[:mo.start(1)]
+            if escape_delimiter:
+                name = name.replace(r'\=','=')  # Unescape \= in name.
+            value = entry[mo.end(1):]
+    elif allow_name_only and entry:         # name or name! entry.
+        name = entry
+        if name[-1] == '!':
+            name = name[:-1]
+            value = None
+        else:
+            value = ''
+    else:
+        return None
+    if unquote:
+        name = strip_quotes(name)
+        if value is not None:
+            value = strip_quotes(value)
+    else:
+        name = name.strip()
+        if value is not None:
+            value = value.strip()
+    if not name:
+        return None
+    if dict is not None:
+        if unique_values:
+            for k,v in dict.items():
+                if v == value: del dict[k]
+        dict[name] = value
+    return name,value
+
+def parse_entries(entries, dict, unquote=False, unique_values=False,
+        allow_name_only=False,escape_delimiter=True):
+    """Parse name=value entries from  from lines of text in 'entries' into
+    dictionary 'dict'. Blank lines are skipped."""
+    entries = config.expand_templates(entries)
+    for entry in entries:
+        if entry and not parse_entry(entry, dict, unquote, unique_values,
+                allow_name_only, escape_delimiter):
+            raise EAsciiDoc,'malformed section entry: %s' % entry
+
+def dump_section(name,dict,f=sys.stdout):
+    """Write parameters in 'dict' as in configuration file section format with
+    section 'name'."""
+    f.write('[%s]%s' % (name,writer.newline))
+    for k,v in dict.items():
+        k = str(k)
+        k = k.replace('=',r'\=')    # Escape = in name.
+        # Quote if necessary.
+        if len(k) != len(k.strip()):
+            k = '"'+k+'"'
+        if v and len(v) != len(v.strip()):
+            v = '"'+v+'"'
+        if v is None:
+            # Don't dump undefined attributes.
+            continue
+        else:
+            s = k+'='+v
+        if s[0] == '#':
+            s = '\\' + s    # Escape so not treated as comment lines.
+        f.write('%s%s' % (s,writer.newline))
+    f.write(writer.newline)
+
+def update_attrs(attrs,dict):
+    """Update 'attrs' dictionary with parsed attributes in dictionary 'dict'."""
+    for k,v in dict.items():
+        if not is_name(k):
+            raise EAsciiDoc,'illegal attribute name: %s' % k
+        attrs[k] = v
+
+def is_attr_defined(attrs,dic):
+    """
+    Check if the sequence of attributes is defined in dictionary 'dic'.
+    Valid 'attrs' sequence syntax:
+    <attr> Return True if single attrbiute is defined.
+    <attr1>,<attr2>,... Return True if one or more attributes are defined.
+    <attr1>+<attr2>+... Return True if all the attributes are defined.
+    """
+    if OR in attrs:
+        for a in attrs.split(OR):
+            if dic.get(a.strip()) is not None:
+                return True
+        else: return False
+    elif AND in attrs:
+        for a in attrs.split(AND):
+            if dic.get(a.strip()) is None:
+                return False
+        else: return True
+    else:
+        return dic.get(attrs.strip()) is not None
+
+def filter_lines(filter_cmd, lines, attrs={}):
+    """
+    Run 'lines' through the 'filter_cmd' shell command and return the result.
+    The 'attrs' dictionary contains additional filter attributes.
+    """
+    def findfilter(name,dir,filter):
+        """Find filter file 'fname' with style name 'name' in directory
+        'dir'. Return found file path or None if not found."""
+        if name:
+            result = os.path.join(dir,'filters',name,filter)
+            if os.path.isfile(result):
+                return result
+        result = os.path.join(dir,'filters',filter)
+        if os.path.isfile(result):
+            return result
+        return None
+
+    # Return input lines if there's not filter.
+    if not filter_cmd or not filter_cmd.strip():
+        return lines
+    # Perform attributes substitution on the filter command.
+    s = subs_attrs(filter_cmd, attrs)
+    if not s:
+        message.error('undefined filter attribute in command: %s' % filter_cmd)
+        return []
+    filter_cmd = s.strip()
+    # Parse for quoted and unquoted command and command tail.
+    # Double quoted.
+    mo = re.match(r'^"(?P<cmd>[^"]+)"(?P<tail>.*)$', filter_cmd)
+    if not mo:
+        # Single quoted.
+        mo = re.match(r"^'(?P<cmd>[^']+)'(?P<tail>.*)$", filter_cmd)
+        if not mo:
+            # Unquoted catch all.
+            mo = re.match(r'^(?P<cmd>\S+)(?P<tail>.*)$', filter_cmd)
+    cmd = mo.group('cmd').strip()
+    found = None
+    if not os.path.dirname(cmd):
+        # Filter command has no directory path so search filter directories.
+        filtername = attrs.get('style')
+        d = document.attributes.get('docdir')
+        if d:
+            found = findfilter(filtername, d, cmd)
+        if not found:
+            if USER_DIR:
+                found = findfilter(filtername, USER_DIR, cmd)
+            if not found:
+                if localapp():
+                    found = findfilter(filtername, APP_DIR, cmd)
+                else:
+                    found = findfilter(filtername, CONF_DIR, cmd)
+    else:
+        if os.path.isfile(cmd):
+            found = cmd
+        else:
+            message.warning('filter not found: %s' % cmd)
+    if found:
+        filter_cmd = '"' + found + '"' + mo.group('tail')
+    if sys.platform == 'win32':
+        # Windows doesn't like running scripts directly so explicitly
+        # specify interpreter.
+        if found:
+            if cmd.endswith('.py'):
+                filter_cmd = 'python ' + filter_cmd
+            elif cmd.endswith('.rb'):
+                filter_cmd = 'ruby ' + filter_cmd
+    message.verbose('filtering: ' + filter_cmd)
+    try:
+        p = subprocess.Popen(filter_cmd, shell=True,
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        output = p.communicate(os.linesep.join(lines))[0]
+    except Exception:
+        raise EAsciiDoc,'filter error: %s: %s' % (filter_cmd, sys.exc_info()[1])
+    if output:
+        result = [s.rstrip() for s in output.split(os.linesep)]
+    else:
+        result = []
+    filter_status = p.wait()
+    if filter_status:
+        message.warning('filter non-zero exit code: %s: returned %d' %
+               (filter_cmd, filter_status))
+    if lines and not result:
+        message.warning('no output from filter: %s' % filter_cmd)
+    return result
+
+def system(name, args, is_macro=False, attrs=None):
+    """
+    Evaluate a system attribute ({name:args}) or system block macro
+    (name::[args]).
+    If is_macro is True then we are processing a system block macro otherwise
+    it's a system attribute.
+    The attrs dictionary is updated by the counter and set system attributes.
+    NOTE: The include1 attribute is used internally by the include1::[] macro
+    and is not for public use.
+    """
+    if is_macro:
+        syntax = '%s::[%s]' % (name,args)
+        separator = '\n'
+    else:
+        syntax = '{%s:%s}' % (name,args)
+        separator = writer.newline
+    if name not in ('eval','eval3','sys','sys2','sys3','include','include1','counter','counter2','set','set2','template'):
+        if is_macro:
+            msg = 'illegal system macro name: %s' % name
+        else:
+            msg = 'illegal system attribute name: %s' % name
+        message.warning(msg)
+        return None
+    if is_macro:
+        s = subs_attrs(args)
+        if s is None:
+            message.warning('skipped %s: undefined attribute in: %s' % (name,args))
+            return None
+        args = s
+    if name != 'include1':
+        message.verbose('evaluating: %s' % syntax)
+    if safe() and name not in ('include','include1'):
+        message.unsafe(syntax)
+        return None
+    result = None
+    if name in ('eval','eval3'):
+        try:
+            result = eval(args)
+            if result is True:
+                result = ''
+            elif result is False:
+                result = None
+            elif result is not None:
+                result = str(result)
+        except Exception:
+            message.warning('%s: evaluation error' % syntax)
+    elif name in ('sys','sys2','sys3'):
+        result = ''
+        fd,tmp = tempfile.mkstemp()
+        os.close(fd)
+        try:
+            cmd = args
+            cmd = cmd + (' > %s' % tmp)
+            if name == 'sys2':
+                cmd = cmd + ' 2>&1'
+            if os.system(cmd):
+                message.warning('%s: non-zero exit status' % syntax)
+            try:
+                if os.path.isfile(tmp):
+                    lines = [s.rstrip() for s in open(tmp)]
+                else:
+                    lines = []
+            except Exception:
+                raise EAsciiDoc,'%s: temp file read error' % syntax
+            result = separator.join(lines)
+        finally:
+            if os.path.isfile(tmp):
+                os.remove(tmp)
+    elif name in ('counter','counter2'):
+        mo = re.match(r'^(?P<attr>[^:]*?)(:(?P<seed>.*))?$', args)
+        attr = mo.group('attr')
+        seed = mo.group('seed')
+        if seed and (not re.match(r'^\d+$', seed) and len(seed) > 1):
+            message.warning('%s: illegal counter seed: %s' % (syntax,seed))
+            return None
+        if not is_name(attr):
+            message.warning('%s: illegal attribute name' % syntax)
+            return None
+        value = document.attributes.get(attr)
+        if value:
+            if not re.match(r'^\d+$', value) and len(value) > 1:
+                message.warning('%s: illegal counter value: %s'
+                                % (syntax,value))
+                return None
+            if re.match(r'^\d+$', value):
+                expr = value + '+1'
+            else:
+                expr = 'chr(ord("%s")+1)' % value
+            try:
+                result = str(eval(expr))
+            except Exception:
+                message.warning('%s: evaluation error: %s' % (syntax, expr))
+        else:
+            if seed:
+                result = seed
+            else:
+                result = '1'
+        document.attributes[attr] = result
+        if attrs is not None:
+            attrs[attr] = result
+        if name == 'counter2':
+            result = ''
+    elif name in ('set','set2'):
+        mo = re.match(r'^(?P<attr>[^:]*?)(:(?P<value>.*))?$', args)
+        attr = mo.group('attr')
+        value = mo.group('value')
+        if value is None:
+            value = ''
+        if attr.endswith('!'):
+            attr = attr[:-1]
+            value = None
+        if not is_name(attr):
+            message.warning('%s: illegal attribute name' % syntax)
+        else:
+            if attrs is not None:
+                attrs[attr] = value
+            if name != 'set2':  # set2 only updates local attributes.
+                document.attributes[attr] = value
+        if value is None:
+            result = None
+        else:
+            result = ''
+    elif name == 'include':
+        if not os.path.exists(args):
+            message.warning('%s: file does not exist' % syntax)
+        elif not is_safe_file(args):
+            message.unsafe(syntax)
+        else:
+            result = [s.rstrip() for s in open(args)]
+            if result:
+                result = subs_attrs(result)
+                result = separator.join(result)
+                result = result.expandtabs(reader.tabsize)
+            else:
+                result = ''
+    elif name == 'include1':
+        result = separator.join(config.include1[args])
+    elif name == 'template':
+        if not args in config.sections:
+            message.warning('%s: template does not exist' % syntax)
+        else:
+            result = []
+            for line in  config.sections[args]:
+                line = subs_attrs(line)
+                if line is not None:
+                    result.append(line)
+            result = '\n'.join(result)
+    else:
+        assert False
+    if result and name in ('eval3','sys3'):
+        macros.passthroughs.append(result)
+        result = '\x07' + str(len(macros.passthroughs)-1) + '\x07'
+    return result
+
+def subs_attrs(lines, dictionary=None):
+    """Substitute 'lines' of text with attributes from the global
+    document.attributes dictionary and from 'dictionary' ('dictionary'
+    entries take precedence). Return a tuple of the substituted lines.  'lines'
+    containing undefined attributes are deleted. If 'lines' is a string then
+    return a string.
+
+    - Attribute references are substituted in the following order: simple,
+      conditional, system.
+    - Attribute references inside 'dictionary' entry values are substituted.
+    """
+
+    def end_brace(text,start):
+        """Return index following end brace that matches brace at start in
+        text."""
+        assert text[start] == '{'
+        n = 0
+        result = start
+        for c in text[start:]:
+            # Skip braces that are followed by a backslash.
+            if result == len(text)-1 or text[result+1] != '\\':
+                if c == '{': n = n + 1
+                elif c == '}': n = n - 1
+            result = result + 1
+            if n == 0: break
+        return result
+
+    if type(lines) == str:
+        string_result = True
+        lines = [lines]
+    else:
+        string_result = False
+    if dictionary is None:
+        attrs = document.attributes
+    else:
+        # Remove numbered document attributes so they don't clash with
+        # attribute list positional attributes.
+        attrs = {}
+        for k,v in document.attributes.items():
+            if not re.match(r'^\d+$', k):
+                attrs[k] = v
+        # Substitute attribute references inside dictionary values.
+        for k,v in dictionary.items():
+            if v is None:
+                del dictionary[k]
+            else:
+                v = subs_attrs(str(v))
+                if v is None:
+                    del dictionary[k]
+                else:
+                    dictionary[k] = v
+        attrs.update(dictionary)
+    # Substitute all attributes in all lines.
+    result = []
+    for line in lines:
+        # Make it easier for regular expressions.
+        line = line.replace('\\{','{\\')
+        line = line.replace('\\}','}\\')
+        # Expand simple attributes ({name}).
+        # Nested attributes not allowed.
+        reo = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w]*?)\}(?!\\)')
+        pos = 0
+        while True:
+            mo = reo.search(line,pos)
+            if not mo: break
+            s =  attrs.get(mo.group('name'))
+            if s is None:
+                pos = mo.end()
+            else:
+                s = str(s)
+                line = line[:mo.start()] + s + line[mo.end():]
+                pos = mo.start() + len(s)
+        # Expand conditional attributes.
+        # Single name -- higher precedence.
+        reo1 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w]*?)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<value>.*?)\}(?!\\)')
+        # Multiple names (n1,n2,... or n1+n2+...) -- lower precedence.
+        reo2 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w'+OR+AND+r']*?)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<value>.*?)\}(?!\\)')
+        for reo in [reo1,reo2]:
+            pos = 0
+            while True:
+                mo = reo.search(line,pos)
+                if not mo: break
+                attr = mo.group()
+                name =  mo.group('name')
+                if reo == reo2:
+                    if OR in name:
+                        sep = OR
+                    else:
+                        sep = AND
+                    names = [s.strip() for s in name.split(sep) if s.strip() ]
+                    for n in names:
+                        if not re.match(r'^[^\\\W][-\w]*$',n):
+                            message.error('illegal attribute syntax: %s' % attr)
+                    if sep == OR:
+                        # Process OR name expression: n1,n2,...
+                        for n in names:
+                            if attrs.get(n) is not None:
+                                lval = ''
+                                break
+                        else:
+                            lval = None
+                    else:
+                        # Process AND name expression: n1+n2+...
+                        for n in names:
+                            if attrs.get(n) is None:
+                                lval = None
+                                break
+                        else:
+                            lval = ''
+                else:
+                    lval =  attrs.get(name)
+                op = mo.group('op')
+                # mo.end() not good enough because '{x={y}}' matches '{x={y}'.
+                end = end_brace(line,mo.start())
+                rval = line[mo.start('value'):end-1]
+                UNDEFINED = '{zzzzz}'
+                if lval is None:
+                    if op == '=': s = rval
+                    elif op == '?': s = ''
+                    elif op == '!': s = rval
+                    elif op == '#': s = UNDEFINED   # So the line is dropped.
+                    elif op == '%': s = rval
+                    elif op in ('@','$'):
+                        s = UNDEFINED               # So the line is dropped.
+                    else:
+                        assert False, 'illegal attribute: %s' % attr
+                else:
+                    if op == '=': s = lval
+                    elif op == '?': s = rval
+                    elif op == '!': s = ''
+                    elif op == '#': s = rval
+                    elif op == '%': s = UNDEFINED   # So the line is dropped.
+                    elif op in ('@','$'):
+                        v = re.split(r'(?<!\\):',rval)
+                        if len(v) not in (2,3):
+                            message.error('illegal attribute syntax: %s' % attr)
+                            s = ''
+                        elif not is_re('^'+v[0]+'$'):
+                            message.error('illegal attribute regexp: %s' % attr)
+                            s = ''
+                        else:
+                            v = [s.replace('\\:',':') for s in v]
+                            re_mo = re.match('^'+v[0]+'$',lval)
+                            if op == '@':
+                                if re_mo:
+                                    s = v[1]         # {<name>@<re>:<v1>[:<v2>]}
+                                else:
+                                    if len(v) == 3:   # {<name>@<re>:<v1>:<v2>}
+                                        s = v[2]
+                                    else:             # {<name>@<re>:<v1>}
+                                        s = ''
+                            else:
+                                if re_mo:
+                                    if len(v) == 2:   # {<name>$<re>:<v1>}
+                                        s = v[1]
+                                    elif v[1] == '':  # {<name>$<re>::<v2>}
+                                        s = UNDEFINED # So the line is dropped.
+                                    else:             # {<name>$<re>:<v1>:<v2>}
+                                        s = v[1]
+                                else:
+                                    if len(v) == 2:   # {<name>$<re>:<v1>}
+                                        s = UNDEFINED # So the line is dropped.
+                                    else:             # {<name>$<re>:<v1>:<v2>}
+                                        s = v[2]
+                    else:
+                        assert False, 'illegal attribute: %s' % attr
+                s = str(s)
+                line = line[:mo.start()] + s + line[end:]
+                pos = mo.start() + len(s)
+        # Drop line if it contains  unsubstituted {name} references.
+        skipped = re.search(r'(?su)\{[^\\\W][-\w]*?\}(?!\\)', line)
+        if skipped:
+            trace('dropped line', line)
+            continue;
+        # Expand system attributes (eval has precedence).
+        reos = [
+            re.compile(r'(?su)\{(?P<action>eval):(?P<expr>.*?)\}(?!\\)'),
+            re.compile(r'(?su)\{(?P<action>[^\\\W][-\w]*?):(?P<expr>.*?)\}(?!\\)'),
+        ]
+        skipped = False
+        for reo in reos:
+            pos = 0
+            while True:
+                mo = reo.search(line,pos)
+                if not mo: break
+                expr = mo.group('expr')
+                action = mo.group('action')
+                expr = expr.replace('{\\','{')
+                expr = expr.replace('}\\','}')
+                s = system(action, expr, attrs=dictionary)
+                if dictionary is not None and action in ('counter','counter2','set','set2'):
+                    # These actions create and update attributes.
+                    attrs.update(dictionary)
+                if s is None:
+                    # Drop line if the action returns None.
+                    skipped = True
+                    break
+                line = line[:mo.start()] + s + line[mo.end():]
+                pos = mo.start() + len(s)
+            if skipped:
+                break
+        if not skipped:
+            # Remove backslash from escaped entries.
+            line = line.replace('{\\','{')
+            line = line.replace('}\\','}')
+            result.append(line)
+    if string_result:
+        if result:
+            return '\n'.join(result)
+        else:
+            return None
+    else:
+        return tuple(result)
+
+def char_encoding():
+    encoding = document.attributes.get('encoding')
+    if encoding:
+        try:
+            codecs.lookup(encoding)
+        except LookupError,e:
+            raise EAsciiDoc,str(e)
+    return encoding
+
+def char_len(s):
+    return len(char_decode(s))
+
+east_asian_widths = {'W': 2,   # Wide
+                     'F': 2,   # Full-width (wide)
+                     'Na': 1,  # Narrow
+                     'H': 1,   # Half-width (narrow)
+                     'N': 1,   # Neutral (not East Asian, treated as narrow)
+                     'A': 1}   # Ambiguous (s/b wide in East Asian context,
+                               # narrow otherwise, but that doesn't work)
+"""Mapping of result codes from `unicodedata.east_asian_width()` to character
+column widths."""
+
+def column_width(s):
+    text = char_decode(s)
+    if isinstance(text, unicode):
+        width = 0
+        for c in text:
+            width += east_asian_widths[unicodedata.east_asian_width(c)]
+        return width
+    else:
+        return len(text)
+
+def char_decode(s):
+    if char_encoding():
+        try:
+            return s.decode(char_encoding())
+        except Exception:
+            raise EAsciiDoc, \
+                "'%s' codec can't decode \"%s\"" % (char_encoding(), s)
+    else:
+        return s
+
+def char_encode(s):
+    if char_encoding():
+        return s.encode(char_encoding())
+    else:
+        return s
+
+def time_str(t):
+    """Convert seconds since the Epoch to formatted local time string."""
+    t = time.localtime(t)
+    s = time.strftime('%H:%M:%S',t)
+    if time.daylight and t.tm_isdst == 1:
+        result = s + ' ' + time.tzname[1]
+    else:
+        result = s + ' ' + time.tzname[0]
+    # Attempt to convert the localtime to the output encoding.
+    try:
+        result = char_encode(result.decode(locale.getdefaultlocale()[1]))
+    except Exception:
+        pass
+    return result
+
+def date_str(t):
+    """Convert seconds since the Epoch to formatted local date string."""
+    t = time.localtime(t)
+    return time.strftime('%Y-%m-%d',t)
+
+
+class Lex:
+    """Lexical analysis routines. Static methods and attributes only."""
+    prev_element = None
+    prev_cursor = None
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def next():
+        """Returns class of next element on the input (None if EOF).  The
+        reader is assumed to be at the first line following a previous element,
+        end of file or line one.  Exits with the reader pointing to the first
+        line of the next element or EOF (leading blank lines are skipped)."""
+        reader.skip_blank_lines()
+        if reader.eof(): return None
+        # Optimization: If we've already checked for an element at this
+        # position return the element.
+        if Lex.prev_element and Lex.prev_cursor == reader.cursor:
+            return Lex.prev_element
+        if AttributeEntry.isnext():
+            result = AttributeEntry
+        elif AttributeList.isnext():
+            result = AttributeList
+        elif BlockTitle.isnext() and not tables_OLD.isnext():
+            result = BlockTitle
+        elif Title.isnext():
+            if AttributeList.style() == 'float':
+                result = FloatingTitle
+            else:
+                result = Title
+        elif macros.isnext():
+            result = macros.current
+        elif lists.isnext():
+            result = lists.current
+        elif blocks.isnext():
+            result = blocks.current
+        elif tables_OLD.isnext():
+            result = tables_OLD.current
+        elif tables.isnext():
+            result = tables.current
+        else:
+            if not paragraphs.isnext():
+                raise EAsciiDoc,'paragraph expected'
+            result = paragraphs.current
+        # Optimization: Cache answer.
+        Lex.prev_cursor = reader.cursor
+        Lex.prev_element = result
+        return result
+
+    @staticmethod
+    def canonical_subs(options):
+        """Translate composite subs values."""
+        if len(options) == 1:
+            if options[0] == 'none':
+                options = ()
+            elif options[0] == 'normal':
+                options = config.subsnormal
+            elif options[0] == 'verbatim':
+                options = config.subsverbatim
+        return options
+
+    @staticmethod
+    def subs_1(s,options):
+        """Perform substitution specified in 'options' (in 'options' order)."""
+        if not s:
+            return s
+        if document.attributes.get('plaintext') is not None:
+            options = ('specialcharacters',)
+        result = s
+        options = Lex.canonical_subs(options)
+        for o in options:
+            if o == 'specialcharacters':
+                result = config.subs_specialchars(result)
+            elif o == 'attributes':
+                result = subs_attrs(result)
+            elif o == 'quotes':
+                result = subs_quotes(result)
+            elif o == 'specialwords':
+                result = config.subs_specialwords(result)
+            elif o in ('replacements','replacements2'):
+                result = config.subs_replacements(result,o)
+            elif o == 'macros':
+                result = macros.subs(result)
+            elif o == 'callouts':
+                result = macros.subs(result,callouts=True)
+            else:
+                raise EAsciiDoc,'illegal substitution option: %s' % o
+            trace(o, s, result)
+            if not result:
+                break
+        return result
+
+    @staticmethod
+    def subs(lines,options):
+        """Perform inline processing specified by 'options' (in 'options'
+        order) on sequence of 'lines'."""
+        if not lines or not options:
+            return lines
+        options = Lex.canonical_subs(options)
+        # Join lines so quoting can span multiple lines.
+        para = '\n'.join(lines)
+        if 'macros' in options:
+            para = macros.extract_passthroughs(para)
+        for o in options:
+            if o == 'attributes':
+                # If we don't substitute attributes line-by-line then a single
+                # undefined attribute will drop the entire paragraph.
+                lines = subs_attrs(para.split('\n'))
+                para = '\n'.join(lines)
+            else:
+                para = Lex.subs_1(para,(o,))
+        if 'macros' in options:
+            para = macros.restore_passthroughs(para)
+        return para.splitlines()
+
+    @staticmethod
+    def set_margin(lines, margin=0):
+        """Utility routine that sets the left margin to 'margin' space in a
+        block of non-blank lines."""
+        # Calculate width of block margin.
+        lines = list(lines)
+        width = len(lines[0])
+        for s in lines:
+            i = re.search(r'\S',s).start()
+            if i < width: width = i
+        # Strip margin width from all lines.
+        for i in range(len(lines)):
+            lines[i] = ' '*margin + lines[i][width:]
+        return lines
+
+#---------------------------------------------------------------------------
+# Document element classes parse AsciiDoc reader input and write DocBook writer
+# output.
+#---------------------------------------------------------------------------
+class Document(object):
+
+    # doctype property.
+    def getdoctype(self):
+        return self.attributes.get('doctype')
+    def setdoctype(self,doctype):
+        self.attributes['doctype'] = doctype
+    doctype = property(getdoctype,setdoctype)
+
+    # backend property.
+    def getbackend(self):
+        return self.attributes.get('backend')
+    def setbackend(self,backend):
+        if backend:
+            backend = self.attributes.get('backend-alias-' + backend, backend)
+        self.attributes['backend'] = backend
+    backend = property(getbackend,setbackend)
+
+    def __init__(self):
+        self.infile = None      # Source file name.
+        self.outfile = None     # Output file name.
+        self.attributes = InsensitiveDict()
+        self.level = 0          # 0 => front matter. 1,2,3 => sect1,2,3.
+        self.has_errors = False # Set true if processing errors were flagged.
+        self.has_warnings = False # Set true if warnings were flagged.
+        self.safe = False       # Default safe mode.
+    def update_attributes(self,attrs=None):
+        """
+        Set implicit attributes and attributes in 'attrs'.
+        """
+        t = time.time()
+        self.attributes['localtime'] = time_str(t)
+        self.attributes['localdate'] = date_str(t)
+        self.attributes['asciidoc-version'] = VERSION
+        self.attributes['asciidoc-file'] = APP_FILE
+        self.attributes['asciidoc-dir'] = APP_DIR
+        self.attributes['asciidoc-confdir'] = CONF_DIR
+        self.attributes['user-dir'] = USER_DIR
+        if config.verbose:
+            self.attributes['verbose'] = ''
+        # Update with configuration file attributes.
+        if attrs:
+            self.attributes.update(attrs)
+        # Update with command-line attributes.
+        self.attributes.update(config.cmd_attrs)
+        # Extract miscellaneous configuration section entries from attributes.
+        if attrs:
+            config.load_miscellaneous(attrs)
+        config.load_miscellaneous(config.cmd_attrs)
+        self.attributes['newline'] = config.newline
+        # File name related attributes can't be overridden.
+        if self.infile is not None:
+            if self.infile and os.path.exists(self.infile):
+                t = os.path.getmtime(self.infile)
+            elif self.infile == '<stdin>':
+                t = time.time()
+            else:
+                t = None
+            if t:
+                self.attributes['doctime'] = time_str(t)
+                self.attributes['docdate'] = date_str(t)
+            if self.infile != '<stdin>':
+                self.attributes['infile'] = self.infile
+                self.attributes['indir'] = os.path.dirname(self.infile)
+                self.attributes['docfile'] = self.infile
+                self.attributes['docdir'] = os.path.dirname(self.infile)
+                self.attributes['docname'] = os.path.splitext(
+                        os.path.basename(self.infile))[0]
+        if self.outfile:
+            if self.outfile != '<stdout>':
+                self.attributes['outfile'] = self.outfile
+                self.attributes['outdir'] = os.path.dirname(self.outfile)
+                if self.infile == '<stdin>':
+                    self.attributes['docname'] = os.path.splitext(
+                            os.path.basename(self.outfile))[0]
+                ext = os.path.splitext(self.outfile)[1][1:]
+            elif config.outfilesuffix:
+                ext = config.outfilesuffix[1:]
+            else:
+                ext = ''
+            if ext:
+                self.attributes['filetype'] = ext
+                self.attributes['filetype-'+ext] = ''
+    def load_lang(self):
+        """
+        Load language configuration file.
+        """
+        lang = self.attributes.get('lang')
+        if lang is None:
+            filename = 'lang-en.conf'   # Default language file.
+        else:
+            filename = 'lang-' + lang + '.conf'
+        if config.load_from_dirs(filename):
+            self.attributes['lang'] = lang  # Reinstate new lang attribute.
+        else:
+            if lang is None:
+                # The default language file must exist.
+                message.error('missing conf file: %s' % filename, halt=True)
+            else:
+                message.warning('missing language conf file: %s' % filename)
+    def set_deprecated_attribute(self,old,new):
+        """
+        Ensures the 'old' name of an attribute that was renamed to 'new' is
+        still honored.
+        """
+        if self.attributes.get(new) is None:
+            if self.attributes.get(old) is not None:
+                self.attributes[new] = self.attributes[old]
+        else:
+            self.attributes[old] = self.attributes[new]
+    def consume_attributes_and_comments(self,comments_only=False,noblanks=False):
+        """
+        Returns True if one or more attributes or comments were consumed.
+        If 'noblanks' is True then consumation halts if a blank line is
+        encountered.
+        """
+        result = False
+        finished = False
+        while not finished:
+            finished = True
+            if noblanks and not reader.read_next(): return result
+            if blocks.isnext() and 'skip' in blocks.current.options:
+                result = True
+                finished = False
+                blocks.current.translate()
+            if noblanks and not reader.read_next(): return result
+            if macros.isnext() and macros.current.name == 'comment':
+                result = True
+                finished = False
+                macros.current.translate()
+            if not comments_only:
+                if AttributeEntry.isnext():
+                    result = True
+                    finished = False
+                    AttributeEntry.translate()
+                if AttributeList.isnext():
+                    result = True
+                    finished = False
+                    AttributeList.translate()
+        return result
+    def parse_header(self,doctype,backend):
+        """
+        Parses header, sets corresponding document attributes and finalizes
+        document doctype and backend properties.
+        Returns False if the document does not have a header.
+        'doctype' and 'backend' are the doctype and backend option values
+        passed on the command-line, None if no command-line option was not
+        specified.
+        """
+        assert self.level == 0
+        # Skip comments and attribute entries that preceed the header.
+        self.consume_attributes_and_comments()
+        if doctype is not None:
+            # Command-line overrides header.
+            self.doctype = doctype
+        elif self.doctype is None:
+            # Was not set on command-line or in document header.
+            self.doctype = DEFAULT_DOCTYPE
+        # Process document header.
+        has_header = (Title.isnext() and Title.level == 0
+                      and AttributeList.style() != 'float')
+        if self.doctype == 'manpage' and not has_header:
+            message.error('manpage document title is mandatory',halt=True)
+        if has_header:
+            Header.parse()
+        # Command-line entries override header derived entries.
+        self.attributes.update(config.cmd_attrs)
+        # DEPRECATED: revision renamed to revnumber.
+        self.set_deprecated_attribute('revision','revnumber')
+        # DEPRECATED: date renamed to revdate.
+        self.set_deprecated_attribute('date','revdate')
+        if doctype is not None:
+            # Command-line overrides header.
+            self.doctype = doctype
+        if backend is not None:
+            # Command-line overrides header.
+            self.backend = backend
+        elif self.backend is None:
+            # Was not set on command-line or in document header.
+            self.backend = DEFAULT_BACKEND
+        else:
+            # Has been set in document header.
+            self.backend = self.backend # Translate alias in header.
+        assert self.doctype in ('article','manpage','book'), 'illegal document type'
+        return has_header
+    def translate(self,has_header):
+        if self.doctype == 'manpage':
+            # Translate mandatory NAME section.
+            if Lex.next() is not Title:
+                message.error('name section expected')
+            else:
+                Title.translate()
+                if Title.level != 1:
+                    message.error('name section title must be at level 1')
+                if not isinstance(Lex.next(),Paragraph):
+                    message.error('malformed name section body')
+                lines = reader.read_until(r'^$')
+                s = ' '.join(lines)
+                mo = re.match(r'^(?P<manname>.*?)\s+-\s+(?P<manpurpose>.*)$',s)
+                if not mo:
+                    message.error('malformed name section body')
+                self.attributes['manname'] = mo.group('manname').strip()
+                self.attributes['manpurpose'] = mo.group('manpurpose').strip()
+                names = [s.strip() for s in self.attributes['manname'].split(',')]
+                if len(names) > 9:
+                    message.warning('to many manpage names')
+                for i,name in enumerate(names):
+                    self.attributes['manname%d' % (i+1)] = name
+        if has_header:
+            # Do postponed substitutions (backend confs have been loaded).
+            self.attributes['doctitle'] = Title.dosubs(self.attributes['doctitle'])
+            if config.header_footer:
+                hdr = config.subs_section('header',{})
+                writer.write(hdr,trace='header')
+            if 'title' in self.attributes:
+                del self.attributes['title']
+            self.consume_attributes_and_comments()
+            if self.doctype in ('article','book'):
+                # Translate 'preamble' (untitled elements between header
+                # and first section title).
+                if Lex.next() is not Title:
+                    stag,etag = config.section2tags('preamble')
+                    writer.write(stag,trace='preamble open')
+                    Section.translate_body()
+                    writer.write(etag,trace='preamble close')
+            elif self.doctype == 'manpage' and 'name' in config.sections:
+                writer.write(config.subs_section('name',{}), trace='name')
+        else:
+            self.process_author_names()
+            if config.header_footer:
+                hdr = config.subs_section('header',{})
+                writer.write(hdr,trace='header')
+            if Lex.next() is not Title:
+                Section.translate_body()
+        # Process remaining sections.
+        while not reader.eof():
+            if Lex.next() is not Title:
+                raise EAsciiDoc,'section title expected'
+            Section.translate()
+        Section.setlevel(0) # Write remaining unwritten section close tags.
+        # Substitute document parameters and write document footer.
+        if config.header_footer:
+            ftr = config.subs_section('footer',{})
+            writer.write(ftr,trace='footer')
+    def parse_author(self,s):
+        """ Return False if the author is malformed."""
+        attrs = self.attributes # Alias for readability.
+        s = s.strip()
+        mo = re.match(r'^(?P<name1>[^<>\s]+)'
+                '(\s+(?P<name2>[^<>\s]+))?'
+                '(\s+(?P<name3>[^<>\s]+))?'
+                '(\s+<(?P<email>\S+)>)?$',s)
+        if not mo:
+            # Names that don't match the formal specification.
+            if s:
+                attrs['firstname'] = s
+            return
+        firstname = mo.group('name1')
+        if mo.group('name3'):
+            middlename = mo.group('name2')
+            lastname = mo.group('name3')
+        else:
+            middlename = None
+            lastname = mo.group('name2')
+        firstname = firstname.replace('_',' ')
+        if middlename:
+            middlename = middlename.replace('_',' ')
+        if lastname:
+            lastname = lastname.replace('_',' ')
+        email = mo.group('email')
+        if firstname:
+            attrs['firstname'] = firstname
+        if middlename:
+            attrs['middlename'] = middlename
+        if lastname:
+            attrs['lastname'] = lastname
+        if email:
+            attrs['email'] = email
+        return
+    def process_author_names(self):
+        """ Calculate any missing author related attributes."""
+        attrs = self.attributes # Alias for readability.
+        firstname = attrs.get('firstname','')
+        middlename = attrs.get('middlename','')
+        lastname = attrs.get('lastname','')
+        author = attrs.get('author')
+        initials = attrs.get('authorinitials')
+        if author and not (firstname or middlename or lastname):
+            self.parse_author(author)
+            attrs['author'] = author.replace('_',' ')
+            self.process_author_names()
+            return
+        if not author:
+            author = '%s %s %s' % (firstname, middlename, lastname)
+            author = author.strip()
+            author = re.sub(r'\s+',' ', author)
+        if not initials:
+            initials = (char_decode(firstname)[:1] +
+                       char_decode(middlename)[:1] + char_decode(lastname)[:1])
+            initials = char_encode(initials).upper()
+        names = [firstname,middlename,lastname,author,initials]
+        for i,v in enumerate(names):
+            v = config.subs_specialchars(v)
+            v = subs_attrs(v)
+            names[i] = v
+        firstname,middlename,lastname,author,initials = names
+        if firstname:
+            attrs['firstname'] = firstname
+        if middlename:
+            attrs['middlename'] = middlename
+        if lastname:
+            attrs['lastname'] = lastname
+        if author:
+            attrs['author'] = author
+        if initials:
+            attrs['authorinitials'] = initials
+        if author:
+            attrs['authored'] = ''
+
+
+class Header:
+    """Static methods and attributes only."""
+    REV_LINE_RE = r'^(\D*(?P<revnumber>.*?),)?(?P<revdate>.*?)(:\s*(?P<revremark>.*))?$'
+    RCS_ID_RE = r'^\$Id: \S+ (?P<revnumber>\S+) (?P<revdate>\S+) \S+ (?P<author>\S+) (\S+ )?\$$'
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def parse():
+        assert Lex.next() is Title and Title.level == 0
+        attrs = document.attributes # Alias for readability.
+        # Postpone title subs until backend conf files have been loaded.
+        Title.translate(skipsubs=True)
+        attrs['doctitle'] = Title.attributes['title']
+        document.consume_attributes_and_comments(noblanks=True)
+        s = reader.read_next()
+        mo = None
+        if s:
+            # Process first header line after the title that is not a comment
+            # or an attribute entry.
+            s = reader.read()
+            mo = re.match(Header.RCS_ID_RE,s)
+            if not mo:
+                document.parse_author(s)
+                document.consume_attributes_and_comments(noblanks=True)
+                if reader.read_next():
+                    # Process second header line after the title that is not a
+                    # comment or an attribute entry.
+                    s = reader.read()
+                    s = subs_attrs(s)
+                    if s:
+                        mo = re.match(Header.RCS_ID_RE,s)
+                        if not mo:
+                            mo = re.match(Header.REV_LINE_RE,s)
+            document.consume_attributes_and_comments(noblanks=True)
+        s = attrs.get('revnumber')
+        if s:
+            mo = re.match(Header.RCS_ID_RE,s)
+        if mo:
+            revnumber = mo.group('revnumber')
+            if revnumber:
+                attrs['revnumber'] = revnumber.strip()
+            author = mo.groupdict().get('author')
+            if author and 'firstname' not in attrs:
+                document.parse_author(author)
+            revremark = mo.groupdict().get('revremark')
+            if revremark is not None:
+                revremark = [revremark]
+                # Revision remarks can continue on following lines.
+                while reader.read_next():
+                    if document.consume_attributes_and_comments(noblanks=True):
+                        break
+                    revremark.append(reader.read())
+                revremark = Lex.subs(revremark,['normal'])
+                revremark = '\n'.join(revremark).strip()
+                attrs['revremark'] = revremark
+            revdate = mo.group('revdate')
+            if revdate:
+                attrs['revdate'] = revdate.strip()
+            elif revnumber or revremark:
+                # Set revision date to ensure valid DocBook revision.
+                attrs['revdate'] = attrs['docdate']
+        document.process_author_names()
+        if document.doctype == 'manpage':
+            # manpage title formatted like mantitle(manvolnum).
+            mo = re.match(r'^(?P<mantitle>.*)\((?P<manvolnum>.*)\)$',
+                          attrs['doctitle'])
+            if not mo:
+                message.error('malformed manpage title')
+            else:
+                mantitle = mo.group('mantitle').strip()
+                mantitle = subs_attrs(mantitle)
+                if mantitle is None:
+                    message.error('undefined attribute in manpage title')
+                # mantitle is lowered only if in ALL CAPS
+                if mantitle == mantitle.upper():
+                    mantitle = mantitle.lower()
+                attrs['mantitle'] = mantitle;
+                attrs['manvolnum'] = mo.group('manvolnum').strip()
+
+class AttributeEntry:
+    """Static methods and attributes only."""
+    pattern = None
+    subs = None
+    name = None
+    name2 = None
+    value = None
+    attributes = {}     # Accumulates all the parsed attribute entries.
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        if not AttributeEntry.pattern:
+            pat = document.attributes.get('attributeentry-pattern')
+            if not pat:
+                message.error("[attributes] missing 'attributeentry-pattern' entry")
+            AttributeEntry.pattern = pat
+        line = reader.read_next()
+        if line:
+            # Attribute entry formatted like :<name>[.<name2>]:[ <value>]
+            mo = re.match(AttributeEntry.pattern,line)
+            if mo:
+                AttributeEntry.name = mo.group('attrname')
+                AttributeEntry.name2 = mo.group('attrname2')
+                AttributeEntry.value = mo.group('attrvalue') or ''
+                AttributeEntry.value = AttributeEntry.value.strip()
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is AttributeEntry
+        attr = AttributeEntry    # Alias for brevity.
+        reader.read()            # Discard attribute entry from reader.
+        while attr.value.endswith(' +'):
+            if not reader.read_next(): break
+            attr.value = attr.value[:-1] + reader.read().strip()
+        if attr.name2 is not None:
+            # Configuration file attribute.
+            if attr.name2 != '':
+                # Section entry attribute.
+                section = {}
+                # Some sections can have name! syntax.
+                if attr.name in ('attributes','miscellaneous') and attr.name2[-1] == '!':
+                    section[attr.name] = [attr.name2]
+                else:
+                   section[attr.name] = ['%s=%s' % (attr.name2,attr.value)]
+                config.load_sections(section)
+                config.load_miscellaneous(config.conf_attrs)
+            else:
+                # Markup template section attribute.
+                if attr.name in config.sections:
+                    config.sections[attr.name] = [attr.value]
+                else:
+                    message.warning('missing configuration section: %s' % attr.name)
+        else:
+            # Normal attribute.
+            if attr.name[-1] == '!':
+                # Names like name! undefine the attribute.
+                attr.name = attr.name[:-1]
+                attr.value = None
+            # Strip white space and illegal name chars.
+            attr.name = re.sub(r'(?u)[^\w\-_]', '', attr.name).lower()
+            # Don't override most command-line attributes.
+            if attr.name in config.cmd_attrs \
+                    and attr.name not in ('trace','numbered'):
+                return
+            # Update document attributes with attribute value.
+            if attr.value is not None:
+                mo = re.match(r'^pass:(?P<attrs>.*)\[(?P<value>.*)\]$', attr.value)
+                if mo:
+                    # Inline passthrough syntax.
+                    attr.subs = mo.group('attrs')
+                    attr.value = mo.group('value')  # Passthrough.
+                else:
+                    # Default substitution.
+                    # DEPRECATED: attributeentry-subs
+                    attr.subs = document.attributes.get('attributeentry-subs',
+                                'specialcharacters,attributes')
+                attr.subs = parse_options(attr.subs, SUBS_OPTIONS,
+                            'illegal substitution option')
+                attr.value = Lex.subs((attr.value,), attr.subs)
+                attr.value = writer.newline.join(attr.value)
+                document.attributes[attr.name] = attr.value
+            elif attr.name in document.attributes:
+                del document.attributes[attr.name]
+            attr.attributes[attr.name] = attr.value
+
+class AttributeList:
+    """Static methods and attributes only."""
+    pattern = None
+    match = None
+    attrs = {}
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def initialize():
+        if not 'attributelist-pattern' in document.attributes:
+            message.error("[attributes] missing 'attributelist-pattern' entry")
+        AttributeList.pattern = document.attributes['attributelist-pattern']
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        line = reader.read_next()
+        if line:
+            mo = re.match(AttributeList.pattern, line)
+            if mo:
+                AttributeList.match = mo
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is AttributeList
+        reader.read()   # Discard attribute list from reader.
+        attrs = {}
+        d = AttributeList.match.groupdict()
+        for k,v in d.items():
+            if v is not None:
+                if k == 'attrlist':
+                    v = subs_attrs(v)
+                    if v:
+                        parse_attributes(v, attrs)
+                else:
+                    AttributeList.attrs[k] = v
+        AttributeList.subs(attrs)
+        AttributeList.attrs.update(attrs)
+    @staticmethod
+    def subs(attrs):
+        '''Substitute single quoted attribute values normally.'''
+        reo = re.compile(r"^'.*'$")
+        for k,v in attrs.items():
+            if reo.match(str(v)):
+                attrs[k] = Lex.subs_1(v[1:-1],SUBS_NORMAL)
+    @staticmethod
+    def style():
+        return AttributeList.attrs.get('style') or AttributeList.attrs.get('1')
+    @staticmethod
+    def consume(d):
+        """Add attribute list to the dictionary 'd' and reset the
+        list."""
+        if AttributeList.attrs:
+            d.update(AttributeList.attrs)
+            AttributeList.attrs = {}
+            # Generate option attributes.
+            if 'options' in d:
+                options = parse_options(d['options'], (), 'illegal option name')
+                for option in options:
+                    d[option+'-option'] = ''
+
+class BlockTitle:
+    """Static methods and attributes only."""
+    title = None
+    pattern = None
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        line = reader.read_next()
+        if line:
+            mo = re.match(BlockTitle.pattern,line)
+            if mo:
+                BlockTitle.title = mo.group('title')
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is BlockTitle
+        reader.read()   # Discard title from reader.
+        # Perform title substitutions.
+        if not Title.subs:
+            Title.subs = config.subsnormal
+        s = Lex.subs((BlockTitle.title,), Title.subs)
+        s = writer.newline.join(s)
+        if not s:
+            message.warning('blank block title')
+        BlockTitle.title = s
+    @staticmethod
+    def consume(d):
+        """If there is a title add it to dictionary 'd' then reset title."""
+        if BlockTitle.title:
+            d['title'] = BlockTitle.title
+            BlockTitle.title = None
+
+class Title:
+    """Processes Header and Section titles. Static methods and attributes
+    only."""
+    # Class variables
+    underlines = ('==','--','~~','^^','++') # Levels 0,1,2,3,4.
+    subs = ()
+    pattern = None
+    level = 0
+    attributes = {}
+    sectname = None
+    section_numbers = [0]*len(underlines)
+    dump_dict = {}
+    linecount = None    # Number of lines in title (1 or 2).
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def translate(skipsubs=False):
+        """Parse the Title.attributes and Title.level from the reader. The
+        real work has already been done by parse()."""
+        assert Lex.next() in (Title,FloatingTitle)
+        # Discard title from reader.
+        for i in range(Title.linecount):
+            reader.read()
+        Title.setsectname()
+        if not skipsubs:
+            Title.attributes['title'] = Title.dosubs(Title.attributes['title'])
+    @staticmethod
+    def dosubs(title):
+        """
+        Perform title substitutions.
+        """
+        if not Title.subs:
+            Title.subs = config.subsnormal
+        title = Lex.subs((title,), Title.subs)
+        title = writer.newline.join(title)
+        if not title:
+            message.warning('blank section title')
+        return title
+    @staticmethod
+    def isnext():
+        lines = reader.read_ahead(2)
+        return Title.parse(lines)
+    @staticmethod
+    def parse(lines):
+        """Parse title at start of lines tuple."""
+        if len(lines) == 0: return False
+        if len(lines[0]) == 0: return False # Title can't be blank.
+        # Check for single-line titles.
+        result = False
+        for level in range(len(Title.underlines)):
+            k = 'sect%s' % level
+            if k in Title.dump_dict:
+                mo = re.match(Title.dump_dict[k], lines[0])
+                if mo:
+                    Title.attributes = mo.groupdict()
+                    Title.level = level
+                    Title.linecount = 1
+                    result = True
+                    break
+        if not result:
+            # Check for double-line titles.
+            if not Title.pattern: return False  # Single-line titles only.
+            if len(lines) < 2: return False
+            title,ul = lines[:2]
+            title_len = column_width(title)
+            ul_len = char_len(ul)
+            if ul_len < 2: return False
+            # Fast elimination check.
+            if ul[:2] not in Title.underlines: return False
+            # Length of underline must be within +-3 of title.
+            if not ((ul_len-3 < title_len < ul_len+3)
+                    # Next test for backward compatibility.
+                    or (ul_len-3 < char_len(title) < ul_len+3)):
+                return False
+            # Check for valid repetition of underline character pairs.
+            s = ul[:2]*((ul_len+1)/2)
+            if ul != s[:ul_len]: return False
+            # Don't be fooled by back-to-back delimited blocks, require at
+            # least one alphanumeric character in title.
+            if not re.search(r'(?u)\w',title): return False
+            mo = re.match(Title.pattern, title)
+            if mo:
+                Title.attributes = mo.groupdict()
+                Title.level = list(Title.underlines).index(ul[:2])
+                Title.linecount = 2
+                result = True
+        # Check for expected pattern match groups.
+        if result:
+            if not 'title' in Title.attributes:
+                message.warning('[titles] entry has no <title> group')
+                Title.attributes['title'] = lines[0]
+            for k,v in Title.attributes.items():
+                if v is None: del Title.attributes[k]
+        try:
+            Title.level += int(document.attributes.get('leveloffset','0'))
+        except:
+            pass
+        Title.attributes['level'] = str(Title.level)
+        return result
+    @staticmethod
+    def load(entries):
+        """Load and validate [titles] section entries dictionary."""
+        if 'underlines' in entries:
+            errmsg = 'malformed [titles] underlines entry'
+            try:
+                underlines = parse_list(entries['underlines'])
+            except Exception:
+                raise EAsciiDoc,errmsg
+            if len(underlines) != len(Title.underlines):
+                raise EAsciiDoc,errmsg
+            for s in underlines:
+                if len(s) !=2:
+                    raise EAsciiDoc,errmsg
+            Title.underlines = tuple(underlines)
+            Title.dump_dict['underlines'] = entries['underlines']
+        if 'subs' in entries:
+            Title.subs = parse_options(entries['subs'], SUBS_OPTIONS,
+                'illegal [titles] subs entry')
+            Title.dump_dict['subs'] = entries['subs']
+        if 'sectiontitle' in entries:
+            pat = entries['sectiontitle']
+            if not pat or not is_re(pat):
+                raise EAsciiDoc,'malformed [titles] sectiontitle entry'
+            Title.pattern = pat
+            Title.dump_dict['sectiontitle'] = pat
+        if 'blocktitle' in entries:
+            pat = entries['blocktitle']
+            if not pat or not is_re(pat):
+                raise EAsciiDoc,'malformed [titles] blocktitle entry'
+            BlockTitle.pattern = pat
+            Title.dump_dict['blocktitle'] = pat
+        # Load single-line title patterns.
+        for k in ('sect0','sect1','sect2','sect3','sect4'):
+            if k in entries:
+                pat = entries[k]
+                if not pat or not is_re(pat):
+                    raise EAsciiDoc,'malformed [titles] %s entry' % k
+                Title.dump_dict[k] = pat
+        # TODO: Check we have either a Title.pattern or at least one
+        # single-line title pattern -- can this be done here or do we need
+        # check routine like the other block checkers?
+    @staticmethod
+    def dump():
+        dump_section('titles',Title.dump_dict)
+    @staticmethod
+    def setsectname():
+        """
+        Set Title section name:
+        If the first positional or 'template' attribute is set use it,
+        next search for section title in [specialsections],
+        if not found use default 'sect<level>' name.
+        """
+        sectname = AttributeList.attrs.get('1')
+        if sectname and sectname != 'float':
+            Title.sectname = sectname
+        elif 'template' in AttributeList.attrs:
+            Title.sectname = AttributeList.attrs['template']
+        else:
+            for pat,sect in config.specialsections.items():
+                mo = re.match(pat,Title.attributes['title'])
+                if mo:
+                    title = mo.groupdict().get('title')
+                    if title is not None:
+                        Title.attributes['title'] = title.strip()
+                    else:
+                        Title.attributes['title'] = mo.group().strip()
+                    Title.sectname = sect
+                    break
+            else:
+                Title.sectname = 'sect%d' % Title.level
+    @staticmethod
+    def getnumber(level):
+        """Return next section number at section 'level' formatted like
+        1.2.3.4."""
+        number = ''
+        for l in range(len(Title.section_numbers)):
+            n = Title.section_numbers[l]
+            if l == 0:
+                continue
+            elif l < level:
+                number = '%s%d.' % (number, n)
+            elif l == level:
+                number = '%s%d.' % (number, n + 1)
+                Title.section_numbers[l] = n + 1
+            elif l > level:
+                # Reset unprocessed section levels.
+                Title.section_numbers[l] = 0
+        return number
+
+
+class FloatingTitle(Title):
+    '''Floated titles are translated differently.'''
+    @staticmethod
+    def isnext():
+        return Title.isnext() and AttributeList.style() == 'float'
+    @staticmethod
+    def translate():
+        assert Lex.next() is FloatingTitle
+        Title.translate()
+        Section.set_id()
+        AttributeList.consume(Title.attributes)
+        template = 'floatingtitle'
+        if template in config.sections:
+            stag,etag = config.section2tags(template,Title.attributes)
+            writer.write(stag,trace='floating title')
+        else:
+            message.warning('missing template section: [%s]' % template)
+
+
+class Section:
+    """Static methods and attributes only."""
+    endtags = []  # Stack of currently open section (level,endtag) tuples.
+    ids = []      # List of already used ids.
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def savetag(level,etag):
+        """Save section end."""
+        Section.endtags.append((level,etag))
+    @staticmethod
+    def setlevel(level):
+        """Set document level and write open section close tags up to level."""
+        while Section.endtags and Section.endtags[-1][0] >= level:
+            writer.write(Section.endtags.pop()[1],trace='section close')
+        document.level = level
+    @staticmethod
+    def gen_id(title):
+        """
+        The normalized value of the id attribute is an NCName according to
+        the 'Namespaces in XML' Recommendation:
+        NCName          ::=     NCNameStartChar NCNameChar*
+        NCNameChar      ::=     NameChar - ':'
+        NCNameStartChar ::=     Letter | '_'
+        NameChar        ::=     Letter | Digit | '.' | '-' | '_' | ':'
+        """
+        # Replace non-alpha numeric characters in title with underscores and
+        # convert to lower case.
+        base_ident = char_encode(re.sub(r'(?u)\W+', '_',
+                char_decode(title)).strip('_').lower())
+        # Prefix the ID name with idprefix attribute or underscore if not
+        # defined. Prefix ensures the ID does not clash with existing IDs.
+        idprefix = document.attributes.get('idprefix','_')
+        base_ident = idprefix + base_ident
+        i = 1
+        while True:
+            if i == 1:
+                ident = base_ident
+            else:
+                ident = '%s_%d' % (base_ident, i)
+            if ident not in Section.ids:
+                Section.ids.append(ident)
+                return ident
+            else:
+                ident = base_ident
+            i += 1
+    @staticmethod
+    def set_id():
+        if not document.attributes.get('sectids') is None \
+                and 'id' not in AttributeList.attrs:
+            # Generate ids for sections.
+            AttributeList.attrs['id'] = Section.gen_id(Title.attributes['title'])
+    @staticmethod
+    def translate():
+        assert Lex.next() is Title
+        prev_sectname = Title.sectname
+        Title.translate()
+        if Title.level == 0 and document.doctype != 'book':
+            message.error('only book doctypes can contain level 0 sections')
+        if Title.level > document.level \
+                and 'basebackend-docbook' in document.attributes \
+                and prev_sectname in ('colophon','abstract', \
+                    'dedication','glossary','bibliography'):
+            message.error('%s section cannot contain sub-sections' % prev_sectname)
+        if Title.level > document.level+1:
+            # Sub-sections of multi-part book level zero Preface and Appendices
+            # are meant to be out of sequence.
+            if document.doctype == 'book' \
+                    and document.level == 0 \
+                    and Title.level == 2 \
+                    and prev_sectname in ('preface','appendix'):
+                pass
+            else:
+                message.warning('section title out of sequence: '
+                    'expected level %d, got level %d'
+                    % (document.level+1, Title.level))
+        Section.set_id()
+        Section.setlevel(Title.level)
+        if 'numbered' in document.attributes:
+            Title.attributes['sectnum'] = Title.getnumber(document.level)
+        else:
+            Title.attributes['sectnum'] = ''
+        AttributeList.consume(Title.attributes)
+        stag,etag = config.section2tags(Title.sectname,Title.attributes)
+        Section.savetag(Title.level,etag)
+        writer.write(stag,trace='section open: level %d: %s' %
+                (Title.level, Title.attributes['title']))
+        Section.translate_body()
+    @staticmethod
+    def translate_body(terminator=Title):
+        isempty = True
+        next = Lex.next()
+        while next and next is not terminator:
+            if isinstance(terminator,DelimitedBlock) and next is Title:
+                message.error('section title not permitted in delimited block')
+            next.translate()
+            next = Lex.next()
+            isempty = False
+        # The section is not empty if contains a subsection.
+        if next and isempty and Title.level > document.level:
+            isempty = False
+        # Report empty sections if invalid markup will result.
+        if isempty:
+            if document.backend == 'docbook' and Title.sectname != 'index':
+                message.error('empty section is not valid')
+
+class AbstractBlock:
+    def __init__(self):
+        # Configuration parameter names common to all blocks.
+        self.CONF_ENTRIES = ('delimiter','options','subs','presubs','postsubs',
+                             'posattrs','style','.*-style','template','filter')
+        self.start = None   # File reader cursor at start delimiter.
+        self.name=None      # Configuration file section name.
+        # Configuration parameters.
+        self.delimiter=None # Regular expression matching block delimiter.
+        self.delimiter_reo=None # Compiled delimiter.
+        self.template=None  # template section entry.
+        self.options=()     # options entry list.
+        self.presubs=None   # presubs/subs entry list.
+        self.postsubs=()    # postsubs entry list.
+        self.filter=None    # filter entry.
+        self.posattrs=()    # posattrs entry list.
+        self.style=None     # Default style.
+        self.styles=OrderedDict() # Each entry is a styles dictionary.
+        # Before a block is processed it's attributes (from it's
+        # attributes list) are merged with the block configuration parameters
+        # (by self.merge_attributes()) resulting in the template substitution
+        # dictionary (self.attributes) and the block's processing parameters
+        # (self.parameters).
+        self.attributes={}
+        # The names of block parameters.
+        self.PARAM_NAMES=('template','options','presubs','postsubs','filter')
+        self.parameters=None
+        # Leading delimiter match object.
+        self.mo=None
+    def short_name(self):
+        """ Return the text following the last dash in the section name."""
+        i = self.name.rfind('-')
+        if i == -1:
+            return self.name
+        else:
+            return self.name[i+1:]
+    def error(self, msg, cursor=None, halt=False):
+        message.error('[%s] %s' % (self.name,msg), cursor, halt)
+    def is_conf_entry(self,param):
+        """Return True if param matches an allowed configuration file entry
+        name."""
+        for s in self.CONF_ENTRIES:
+            if re.match('^'+s+'$',param):
+                return True
+        return False
+    def load(self,name,entries):
+        """Update block definition from section 'entries' dictionary."""
+        self.name = name
+        self.update_parameters(entries, self, all=True)
+    def update_parameters(self, src, dst=None, all=False):
+        """
+        Parse processing parameters from src dictionary to dst object.
+        dst defaults to self.parameters.
+        If all is True then copy src entries that aren't parameter names.
+        """
+        dst = dst or self.parameters
+        msg = '[%s] malformed entry %%s: %%s' % self.name
+        def copy(obj,k,v):
+            if isinstance(obj,dict):
+                obj[k] = v
+            else:
+                setattr(obj,k,v)
+        for k,v in src.items():
+            if not re.match(r'\d+',k) and not is_name(k):
+                raise EAsciiDoc, msg % (k,v)
+            if k == 'template':
+                if not is_name(v):
+                    raise EAsciiDoc, msg % (k,v)
+                copy(dst,k,v)
+            elif k == 'filter':
+                copy(dst,k,v)
+            elif k == 'options':
+                if isinstance(v,str):
+                    v = parse_options(v, (), msg % (k,v))
+                    # Merge with existing options.
+                    v = tuple(set(dst.options).union(set(v)))
+                copy(dst,k,v)
+            elif k in ('subs','presubs','postsubs'):
+                # Subs is an alias for presubs.
+                if k == 'subs': k = 'presubs'
+                if isinstance(v,str):
+                    v = parse_options(v, SUBS_OPTIONS, msg % (k,v))
+                copy(dst,k,v)
+            elif k == 'delimiter':
+                if v and is_re(v):
+                    copy(dst,k,v)
+                else:
+                    raise EAsciiDoc, msg % (k,v)
+            elif k == 'style':
+                if is_name(v):
+                    copy(dst,k,v)
+                else:
+                    raise EAsciiDoc, msg % (k,v)
+            elif k == 'posattrs':
+                v = parse_options(v, (), msg % (k,v))
+                copy(dst,k,v)
+            else:
+                mo = re.match(r'^(?P<style>.*)-style$',k)
+                if mo:
+                    if not v:
+                        raise EAsciiDoc, msg % (k,v)
+                    style = mo.group('style')
+                    if not is_name(style):
+                        raise EAsciiDoc, msg % (k,v)
+                    d = {}
+                    if not parse_named_attributes(v,d):
+                        raise EAsciiDoc, msg % (k,v)
+                    if 'subs' in d:
+                        # Subs is an alias for presubs.
+                        d['presubs'] = d['subs']
+                        del d['subs']
+                    self.styles[style] = d
+                elif all or k in self.PARAM_NAMES:
+                    copy(dst,k,v) # Derived class specific entries.
+    def get_param(self,name,params=None):
+        """
+        Return named processing parameter from params dictionary.
+        If the parameter is not in params look in self.parameters.
+        """
+        if params and name in params:
+            return params[name]
+        elif name in self.parameters:
+            return self.parameters[name]
+        else:
+            return None
+    def get_subs(self,params=None):
+        """
+        Return (presubs,postsubs) tuple.
+        """
+        presubs = self.get_param('presubs',params)
+        postsubs = self.get_param('postsubs',params)
+        return (presubs,postsubs)
+    def dump(self):
+        """Write block definition to stdout."""
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('['+self.name+']')
+        if self.is_conf_entry('delimiter'):
+            write('delimiter='+self.delimiter)
+        if self.template:
+            write('template='+self.template)
+        if self.options:
+            write('options='+','.join(self.options))
+        if self.presubs:
+            if self.postsubs:
+                write('presubs='+','.join(self.presubs))
+            else:
+                write('subs='+','.join(self.presubs))
+        if self.postsubs:
+            write('postsubs='+','.join(self.postsubs))
+        if self.filter:
+            write('filter='+self.filter)
+        if self.posattrs:
+            write('posattrs='+','.join(self.posattrs))
+        if self.style:
+            write('style='+self.style)
+        if self.styles:
+            for style,d in self.styles.items():
+                s = ''
+                for k,v in d.items(): s += '%s=%r,' % (k,v)
+                write('%s-style=%s' % (style,s[:-1]))
+    def validate(self):
+        """Validate block after the complete configuration has been loaded."""
+        if self.is_conf_entry('delimiter') and not self.delimiter:
+            raise EAsciiDoc,'[%s] missing delimiter' % self.name
+        if self.style:
+            if not is_name(self.style):
+                raise EAsciiDoc, 'illegal style name: %s' % self.style
+            if not self.style in self.styles:
+                if not isinstance(self,List):   # Lists don't have templates.
+                    message.warning('[%s] \'%s\' style not in %s' % (
+                        self.name,self.style,self.styles.keys()))
+        # Check all styles for missing templates.
+        all_styles_have_template = True
+        for k,v in self.styles.items():
+            t = v.get('template')
+            if t and not t in config.sections:
+                # Defer check if template name contains attributes.
+                if not re.search(r'{.+}',t):
+                    message.warning('missing template section: [%s]' % t)
+            if not t:
+                all_styles_have_template = False
+        # Check we have a valid template entry or alternatively that all the
+        # styles have templates.
+        if self.is_conf_entry('template') and not 'skip' in self.options:
+            if self.template:
+                if not self.template in config.sections:
+                    # Defer check if template name contains attributes.
+                    if not re.search(r'{.+}',self.template):
+                        message.warning('missing template section: [%s]'
+                                        % self.template)
+            elif not all_styles_have_template:
+                if not isinstance(self,List): # Lists don't have templates.
+                    message.warning('missing styles templates: [%s]' % self.name)
+    def isnext(self):
+        """Check if this block is next in document reader."""
+        result = False
+        reader.skip_blank_lines()
+        if reader.read_next():
+            if not self.delimiter_reo:
+                # Cache compiled delimiter optimization.
+                self.delimiter_reo = re.compile(self.delimiter)
+            mo = self.delimiter_reo.match(reader.read_next())
+            if mo:
+                self.mo = mo
+                result = True
+        return result
+    def translate(self):
+        """Translate block from document reader."""
+        if not self.presubs:
+            self.presubs = config.subsnormal
+        if reader.cursor:
+            self.start = reader.cursor[:]
+    def merge_attributes(self,attrs,params=[]):
+        """
+        Use the current blocks attribute list (attrs dictionary) to build a
+        dictionary of block processing parameters (self.parameters) and tag
+        substitution attributes (self.attributes).
+
+        1. Copy the default parameters (self.*) to self.parameters.
+        self.parameters are used internally to render the current block.
+        Optional params array of additional parameters.
+
+        2. Copy attrs to self.attributes. self.attributes are used for template
+        and tag substitution in the current block.
+
+        3. If a style attribute was specified update self.parameters with the
+        corresponding style parameters; if there are any style parameters
+        remaining add them to self.attributes (existing attribute list entries
+        take precedence).
+
+        4. Set named positional attributes in self.attributes if self.posattrs
+        was specified.
+
+        5. Finally self.parameters is updated with any corresponding parameters
+        specified in attrs.
+
+        """
+
+        def check_array_parameter(param):
+            # Check the parameter is a sequence type.
+            if not is_array(self.parameters[param]):
+                message.error('malformed presubs attribute: %s' %
+                        self.parameters[param])
+                # Revert to default value.
+                self.parameters[param] = getattr(self,param)
+
+        params = list(self.PARAM_NAMES) + params
+        self.attributes = {}
+        if self.style:
+            # If a default style is defined make it available in the template.
+            self.attributes['style'] = self.style
+        self.attributes.update(attrs)
+        # Calculate dynamic block parameters.
+        # Start with configuration file defaults.
+        self.parameters = AttrDict()
+        for name in params:
+            self.parameters[name] = getattr(self,name)
+        # Load the selected style attributes.
+        posattrs = self.posattrs
+        if posattrs and posattrs[0] == 'style':
+            # Positional attribute style has highest precedence.
+            style = self.attributes.get('1')
+        else:
+            style = None
+        if not style:
+            # Use explicit style attribute, fall back to default style.
+            style = self.attributes.get('style',self.style)
+        if style:
+            if not is_name(style):
+                message.error('illegal style name: %s' % style)
+                style = self.style
+            # Lists have implicit styles and do their own style checks.
+            elif style not in self.styles and not isinstance(self,List):
+                message.warning('missing style: [%s]: %s' % (self.name,style))
+                style = self.style
+            if style in self.styles:
+                self.attributes['style'] = style
+                for k,v in self.styles[style].items():
+                    if k == 'posattrs':
+                        posattrs = v
+                    elif k in params:
+                        self.parameters[k] = v
+                    elif not k in self.attributes:
+                        # Style attributes don't take precedence over explicit.
+                        self.attributes[k] = v
+        # Set named positional attributes.
+        for i,v in enumerate(posattrs):
+            if str(i+1) in self.attributes:
+                self.attributes[v] = self.attributes[str(i+1)]
+        # Override config and style attributes with attribute list attributes.
+        self.update_parameters(attrs)
+        check_array_parameter('options')
+        check_array_parameter('presubs')
+        check_array_parameter('postsubs')
+
+class AbstractBlocks:
+    """List of block definitions."""
+    PREFIX = ''         # Conf file section name prefix set in derived classes.
+    BLOCK_TYPE = None   # Block type set in derived classes.
+    def __init__(self):
+        self.current=None
+        self.blocks = []        # List of Block objects.
+        self.default = None     # Default Block.
+        self.delimiters = None  # Combined delimiters regular expression.
+    def load(self,sections):
+        """Load block definition from 'sections' dictionary."""
+        for k in sections.keys():
+            if re.match(r'^'+ self.PREFIX + r'.+$',k):
+                d = {}
+                parse_entries(sections.get(k,()),d)
+                for b in self.blocks:
+                    if b.name == k:
+                        break
+                else:
+                    b = self.BLOCK_TYPE()
+                    self.blocks.append(b)
+                try:
+                    b.load(k,d)
+                except EAsciiDoc,e:
+                    raise EAsciiDoc,'[%s] %s' % (k,str(e))
+    def dump(self):
+        for b in self.blocks:
+            b.dump()
+    def isnext(self):
+        for b in self.blocks:
+            if b.isnext():
+                self.current = b
+                return True;
+        return False
+    def validate(self):
+        """Validate the block definitions."""
+        # Validate delimiters and build combined lists delimiter pattern.
+        delimiters = []
+        for b in self.blocks:
+            assert b.__class__ is self.BLOCK_TYPE
+            b.validate()
+            if b.delimiter:
+                delimiters.append(b.delimiter)
+        self.delimiters = re_join(delimiters)
+
+class Paragraph(AbstractBlock):
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.text=None          # Text in first line of paragraph.
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('')
+    def isnext(self):
+        result = AbstractBlock.isnext(self)
+        if result:
+            self.text = self.mo.groupdict().get('text')
+        return result
+    def translate(self):
+        AbstractBlock.translate(self)
+        attrs = self.mo.groupdict().copy()
+        if 'text' in attrs: del attrs['text']
+        BlockTitle.consume(attrs)
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        reader.read()   # Discard (already parsed item first line).
+        body = reader.read_until(paragraphs.terminators)
+        body = [self.text] + list(body)
+        presubs = self.parameters.presubs
+        postsubs = self.parameters.postsubs
+        if document.attributes.get('plaintext') is None:
+            body = Lex.set_margin(body) # Move body to left margin.
+        body = Lex.subs(body,presubs)
+        template = self.parameters.template
+        template = subs_attrs(template,attrs)
+        stag = config.section2tags(template, self.attributes,skipend=True)[0]
+        if self.parameters.filter:
+            body = filter_lines(self.parameters.filter,body,self.attributes)
+        body = Lex.subs(body,postsubs)
+        etag = config.section2tags(template, self.attributes,skipstart=True)[1]
+        # Write start tag, content, end tag.
+        writer.write(dovetail_tags(stag,body,etag),trace='paragraph')
+
+class Paragraphs(AbstractBlocks):
+    """List of paragraph definitions."""
+    BLOCK_TYPE = Paragraph
+    PREFIX = 'paradef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        self.terminators=None    # List of compiled re's.
+    def initialize(self):
+        self.terminators = [
+                re.compile(r'^\+$|^$'),
+                re.compile(AttributeList.pattern),
+                re.compile(blocks.delimiters),
+                re.compile(tables.delimiters),
+                re.compile(tables_OLD.delimiters),
+            ]
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        AbstractBlocks.validate(self)
+        # Check we have a default paragraph definition, put it last in list.
+        for b in self.blocks:
+            if b.name == 'paradef-default':
+                self.blocks.append(b)
+                self.default = b
+                self.blocks.remove(b)
+                break
+        else:
+            raise EAsciiDoc,'missing section: [paradef-default]'
+
+class List(AbstractBlock):
+    NUMBER_STYLES= ('arabic','loweralpha','upperalpha','lowerroman',
+                    'upperroman')
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('type','tags')
+        self.PARAM_NAMES += ('tags',)
+        # tabledef conf file parameters.
+        self.type=None
+        self.tags=None      # Name of listtags-<tags> conf section.
+        # Calculated parameters.
+        self.tag=None       # Current tags AttrDict.
+        self.label=None     # List item label (labeled lists).
+        self.text=None      # Text in first line of list item.
+        self.index=None     # Matched delimiter 'index' group (numbered lists).
+        self.type=None      # List type ('numbered','bulleted','labeled').
+        self.ordinal=None   # Current list item ordinal number (1..)
+        self.number_style=None # Current numbered list style ('arabic'..)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('type='+self.type)
+        write('tags='+self.tags)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        tags = [self.tags]
+        tags += [s['tags'] for s in self.styles.values() if 'tags' in s]
+        for t in tags:
+            if t not in lists.tags:
+                self.error('missing section: [listtags-%s]' % t,halt=True)
+    def isnext(self):
+        result = AbstractBlock.isnext(self)
+        if result:
+            self.label = self.mo.groupdict().get('label')
+            self.text = self.mo.groupdict().get('text')
+            self.index = self.mo.groupdict().get('index')
+        return result
+    def translate_entry(self):
+        assert self.type == 'labeled'
+        entrytag = subs_tag(self.tag.entry, self.attributes)
+        labeltag = subs_tag(self.tag.label, self.attributes)
+        writer.write(entrytag[0],trace='list entry open')
+        writer.write(labeltag[0],trace='list label open')
+        # Write labels.
+        while Lex.next() is self:
+            reader.read()   # Discard (already parsed item first line).
+            writer.write_tag(self.tag.term, [self.label],
+                             self.presubs, self.attributes,trace='list term')
+            if self.text: break
+        writer.write(labeltag[1],trace='list label close')
+        # Write item text.
+        self.translate_item()
+        writer.write(entrytag[1],trace='list entry close')
+    def translate_item(self):
+        if self.type == 'callout':
+            self.attributes['coids'] = calloutmap.calloutids(self.ordinal)
+        itemtag = subs_tag(self.tag.item, self.attributes)
+        writer.write(itemtag[0],trace='list item open')
+        # Write ItemText.
+        text = reader.read_until(lists.terminators)
+        if self.text:
+            text = [self.text] + list(text)
+        if text:
+            writer.write_tag(self.tag.text, text, self.presubs, self.attributes,trace='list text')
+        # Process explicit and implicit list item continuations.
+        while True:
+            continuation = reader.read_next() == '+'
+            if continuation: reader.read()  # Discard continuation line.
+            while Lex.next() in (BlockTitle,AttributeList):
+                # Consume continued element title and attributes.
+                Lex.next().translate()
+            if not continuation and BlockTitle.title:
+                # Titled elements terminate the list.
+                break
+            next = Lex.next()
+            if next in lists.open:
+                break
+            elif isinstance(next,List):
+                next.translate()
+            elif isinstance(next,Paragraph) and 'listelement' in next.options:
+                next.translate()
+            elif continuation:
+                # This is where continued elements are processed.
+                if next is Title:
+                    message.error('section title not allowed in list item',halt=True)
+                next.translate()
+            else:
+                break
+        writer.write(itemtag[1],trace='list item close')
+
+    @staticmethod
+    def calc_style(index):
+        """Return the numbered list style ('arabic'...) of the list item index.
+        Return None if unrecognized style."""
+        if re.match(r'^\d+[\.>]$', index):
+            style = 'arabic'
+        elif re.match(r'^[ivx]+\)$', index):
+            style = 'lowerroman'
+        elif re.match(r'^[IVX]+\)$', index):
+            style = 'upperroman'
+        elif re.match(r'^[a-z]\.$', index):
+            style = 'loweralpha'
+        elif re.match(r'^[A-Z]\.$', index):
+            style = 'upperalpha'
+        else:
+            assert False
+        return style
+
+    @staticmethod
+    def calc_index(index,style):
+        """Return the ordinal number of (1...) of the list item index
+        for the given list style."""
+        def roman_to_int(roman):
+            roman = roman.lower()
+            digits = {'i':1,'v':5,'x':10}
+            result = 0
+            for i in range(len(roman)):
+                digit = digits[roman[i]]
+                # If next digit is larger this digit is negative.
+                if i+1 < len(roman) and digits[roman[i+1]] > digit:
+                    result -= digit
+                else:
+                    result += digit
+            return result
+        index = index[:-1]
+        if style == 'arabic':
+            ordinal = int(index)
+        elif style == 'lowerroman':
+            ordinal = roman_to_int(index)
+        elif style == 'upperroman':
+            ordinal = roman_to_int(index)
+        elif style == 'loweralpha':
+            ordinal = ord(index) - ord('a') + 1
+        elif style == 'upperalpha':
+            ordinal = ord(index) - ord('A') + 1
+        else:
+            assert False
+        return ordinal
+
+    def check_index(self):
+        """Check calculated self.ordinal (1,2,...) against the item number
+        in the document (self.index) and check the number style is the same as
+        the first item (self.number_style)."""
+        assert self.type in ('numbered','callout')
+        if self.index:
+            style = self.calc_style(self.index)
+            if style != self.number_style:
+                message.warning('list item style: expected %s got %s' %
+                        (self.number_style,style), offset=1)
+            ordinal = self.calc_index(self.index,style)
+            if ordinal != self.ordinal:
+                message.warning('list item index: expected %s got %s' %
+                        (self.ordinal,ordinal), offset=1)
+
+    def check_tags(self):
+        """ Check that all necessary tags are present. """
+        tags = set(Lists.TAGS)
+        if self.type != 'labeled':
+            tags = tags.difference(['entry','label','term'])
+        missing = tags.difference(self.tag.keys())
+        if missing:
+            self.error('missing tag(s): %s' % ','.join(missing), halt=True)
+    def translate(self):
+        AbstractBlock.translate(self)
+        if self.short_name() in ('bibliography','glossary','qanda'):
+            message.deprecated('old %s list syntax' % self.short_name())
+        lists.open.append(self)
+        attrs = self.mo.groupdict().copy()
+        for k in ('label','text','index'):
+            if k in attrs: del attrs[k]
+        if self.index:
+            # Set the numbering style from first list item.
+            attrs['style'] = self.calc_style(self.index)
+        BlockTitle.consume(attrs)
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs,['tags'])
+        if self.type in ('numbered','callout'):
+            self.number_style = self.attributes.get('style')
+            if self.number_style not in self.NUMBER_STYLES:
+                message.error('illegal numbered list style: %s' % self.number_style)
+                # Fall back to default style.
+                self.attributes['style'] = self.number_style = self.style
+        self.tag = lists.tags[self.parameters.tags]
+        self.check_tags()
+        if 'width' in self.attributes:
+            # Set horizontal list 'labelwidth' and 'itemwidth' attributes.
+            v = str(self.attributes['width'])
+            mo = re.match(r'^(\d{1,2})%?$',v)
+            if mo:
+                labelwidth = int(mo.group(1))
+                self.attributes['labelwidth'] = str(labelwidth)
+                self.attributes['itemwidth'] = str(100-labelwidth)
+            else:
+                self.error('illegal attribute value: width="%s"' % v)
+        stag,etag = subs_tag(self.tag.list, self.attributes)
+        if stag:
+            writer.write(stag,trace='list open')
+        self.ordinal = 0
+        # Process list till list syntax changes or there is a new title.
+        while Lex.next() is self and not BlockTitle.title:
+            self.ordinal += 1
+            document.attributes['listindex'] = str(self.ordinal)
+            if self.type in ('numbered','callout'):
+                self.check_index()
+            if self.type in ('bulleted','numbered','callout'):
+                reader.read()   # Discard (already parsed item first line).
+                self.translate_item()
+            elif self.type == 'labeled':
+                self.translate_entry()
+            else:
+                raise AssertionError,'illegal [%s] list type' % self.name
+        if etag:
+            writer.write(etag,trace='list close')
+        if self.type == 'callout':
+            calloutmap.validate(self.ordinal)
+            calloutmap.listclose()
+        lists.open.pop()
+        if len(lists.open):
+            document.attributes['listindex'] = str(lists.open[-1].ordinal)
+
+class Lists(AbstractBlocks):
+    """List of List objects."""
+    BLOCK_TYPE = List
+    PREFIX = 'listdef-'
+    TYPES = ('bulleted','numbered','labeled','callout')
+    TAGS = ('list', 'entry','item','text', 'label','term')
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        self.open = []  # A stack of the current and parent lists.
+        self.tags={}    # List tags dictionary. Each entry is a tags AttrDict.
+        self.terminators=None    # List of compiled re's.
+    def initialize(self):
+        self.terminators = [
+                re.compile(r'^\+$|^$'),
+                re.compile(AttributeList.pattern),
+                re.compile(lists.delimiters),
+                re.compile(blocks.delimiters),
+                re.compile(tables.delimiters),
+                re.compile(tables_OLD.delimiters),
+            ]
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+        self.load_tags(sections)
+    def load_tags(self,sections):
+        """
+        Load listtags-* conf file sections to self.tags.
+        """
+        for section in sections.keys():
+            mo = re.match(r'^listtags-(?P<name>\w+)$',section)
+            if mo:
+                name = mo.group('name')
+                if name in self.tags:
+                    d = self.tags[name]
+                else:
+                    d = AttrDict()
+                parse_entries(sections.get(section,()),d)
+                for k in d.keys():
+                    if k not in self.TAGS:
+                        message.warning('[%s] contains illegal list tag: %s' %
+                                (section,k))
+                self.tags[name] = d
+    def validate(self):
+        AbstractBlocks.validate(self)
+        for b in self.blocks:
+            # Check list has valid type.
+            if not b.type in Lists.TYPES:
+                raise EAsciiDoc,'[%s] illegal type' % b.name
+            b.validate()
+    def dump(self):
+        AbstractBlocks.dump(self)
+        for k,v in self.tags.items():
+            dump_section('listtags-'+k, v)
+
+
+class DelimitedBlock(AbstractBlock):
+    def __init__(self):
+        AbstractBlock.__init__(self)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('')
+    def isnext(self):
+        return AbstractBlock.isnext(self)
+    def translate(self):
+        AbstractBlock.translate(self)
+        reader.read()   # Discard delimiter.
+        attrs = {}
+        if self.short_name() != 'comment':
+            BlockTitle.consume(attrs)
+            AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        options = self.parameters.options
+        if 'skip' in options:
+            reader.read_until(self.delimiter,same_file=True)
+        elif safe() and self.name == 'blockdef-backend':
+            message.unsafe('Backend Block')
+            reader.read_until(self.delimiter,same_file=True)
+        else:
+            template = self.parameters.template
+            template = subs_attrs(template,attrs)
+            name = self.short_name()+' block'
+            if 'sectionbody' in options:
+                # The body is treated like a section body.
+                stag,etag = config.section2tags(template,self.attributes)
+                writer.write(stag,trace=name+' open')
+                Section.translate_body(self)
+                writer.write(etag,trace=name+' close')
+            else:
+                stag = config.section2tags(template,self.attributes,skipend=True)[0]
+                body = reader.read_until(self.delimiter,same_file=True)
+                presubs = self.parameters.presubs
+                postsubs = self.parameters.postsubs
+                body = Lex.subs(body,presubs)
+                if self.parameters.filter:
+                    body = filter_lines(self.parameters.filter,body,self.attributes)
+                body = Lex.subs(body,postsubs)
+                # Write start tag, content, end tag.
+                etag = config.section2tags(template,self.attributes,skipstart=True)[1]
+                writer.write(dovetail_tags(stag,body,etag),trace=name)
+            trace(self.short_name()+' block close',etag)
+        if reader.eof():
+            self.error('missing closing delimiter',self.start)
+        else:
+            delimiter = reader.read()   # Discard delimiter line.
+            assert re.match(self.delimiter,delimiter)
+
+class DelimitedBlocks(AbstractBlocks):
+    """List of delimited blocks."""
+    BLOCK_TYPE = DelimitedBlock
+    PREFIX = 'blockdef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+    def load(self,sections):
+        """Update blocks defined in 'sections' dictionary."""
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        AbstractBlocks.validate(self)
+
+class Column:
+    """Table column."""
+    def __init__(self, width=None, align_spec=None, style=None):
+        self.width = width or '1'
+        self.halign, self.valign = Table.parse_align_spec(align_spec)
+        self.style = style      # Style name or None.
+        # Calculated attribute values.
+        self.abswidth = None    # 1..   (page units).
+        self.pcwidth = None     # 1..99 (percentage).
+
+class Cell:
+    def __init__(self, data, span_spec=None, align_spec=None, style=None):
+        self.data = data
+        self.span, self.vspan = Table.parse_span_spec(span_spec)
+        self.halign, self.valign = Table.parse_align_spec(align_spec)
+        self.style = style
+    def __repr__(self):
+        return '<Cell: %d.%d %s.%s %s "%s">' % (
+                self.span, self.vspan,
+                self.halign, self.valign,
+                self.style or '',
+                self.data)
+
+class Table(AbstractBlock):
+    ALIGN = {'<':'left', '>':'right', '^':'center'}
+    VALIGN = {'<':'top', '>':'bottom', '^':'middle'}
+    FORMATS = ('psv','csv','dsv')
+    SEPARATORS = dict(
+        csv=',',
+        dsv=r':|\n',
+        # The count and align group matches are not exact.
+        psv=r'((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<style>[a-z])?)?\|'
+    )
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('format','tags','separator')
+        # tabledef conf file parameters.
+        self.format='psv'
+        self.separator=None
+        self.tags=None          # Name of tabletags-<tags> conf section.
+        # Calculated parameters.
+        self.abswidth=None      # 1..   (page units).
+        self.pcwidth = None     # 1..99 (percentage).
+        self.rows=[]            # Parsed rows, each row is a list of Cells.
+        self.columns=[]         # List of Columns.
+    @staticmethod
+    def parse_align_spec(align_spec):
+        """
+        Parse AsciiDoc cell alignment specifier and return 2-tuple with
+        horizonatal and vertical alignment names. Unspecified alignments
+        set to None.
+        """
+        result = (None, None)
+        if align_spec:
+            mo = re.match(r'^([<\^>])?(\.([<\^>]))?$', align_spec)
+            if mo:
+                result = (Table.ALIGN.get(mo.group(1)),
+                          Table.VALIGN.get(mo.group(3)))
+        return result
+    @staticmethod
+    def parse_span_spec(span_spec):
+        """
+        Parse AsciiDoc cell span specifier and return 2-tuple with horizonatal
+        and vertical span counts. Set default values (1,1) if not
+        specified.
+        """
+        result = (None, None)
+        if span_spec:
+            mo = re.match(r'^(\d+)?(\.(\d+))?$', span_spec)
+            if mo:
+                result = (mo.group(1) and int(mo.group(1)),
+                          mo.group(3) and int(mo.group(3)))
+        return (result[0] or 1, result[1] or 1)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('format='+self.format)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        if self.format not in Table.FORMATS:
+            self.error('illegal format=%s' % self.format,halt=True)
+        self.tags = self.tags or 'default'
+        tags = [self.tags]
+        tags += [s['tags'] for s in self.styles.values() if 'tags' in s]
+        for t in tags:
+            if t not in tables.tags:
+                self.error('missing section: [tabletags-%s]' % t,halt=True)
+        if self.separator:
+            # Evaluate escape characters.
+            self.separator = eval('"'+self.separator+'"')
+        #TODO: Move to class Tables
+        # Check global table parameters.
+        elif config.pagewidth is None:
+            self.error('missing [miscellaneous] entry: pagewidth')
+        elif config.pageunits is None:
+            self.error('missing [miscellaneous] entry: pageunits')
+    def validate_attributes(self):
+        """Validate and parse table attributes."""
+        # Set defaults.
+        format = self.format
+        tags = self.tags
+        separator = self.separator
+        abswidth = float(config.pagewidth)
+        pcwidth = 100.0
+        for k,v in self.attributes.items():
+            if k == 'format':
+                if v not in self.FORMATS:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    format = v
+            elif k == 'tags':
+                if v not in tables.tags:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    tags = v
+            elif k == 'separator':
+                separator = v
+            elif k == 'width':
+                if not re.match(r'^\d{1,3}%$',v) or int(v[:-1]) > 100:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    abswidth = float(v[:-1])/100 * config.pagewidth
+                    pcwidth = float(v[:-1])
+        # Calculate separator if it has not been specified.
+        if not separator:
+            separator = Table.SEPARATORS[format]
+        if format == 'csv':
+            if len(separator) > 1:
+                self.error('illegal csv separator=%s' % separator)
+                separator = ','
+        else:
+            if not is_re(separator):
+                self.error('illegal regular expression: separator=%s' %
+                        separator)
+        self.parameters.format = format
+        self.parameters.tags = tags
+        self.parameters.separator = separator
+        self.abswidth = abswidth
+        self.pcwidth = pcwidth
+    def get_tags(self,params):
+        tags = self.get_param('tags',params)
+        assert(tags and tags in tables.tags)
+        return tables.tags[tags]
+    def get_style(self,prefix):
+        """
+        Return the style dictionary whose name starts with 'prefix'.
+        """
+        if prefix is None:
+            return None
+        names = self.styles.keys()
+        names.sort()
+        for name in names:
+            if name.startswith(prefix):
+                return self.styles[name]
+        else:
+            self.error('missing style: %s*' % prefix)
+            return None
+    def parse_cols(self, cols, halign, valign):
+        """
+        Build list of column objects from table 'cols', 'halign' and 'valign'
+        attributes.
+        """
+        # [<multiplier>*][<align>][<width>][<style>]
+        COLS_RE1 = r'^((?P<count>\d+)\*)?(?P<align>[<\^>.]{,3})?(?P<width>\d+%?)?(?P<style>[a-z]\w*)?$'
+        # [<multiplier>*][<width>][<align>][<style>]
+        COLS_RE2 = r'^((?P<count>\d+)\*)?(?P<width>\d+%?)?(?P<align>[<\^>.]{,3})?(?P<style>[a-z]\w*)?$'
+        reo1 = re.compile(COLS_RE1)
+        reo2 = re.compile(COLS_RE2)
+        cols = str(cols)
+        if re.match(r'^\d+$',cols):
+            for i in range(int(cols)):
+                self.columns.append(Column())
+        else:
+            for col in re.split(r'\s*,\s*',cols):
+                mo = reo1.match(col)
+                if not mo:
+                    mo = reo2.match(col)
+                if mo:
+                    count = int(mo.groupdict().get('count') or 1)
+                    for i in range(count):
+                        self.columns.append(
+                            Column(mo.group('width'), mo.group('align'),
+                                   self.get_style(mo.group('style')))
+                        )
+                else:
+                    self.error('illegal column spec: %s' % col,self.start)
+        # Set column (and indirectly cell) default alignments.
+        for col in self.columns:
+            col.halign = col.halign or halign or document.attributes.get('halign') or 'left'
+            col.valign = col.valign or valign or document.attributes.get('valign') or 'top'
+        # Validate widths and calculate missing widths.
+        n = 0; percents = 0; props = 0
+        for col in self.columns:
+            if col.width:
+                if col.width[-1] == '%': percents += int(col.width[:-1])
+                else: props += int(col.width)
+                n += 1
+        if percents > 0 and props > 0:
+            self.error('mixed percent and proportional widths: %s'
+                    % cols,self.start)
+        pcunits = percents > 0
+        # Fill in missing widths.
+        if n < len(self.columns) and percents < 100:
+            if pcunits:
+                width = float(100 - percents)/float(len(self.columns) - n)
+            else:
+                width = 1
+            for col in self.columns:
+                if not col.width:
+                    if pcunits:
+                        col.width = str(int(width))+'%'
+                        percents += width
+                    else:
+                        col.width = str(width)
+                        props += width
+        # Calculate column alignment and absolute and percent width values.
+        percents = 0
+        for col in self.columns:
+            if pcunits:
+                col.pcwidth = float(col.width[:-1])
+            else:
+                col.pcwidth = (float(col.width)/props)*100
+            col.abswidth = self.abswidth * (col.pcwidth/100)
+            if config.pageunits in ('cm','mm','in','em'):
+                col.abswidth = '%.2f' % round(col.abswidth,2)
+            else:
+                col.abswidth = '%d' % round(col.abswidth)
+            percents += col.pcwidth
+            col.pcwidth = int(col.pcwidth)
+        if round(percents) > 100:
+            self.error('total width exceeds 100%%: %s' % cols,self.start)
+        elif round(percents) < 100:
+            self.error('total width less than 100%%: %s' % cols,self.start)
+    def build_colspecs(self):
+        """
+        Generate column related substitution attributes.
+        """
+        cols = []
+        i = 1
+        for col in self.columns:
+            colspec = self.get_tags(col.style).colspec
+            if colspec:
+                self.attributes['halign'] = col.halign
+                self.attributes['valign'] = col.valign
+                self.attributes['colabswidth'] = col.abswidth
+                self.attributes['colpcwidth'] = col.pcwidth
+                self.attributes['colnumber'] = str(i)
+                s = subs_attrs(colspec, self.attributes)
+                if not s:
+                    message.warning('colspec dropped: contains undefined attribute')
+                else:
+                    cols.append(s)
+            i += 1
+        if cols:
+            self.attributes['colspecs'] = writer.newline.join(cols)
+    def parse_rows(self, text):
+        """
+        Parse the table source text into self.rows (a list of rows, each row
+        is a list of Cells.
+        """
+        reserved = {}  # Cols reserved by rowspans (indexed by row number).
+        if self.parameters.format in ('psv','dsv'):
+            ri = 0  # Current row index 0..
+            cells = self.parse_psv_dsv(text)
+            row = []
+            ci = 0  # Column counter 0..colcount
+            for cell in cells:
+                colcount = len(self.columns) - reserved.get(ri,0)
+                if cell.vspan > 1:
+                    # Reserve spanned columns from ensuing rows.
+                    for i in range(1, cell.vspan):
+                        reserved[ri+i] = reserved.get(ri+i, 0) + cell.span
+                ci += cell.span
+                if ci <= colcount:
+                    row.append(cell)
+                if ci >= colcount:
+                    self.rows.append(row)
+                    ri += 1
+                    row = []
+                    ci = 0
+                if ci > colcount:
+                    message.warning('table row %d: span exceeds number of columns'
+                            % ri)
+        elif self.parameters.format == 'csv':
+            self.rows = self.parse_csv(text)
+        else:
+            assert True,'illegal table format'
+        # Check that all row spans match.
+        for ri,row in enumerate(self.rows):
+            row_span = 0
+            for cell in row:
+                row_span += cell.span
+            row_span += reserved.get(ri,0)
+            if ri == 0:
+                header_span = row_span
+            if row_span < header_span:
+                message.warning('table row %d: does not span all columns' % (ri+1))
+            if row_span > header_span:
+                message.warning('table row %d: exceeds columns span' % (ri+1))
+        # Check that now row spans exceed the number of rows.
+        if len([x for x in reserved.keys() if x >= len(self.rows)]) > 0:
+            message.warning('one or more cell spans exceed the available rows')
+    def subs_rows(self, rows, rowtype='body'):
+        """
+        Return a string of output markup from a list of rows, each row
+        is a list of raw data text.
+        """
+        tags = tables.tags[self.parameters.tags]
+        if rowtype == 'header':
+            rtag = tags.headrow
+        elif rowtype == 'footer':
+            rtag = tags.footrow
+        else:
+            rtag = tags.bodyrow
+        result = []
+        stag,etag = subs_tag(rtag,self.attributes)
+        for row in rows:
+            result.append(stag)
+            result += self.subs_row(row,rowtype)
+            result.append(etag)
+        return writer.newline.join(result)
+    def subs_row(self, row, rowtype):
+        """
+        Substitute the list of Cells using the data tag.
+        Returns a list of marked up table cell elements.
+        """
+        result = []
+        i = 0
+        for cell in row:
+            if i >= len(self.columns):
+                break   # Skip cells outside the header width.
+            col = self.columns[i]
+            self.attributes['halign'] = cell.halign or col.halign
+            self.attributes['valign'] = cell.valign or  col.valign
+            self.attributes['colabswidth'] = col.abswidth
+            self.attributes['colpcwidth'] = col.pcwidth
+            self.attributes['colnumber'] = str(i+1)
+            self.attributes['colspan'] = str(cell.span)
+            self.attributes['colstart'] = self.attributes['colnumber']
+            self.attributes['colend'] = str(i+cell.span)
+            self.attributes['rowspan'] = str(cell.vspan)
+            self.attributes['morerows'] = str(cell.vspan-1)
+            # Fill missing column data with blanks.
+            if i > len(self.columns) - 1:
+                data = ''
+            else:
+                data = cell.data
+            if rowtype == 'header':
+                # Use table style unless overriden by cell style.
+                colstyle = cell.style
+            else:
+                # If the cell style is not defined use the column style.
+                colstyle = cell.style or col.style
+            tags = self.get_tags(colstyle)
+            presubs,postsubs = self.get_subs(colstyle)
+            data = [data]
+            data = Lex.subs(data, presubs)
+            data = filter_lines(self.get_param('filter',colstyle),
+                                data, self.attributes)
+            data = Lex.subs(data, postsubs)
+            if rowtype != 'header':
+                ptag = tags.paragraph
+                if ptag:
+                    stag,etag = subs_tag(ptag,self.attributes)
+                    text = '\n'.join(data).strip()
+                    data = []
+                    for para in re.split(r'\n{2,}',text):
+                        data += dovetail_tags([stag],para.split('\n'),[etag])
+            if rowtype == 'header':
+                dtag = tags.headdata
+            elif rowtype == 'footer':
+                dtag = tags.footdata
+            else:
+                dtag = tags.bodydata
+            stag,etag = subs_tag(dtag,self.attributes)
+            result = result + dovetail_tags([stag],data,[etag])
+            i += cell.span
+        return result
+    def parse_csv(self,text):
+        """
+        Parse the table source text and return a list of rows, each row
+        is a list of Cells.
+        """
+        import StringIO
+        import csv
+        rows = []
+        rdr = csv.reader(StringIO.StringIO('\r\n'.join(text)),
+                     delimiter=self.parameters.separator, skipinitialspace=True)
+        try:
+            for row in rdr:
+                rows.append([Cell(data) for data in row])
+        except Exception:
+            self.error('csv parse error: %s' % row)
+        return rows
+    def parse_psv_dsv(self,text):
+        """
+        Parse list of PSV or DSV table source text lines and return a list of
+        Cells.
+        """
+        def append_cell(data, span_spec, op, align_spec, style):
+            op = op or '+'
+            if op == '*':   # Cell multiplier.
+                span = Table.parse_span_spec(span_spec)[0]
+                for i in range(span):
+                    cells.append(Cell(data, '1', align_spec, style))
+            elif op == '+': # Column spanner.
+                cells.append(Cell(data, span_spec, align_spec, style))
+            else:
+                self.error('illegal table cell operator')
+        text = '\n'.join(text)
+        separator = '(?msu)'+self.parameters.separator
+        format = self.parameters.format
+        start = 0
+        span = None
+        op = None
+        align = None
+        style = None
+        cells = []
+        data = ''
+        for mo in re.finditer(separator,text):
+            data += text[start:mo.start()]
+            if data.endswith('\\'):
+                data = data[:-1]+mo.group() # Reinstate escaped separators.
+            else:
+                append_cell(data, span, op, align, style)
+                span = mo.groupdict().get('span')
+                op = mo.groupdict().get('op')
+                align = mo.groupdict().get('align')
+                style = mo.groupdict().get('style')
+                if style:
+                    style = self.get_style(style)
+                data = ''
+            start = mo.end()
+        # Last cell follows final separator.
+        data += text[start:]
+        append_cell(data, span, op, align, style)
+        # We expect a dummy blank item preceeding first PSV cell.
+        if format == 'psv':
+            if cells[0].data.strip() != '':
+                self.error('missing leading separator: %s' % separator,
+                        self.start)
+            else:
+                cells.pop(0)
+        return cells
+    def translate(self):
+        AbstractBlock.translate(self)
+        reader.read()   # Discard delimiter.
+        # Reset instance specific properties.
+        self.columns = []
+        self.rows = []
+        attrs = {}
+        BlockTitle.consume(attrs)
+        # Mix in document attribute list.
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        self.validate_attributes()
+        # Add global and calculated configuration parameters.
+        self.attributes['pagewidth'] = config.pagewidth
+        self.attributes['pageunits'] = config.pageunits
+        self.attributes['tableabswidth'] = int(self.abswidth)
+        self.attributes['tablepcwidth'] = int(self.pcwidth)
+        # Read the entire table.
+        text = reader.read_until(self.delimiter)
+        if reader.eof():
+            self.error('missing closing delimiter',self.start)
+        else:
+            delimiter = reader.read()   # Discard closing delimiter.
+            assert re.match(self.delimiter,delimiter)
+        if len(text) == 0:
+            message.warning('[%s] table is empty' % self.name)
+            return
+        cols = attrs.get('cols')
+        if not cols:
+            # Calculate column count from number of items in first line.
+            if self.parameters.format == 'csv':
+                cols = text[0].count(self.parameters.separator) + 1
+            else:
+                cols = 0
+                for cell in self.parse_psv_dsv(text[:1]):
+                    cols += cell.span
+        self.parse_cols(cols, attrs.get('halign'), attrs.get('valign'))
+        # Set calculated attributes.
+        self.attributes['colcount'] = len(self.columns)
+        self.build_colspecs()
+        self.parse_rows(text)
+        # The 'rowcount' attribute is used by the experimental LaTeX backend.
+        self.attributes['rowcount'] = str(len(self.rows))
+        # Generate headrows, footrows, bodyrows.
+        # Headrow, footrow and bodyrow data replaces same named attributes in
+        # the table markup template. In order to ensure this data does not get
+        # a second attribute substitution (which would interfere with any
+        # already substituted inline passthroughs) unique placeholders are used
+        # (the tab character does not appear elsewhere since it is expanded on
+        # input) which are replaced after template attribute substitution.
+        headrows = footrows = bodyrows = None
+        if self.rows and 'header' in self.parameters.options:
+            headrows = self.subs_rows(self.rows[0:1],'header')
+            self.attributes['headrows'] = '\x07headrows\x07'
+            self.rows = self.rows[1:]
+        if self.rows and 'footer' in self.parameters.options:
+            footrows = self.subs_rows( self.rows[-1:], 'footer')
+            self.attributes['footrows'] = '\x07footrows\x07'
+            self.rows = self.rows[:-1]
+        if self.rows:
+            bodyrows = self.subs_rows(self.rows)
+            self.attributes['bodyrows'] = '\x07bodyrows\x07'
+        table = subs_attrs(config.sections[self.parameters.template],
+                           self.attributes)
+        table = writer.newline.join(table)
+        # Before we finish replace the table head, foot and body place holders
+        # with the real data.
+        if headrows:
+            table = table.replace('\x07headrows\x07', headrows, 1)
+        if footrows:
+            table = table.replace('\x07footrows\x07', footrows, 1)
+        if bodyrows:
+            table = table.replace('\x07bodyrows\x07', bodyrows, 1)
+        writer.write(table,trace='table')
+
+class Tables(AbstractBlocks):
+    """List of tables."""
+    BLOCK_TYPE = Table
+    PREFIX = 'tabledef-'
+    TAGS = ('colspec', 'headrow','footrow','bodyrow',
+            'headdata','footdata', 'bodydata','paragraph')
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        # Table tags dictionary. Each entry is a tags dictionary.
+        self.tags={}
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+        self.load_tags(sections)
+    def load_tags(self,sections):
+        """
+        Load tabletags-* conf file sections to self.tags.
+        """
+        for section in sections.keys():
+            mo = re.match(r'^tabletags-(?P<name>\w+)$',section)
+            if mo:
+                name = mo.group('name')
+                if name in self.tags:
+                    d = self.tags[name]
+                else:
+                    d = AttrDict()
+                parse_entries(sections.get(section,()),d)
+                for k in d.keys():
+                    if k not in self.TAGS:
+                        message.warning('[%s] contains illegal table tag: %s' %
+                                (section,k))
+                self.tags[name] = d
+    def validate(self):
+        AbstractBlocks.validate(self)
+        # Check we have a default table definition,
+        for i in range(len(self.blocks)):
+            if self.blocks[i].name == 'tabledef-default':
+                default = self.blocks[i]
+                break
+        else:
+            raise EAsciiDoc,'missing section: [tabledef-default]'
+        # Propagate defaults to unspecified table parameters.
+        for b in self.blocks:
+            if b is not default:
+                if b.format is None: b.format = default.format
+                if b.template is None: b.template = default.template
+        # Check tags and propagate default tags.
+        if not 'default' in self.tags:
+            raise EAsciiDoc,'missing section: [tabletags-default]'
+        default = self.tags['default']
+        for tag in ('bodyrow','bodydata','paragraph'): # Mandatory default tags.
+            if tag not in default:
+                raise EAsciiDoc,'missing [tabletags-default] entry: %s' % tag
+        for t in self.tags.values():
+            if t is not default:
+                if t.colspec is None: t.colspec = default.colspec
+                if t.headrow is None: t.headrow = default.headrow
+                if t.footrow is None: t.footrow = default.footrow
+                if t.bodyrow is None: t.bodyrow = default.bodyrow
+                if t.headdata is None: t.headdata = default.headdata
+                if t.footdata is None: t.footdata = default.footdata
+                if t.bodydata is None: t.bodydata = default.bodydata
+                if t.paragraph is None: t.paragraph = default.paragraph
+        # Use body tags if header and footer tags are not specified.
+        for t in self.tags.values():
+            if not t.headrow: t.headrow = t.bodyrow
+            if not t.footrow: t.footrow = t.bodyrow
+            if not t.headdata: t.headdata = t.bodydata
+            if not t.footdata: t.footdata = t.bodydata
+        # Check table definitions are valid.
+        for b in self.blocks:
+            b.validate()
+    def dump(self):
+        AbstractBlocks.dump(self)
+        for k,v in self.tags.items():
+            dump_section('tabletags-'+k, v)
+
+class Macros:
+    # Default system macro syntax.
+    SYS_RE = r'(?u)^(?P<name>[\\]?\w(\w|-)*?)::(?P<target>\S*?)' + \
+             r'(\[(?P<attrlist>.*?)\])$'
+    def __init__(self):
+        self.macros = []        # List of Macros.
+        self.current = None     # The last matched block macro.
+        self.passthroughs = []
+        # Initialize default system macro.
+        m = Macro()
+        m.pattern = self.SYS_RE
+        m.prefix = '+'
+        m.reo = re.compile(m.pattern)
+        self.macros.append(m)
+    def load(self,entries):
+        for entry in entries:
+            m = Macro()
+            m.load(entry)
+            if m.name is None:
+                # Delete undefined macro.
+                for i,m2 in enumerate(self.macros):
+                    if m2.pattern == m.pattern:
+                        del self.macros[i]
+                        break
+                else:
+                    message.warning('unable to delete missing macro: %s' % m.pattern)
+            else:
+                # Check for duplicates.
+                for m2 in self.macros:
+                    if m2.pattern == m.pattern:
+                        message.verbose('macro redefinition: %s%s' % (m.prefix,m.name))
+                        break
+                else:
+                    self.macros.append(m)
+    def dump(self):
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('[macros]')
+        # Dump all macros except the first (built-in system) macro.
+        for m in self.macros[1:]:
+            # Escape = in pattern.
+            macro = '%s=%s%s' % (m.pattern.replace('=',r'\='), m.prefix, m.name)
+            if m.subslist is not None:
+                macro += '[' + ','.join(m.subslist) + ']'
+            write(macro)
+        write('')
+    def validate(self):
+        # Check all named sections exist.
+        if config.verbose:
+            for m in self.macros:
+                if m.name and m.prefix != '+':
+                    m.section_name()
+    def subs(self,text,prefix='',callouts=False):
+        # If callouts is True then only callout macros are processed, if False
+        # then all non-callout macros are processed.
+        result = text
+        for m in self.macros:
+            if m.prefix == prefix:
+                if callouts ^ (m.name != 'callout'):
+                    result = m.subs(result)
+        return result
+    def isnext(self):
+        """Return matching macro if block macro is next on reader."""
+        reader.skip_blank_lines()
+        line = reader.read_next()
+        if line:
+            for m in self.macros:
+                if m.prefix == '#':
+                    if m.reo.match(line):
+                        self.current = m
+                        return m
+        return False
+    def match(self,prefix,name,text):
+        """Return re match object matching 'text' with macro type 'prefix',
+        macro name 'name'."""
+        for m in self.macros:
+            if m.prefix == prefix:
+                mo = m.reo.match(text)
+                if mo:
+                    if m.name == name:
+                        return mo
+                    if re.match(name,mo.group('name')):
+                        return mo
+        return None
+    def extract_passthroughs(self,text,prefix=''):
+        """ Extract the passthrough text and replace with temporary
+        placeholders."""
+        self.passthroughs = []
+        for m in self.macros:
+            if m.has_passthrough() and m.prefix == prefix:
+                text = m.subs_passthroughs(text, self.passthroughs)
+        return text
+    def restore_passthroughs(self,text):
+        """ Replace passthough placeholders with the original passthrough
+        text."""
+        for i,v in enumerate(self.passthroughs):
+            text = text.replace('\x07'+str(i)+'\x07', self.passthroughs[i])
+        return text
+
+class Macro:
+    def __init__(self):
+        self.pattern = None     # Matching regular expression.
+        self.name = ''          # Conf file macro name (None if implicit).
+        self.prefix = ''        # '' if inline, '+' if system, '#' if block.
+        self.reo = None         # Compiled pattern re object.
+        self.subslist = []      # Default subs for macros passtext group.
+    def has_passthrough(self):
+        return self.pattern.find(r'(?P<passtext>') >= 0
+    def section_name(self,name=None):
+        """Return macro markup template section name based on macro name and
+        prefix.  Return None section not found."""
+        assert self.prefix != '+'
+        if not name:
+            assert self.name
+            name = self.name
+        if self.prefix == '#':
+            suffix = '-blockmacro'
+        else:
+            suffix = '-inlinemacro'
+        if name+suffix in config.sections:
+            return name+suffix
+        else:
+            message.warning('missing macro section: [%s]' % (name+suffix))
+            return None
+    def load(self,entry):
+        e = parse_entry(entry)
+        if e is None:
+            # Only the macro pattern was specified, mark for deletion.
+            self.name = None
+            self.pattern = entry
+            return
+        if not is_re(e[0]):
+            raise EAsciiDoc,'illegal macro regular expression: %s' % e[0]
+        pattern, name = e
+        if name and name[0] in ('+','#'):
+            prefix, name = name[0], name[1:]
+        else:
+            prefix = ''
+        # Parse passthrough subslist.
+        mo = re.match(r'^(?P<name>[^[]*)(\[(?P<subslist>.*)\])?$', name)
+        name = mo.group('name')
+        if name and not is_name(name):
+            raise EAsciiDoc,'illegal section name in macro entry: %s' % entry
+        subslist = mo.group('subslist')
+        if subslist is not None:
+            # Parse and validate passthrough subs.
+            subslist = parse_options(subslist, SUBS_OPTIONS,
+                                 'illegal subs in macro entry: %s' % entry)
+        self.pattern = pattern
+        self.reo = re.compile(pattern)
+        self.prefix = prefix
+        self.name = name
+        self.subslist = subslist or []
+
+    def subs(self,text):
+        def subs_func(mo):
+            """Function called to perform macro substitution.
+            Uses matched macro regular expression object and returns string
+            containing the substituted macro body."""
+            # Check if macro reference is escaped.
+            if mo.group()[0] == '\\':
+                return mo.group()[1:]   # Strip leading backslash.
+            d = mo.groupdict()
+            # Delete groups that didn't participate in match.
+            for k,v in d.items():
+                if v is None: del d[k]
+            if self.name:
+                name = self.name
+            else:
+                if not 'name' in d:
+                    message.warning('missing macro name group: %s' % mo.re.pattern)
+                    return ''
+                name = d['name']
+            section_name = self.section_name(name)
+            if not section_name:
+                return ''
+            # If we're dealing with a block macro get optional block ID and
+            # block title.
+            if self.prefix == '#' and self.name != 'comment':
+                AttributeList.consume(d)
+                BlockTitle.consume(d)
+            # Parse macro attributes.
+            if 'attrlist' in d:
+                if d['attrlist'] in (None,''):
+                    del d['attrlist']
+                else:
+                    if self.prefix == '':
+                        # Unescape ] characters in inline macros.
+                        d['attrlist'] = d['attrlist'].replace('\\]',']')
+                    parse_attributes(d['attrlist'],d)
+                    # Generate option attributes.
+                    if 'options' in d:
+                        options = parse_options(d['options'], (),
+                                '%s: illegal option name' % name)
+                        for option in options:
+                            d[option+'-option'] = ''
+                    # Substitute single quoted attribute values in block macros.
+                    if self.prefix == '#':
+                        AttributeList.subs(d)
+            if name == 'callout':
+                listindex =int(d['index'])
+                d['coid'] = calloutmap.add(listindex)
+            # The alt attribute is the first image macro positional attribute.
+            if name == 'image' and '1' in d:
+                d['alt'] = d['1']
+            # Unescape special characters in LaTeX target file names.
+            if document.backend == 'latex' and 'target' in d and d['target']:
+                if not '0' in d:
+                    d['0'] = d['target']
+                d['target']= config.subs_specialchars_reverse(d['target'])
+            # BUG: We've already done attribute substitution on the macro which
+            # means that any escaped attribute references are now unescaped and
+            # will be substituted by config.subs_section() below. As a partial
+            # fix have withheld {0} from substitution but this kludge doesn't
+            # fix it for other attributes containing unescaped references.
+            # Passthrough macros don't have this problem.
+            a0 = d.get('0')
+            if a0:
+                d['0'] = chr(0)  # Replace temporarily with unused character.
+            body = config.subs_section(section_name,d)
+            if len(body) == 0:
+                result = ''
+            elif len(body) == 1:
+                result = body[0]
+            else:
+                if self.prefix == '#':
+                    result = writer.newline.join(body)
+                else:
+                    # Internally processed inline macros use UNIX line
+                    # separator.
+                    result = '\n'.join(body)
+            if a0:
+                result = result.replace(chr(0), a0)
+            return result
+
+        return self.reo.sub(subs_func, text)
+
+    def translate(self):
+        """ Block macro translation."""
+        assert self.prefix == '#'
+        s = reader.read()
+        before = s
+        if self.has_passthrough():
+            s = macros.extract_passthroughs(s,'#')
+        s = subs_attrs(s)
+        if s:
+            s = self.subs(s)
+            if self.has_passthrough():
+                s = macros.restore_passthroughs(s)
+            if s:
+                trace('macro block',before,s)
+                writer.write(s)
+
+    def subs_passthroughs(self, text, passthroughs):
+        """ Replace macro attribute lists in text with placeholders.
+        Substitute and append the passthrough attribute lists to the
+        passthroughs list."""
+        def subs_func(mo):
+            """Function called to perform inline macro substitution.
+            Uses matched macro regular expression object and returns string
+            containing the substituted macro body."""
+            # Don't process escaped macro references.
+            if mo.group()[0] == '\\':
+                return mo.group()
+            d = mo.groupdict()
+            if not 'passtext' in d:
+                message.warning('passthrough macro %s: missing passtext group' %
+                        d.get('name',''))
+                return mo.group()
+            passtext = d['passtext']
+            if re.search('\x07\\d+\x07', passtext):
+                message.warning('nested inline passthrough')
+                return mo.group()
+            if d.get('subslist'):
+                if d['subslist'].startswith(':'):
+                    message.error('block macro cannot occur here: %s' % mo.group(),
+                          halt=True)
+                subslist = parse_options(d['subslist'], SUBS_OPTIONS,
+                          'illegal passthrough macro subs option')
+            else:
+                subslist = self.subslist
+            passtext = Lex.subs_1(passtext,subslist)
+            if passtext is None: passtext = ''
+            if self.prefix == '':
+                # Unescape ] characters in inline macros.
+                passtext = passtext.replace('\\]',']')
+            passthroughs.append(passtext)
+            # Tabs guarantee the placeholders are unambiguous.
+            result = (
+                text[mo.start():mo.start('passtext')] +
+                '\x07' + str(len(passthroughs)-1) + '\x07' +
+                text[mo.end('passtext'):mo.end()]
+            )
+            return result
+
+        return self.reo.sub(subs_func, text)
+
+
+class CalloutMap:
+    def __init__(self):
+        self.comap = {}         # key = list index, value = callouts list.
+        self.calloutindex = 0   # Current callout index number.
+        self.listnumber = 1     # Current callout list number.
+    def listclose(self):
+        # Called when callout list is closed.
+        self.listnumber += 1
+        self.calloutindex = 0
+        self.comap = {}
+    def add(self,listindex):
+        # Add next callout index to listindex map entry. Return the callout id.
+        self.calloutindex += 1
+        # Append the coindex to a list in the comap dictionary.
+        if not listindex in self.comap:
+            self.comap[listindex] = [self.calloutindex]
+        else:
+            self.comap[listindex].append(self.calloutindex)
+        return self.calloutid(self.listnumber, self.calloutindex)
+    @staticmethod
+    def calloutid(listnumber,calloutindex):
+        return 'CO%d-%d' % (listnumber,calloutindex)
+    def calloutids(self,listindex):
+        # Retieve list of callout indexes that refer to listindex.
+        if listindex in self.comap:
+            result = ''
+            for coindex in self.comap[listindex]:
+                result += ' ' + self.calloutid(self.listnumber,coindex)
+            return result.strip()
+        else:
+            message.warning('no callouts refer to list item '+str(listindex))
+            return ''
+    def validate(self,maxlistindex):
+        # Check that all list indexes referenced by callouts exist.
+        for listindex in self.comap.keys():
+            if listindex > maxlistindex:
+                message.warning('callout refers to non-existent list item '
+                        + str(listindex))
+
+#---------------------------------------------------------------------------
+# Input stream Reader and output stream writer classes.
+#---------------------------------------------------------------------------
+
+UTF8_BOM = '\xef\xbb\xbf'
+
+class Reader1:
+    """Line oriented AsciiDoc input file reader. Processes include and
+    conditional inclusion system macros. Tabs are expanded and lines are right
+    trimmed."""
+    # This class is not used directly, use Reader class instead.
+    READ_BUFFER_MIN = 10        # Read buffer low level.
+    def __init__(self):
+        self.f = None           # Input file object.
+        self.fname = None       # Input file name.
+        self.next = []          # Read ahead buffer containing
+                                # [filename,linenumber,linetext] lists.
+        self.cursor = None      # Last read() [filename,linenumber,linetext].
+        self.tabsize = 8        # Tab expansion number of spaces.
+        self.parent = None      # Included reader's parent reader.
+        self._lineno = 0        # The last line read from file object f.
+        self.current_depth = 0  # Current include depth.
+        self.max_depth = 5      # Initial maxiumum allowed include depth.
+        self.bom = None         # Byte order mark (BOM).
+        self.infile = None      # Saved document 'infile' attribute.
+        self.indir = None       # Saved document 'indir' attribute.
+    def open(self,fname):
+        self.fname = fname
+        message.verbose('reading: '+fname)
+        if fname == '<stdin>':
+            self.f = sys.stdin
+            self.infile = None
+            self.indir = None
+        else:
+            self.f = open(fname,'rb')
+            self.infile = fname
+            self.indir = os.path.dirname(fname)
+        document.attributes['infile'] = self.infile
+        document.attributes['indir'] = self.indir
+        self._lineno = 0            # The last line read from file object f.
+        self.next = []
+        # Prefill buffer by reading the first line and then pushing it back.
+        if Reader1.read(self):
+            if self.cursor[2].startswith(UTF8_BOM):
+                self.cursor[2] = self.cursor[2][len(UTF8_BOM):]
+                self.bom = UTF8_BOM
+            self.unread(self.cursor)
+            self.cursor = None
+    def closefile(self):
+        """Used by class methods to close nested include files."""
+        self.f.close()
+        self.next = []
+    def close(self):
+        self.closefile()
+        self.__init__()
+    def read(self, skip=False):
+        """Read next line. Return None if EOF. Expand tabs. Strip trailing
+        white space. Maintain self.next read ahead buffer. If skip=True then
+        conditional exclusion is active (ifdef and ifndef macros)."""
+        # Top up buffer.
+        if len(self.next) <= self.READ_BUFFER_MIN:
+            s = self.f.readline()
+            if s:
+                self._lineno = self._lineno + 1
+            while s:
+                if self.tabsize != 0:
+                    s = s.expandtabs(self.tabsize)
+                s = s.rstrip()
+                self.next.append([self.fname,self._lineno,s])
+                if len(self.next) > self.READ_BUFFER_MIN:
+                    break
+                s = self.f.readline()
+                if s:
+                    self._lineno = self._lineno + 1
+        # Return first (oldest) buffer entry.
+        if len(self.next) > 0:
+            self.cursor = self.next[0]
+            del self.next[0]
+            result = self.cursor[2]
+            # Check for include macro.
+            mo = macros.match('+',r'include[1]?',result)
+            if mo and not skip:
+                # Don't process include macro once the maximum depth is reached.
+                if self.current_depth >= self.max_depth:
+                    return result
+                # Perform attribute substitution on include macro file name.
+                fname = subs_attrs(mo.group('target'))
+                if not fname:
+                    return Reader1.read(self)   # Return next input line.
+                if self.fname != '<stdin>':
+                    fname = os.path.expandvars(os.path.expanduser(fname))
+                    fname = safe_filename(fname, os.path.dirname(self.fname))
+                    if not fname:
+                        return Reader1.read(self)   # Return next input line.
+                    if mo.group('name') == 'include1':
+                        if not config.dumping:
+                            # Store the include file in memory for later
+                            # retrieval by the {include1:} system attribute.
+                            config.include1[fname] = [
+                                s.rstrip() for s in open(fname)]
+                            return '{include1:%s}' % fname
+                        else:
+                            # This is a configuration dump, just pass the macro
+                            # call through.
+                            return result
+                # Parse include macro attributes.
+                attrs = {}
+                parse_attributes(mo.group('attrlist'),attrs)
+                # Clone self and set as parent (self assumes the role of child).
+                parent = Reader1()
+                assign(parent,self)
+                self.parent = parent
+                # Set attributes in child.
+                if 'tabsize' in attrs:
+                    self.tabsize = int(validate(attrs['tabsize'],
+                        'int($)>=0',
+                        'illegal include macro tabsize argument'))
+                else:
+                    self.tabsize = config.tabsize
+                if 'depth' in attrs:
+                    attrs['depth'] = int(validate(attrs['depth'],
+                        'int($)>=1',
+                        'illegal include macro depth argument'))
+                    self.max_depth = self.current_depth + attrs['depth']
+                # Process included file.
+                self.open(fname)
+                self.current_depth = self.current_depth + 1
+                result = Reader1.read(self)
+        else:
+            if not Reader1.eof(self):
+                result = Reader1.read(self)
+            else:
+                result = None
+        return result
+    def eof(self):
+        """Returns True if all lines have been read."""
+        if len(self.next) == 0:
+            # End of current file.
+            if self.parent:
+                self.closefile()
+                assign(self,self.parent)    # Restore parent reader.
+                document.attributes['infile'] = self.infile
+                document.attributes['indir'] = self.indir
+                return Reader1.eof(self)
+            else:
+                return True
+        else:
+            return False
+    def read_next(self):
+        """Like read() but does not advance file pointer."""
+        if Reader1.eof(self):
+            return None
+        else:
+            return self.next[0][2]
+    def unread(self,cursor):
+        """Push the line (filename,linenumber,linetext) tuple back into the read
+        buffer. Note that it's up to the caller to restore the previous
+        cursor."""
+        assert cursor
+        self.next.insert(0,cursor)
+
+class Reader(Reader1):
+    """ Wraps (well, sought of) Reader1 class and implements conditional text
+    inclusion."""
+    def __init__(self):
+        Reader1.__init__(self)
+        self.depth = 0          # if nesting depth.
+        self.skip = False       # true if we're skipping ifdef...endif.
+        self.skipname = ''      # Name of current endif macro target.
+        self.skipto = -1        # The depth at which skipping is reenabled.
+    def read_super(self):
+        result = Reader1.read(self,self.skip)
+        if result is None and self.skip:
+            raise EAsciiDoc,'missing endif::%s[]' % self.skipname
+        return result
+    def read(self):
+        result = self.read_super()
+        if result is None:
+            return None
+        while self.skip:
+            mo = macros.match('+',r'ifdef|ifndef|ifeval|endif',result)
+            if mo:
+                name = mo.group('name')
+                target = mo.group('target')
+                attrlist = mo.group('attrlist')
+                if name == 'endif':
+                    self.depth -= 1
+                    if self.depth < 0:
+                        raise EAsciiDoc,'mismatched macro: %s' % result
+                    if self.depth == self.skipto:
+                        self.skip = False
+                        if target and self.skipname != target:
+                            raise EAsciiDoc,'mismatched macro: %s' % result
+                else:
+                    if name in ('ifdef','ifndef'):
+                        if not target:
+                            raise EAsciiDoc,'missing macro target: %s' % result
+                        if not attrlist:
+                            self.depth += 1
+                    elif name == 'ifeval':
+                        if not attrlist:
+                            raise EAsciiDoc,'missing ifeval condition: %s' % result
+                        self.depth += 1
+            result = self.read_super()
+            if result is None:
+                return None
+        mo = macros.match('+',r'ifdef|ifndef|ifeval|endif',result)
+        if mo:
+            name = mo.group('name')
+            target = mo.group('target')
+            attrlist = mo.group('attrlist')
+            if name == 'endif':
+                self.depth = self.depth-1
+            else:
+                if not target and name in ('ifdef','ifndef'):
+                    raise EAsciiDoc,'missing macro target: %s' % result
+                defined = is_attr_defined(target, document.attributes)
+                if name == 'ifdef':
+                    if attrlist:
+                        if defined: return attrlist
+                    else:
+                        self.skip = not defined
+                elif name == 'ifndef':
+                    if attrlist:
+                        if not defined: return attrlist
+                    else:
+                        self.skip = defined
+                elif name == 'ifeval':
+                    if not attrlist:
+                        raise EAsciiDoc,'missing ifeval condition: %s' % result
+                    cond = False
+                    attrlist = subs_attrs(attrlist)
+                    if attrlist:
+                        try:
+                            cond = eval(attrlist)
+                        except Exception,e:
+                            raise EAsciiDoc,'error evaluating ifeval condition: %s: %s' % (result, str(e))
+                    self.skip = not cond
+                if not attrlist or name == 'ifeval':
+                    if self.skip:
+                        self.skipto = self.depth
+                        self.skipname = target
+                    self.depth = self.depth+1
+            result = self.read()
+        if result:
+            # Expand executable block macros.
+            mo = macros.match('+',r'eval|sys|sys2',result)
+            if mo:
+                action = mo.group('name')
+                cmd = mo.group('attrlist')
+                s = system(action, cmd, is_macro=True)
+                if s is not None:
+                    self.cursor[2] = s  # So we don't re-evaluate.
+                    result = s
+        if result:
+            # Unescape escaped system macros.
+            if macros.match('+',r'\\eval|\\sys|\\sys2|\\ifdef|\\ifndef|\\endif|\\include|\\include1',result):
+                result = result[1:]
+        return result
+    def eof(self):
+        return self.read_next() is None
+    def read_next(self):
+        save_cursor = self.cursor
+        result = self.read()
+        if result is not None:
+            self.unread(self.cursor)
+            self.cursor = save_cursor
+        return result
+    def read_lines(self,count=1):
+        """Return tuple containing count lines."""
+        result = []
+        i = 0
+        while i < count and not self.eof():
+            result.append(self.read())
+        return tuple(result)
+    def read_ahead(self,count=1):
+        """Same as read_lines() but does not advance the file pointer."""
+        result = []
+        putback = []
+        save_cursor = self.cursor
+        try:
+            i = 0
+            while i < count and not self.eof():
+                result.append(self.read())
+                putback.append(self.cursor)
+                i = i+1
+            while putback:
+                self.unread(putback.pop())
+        finally:
+            self.cursor = save_cursor
+        return tuple(result)
+    def skip_blank_lines(self):
+        reader.read_until(r'\s*\S+')
+    def read_until(self,terminators,same_file=False):
+        """Like read() but reads lines up to (but not including) the first line
+        that matches the terminator regular expression, regular expression
+        object or list of regular expression objects. If same_file is True then
+        the terminating pattern must occur in the file the was being read when
+        the routine was called."""
+        if same_file:
+            fname = self.cursor[0]
+        result = []
+        if not isinstance(terminators,list):
+            if isinstance(terminators,basestring):
+                terminators = [re.compile(terminators)]
+            else:
+                terminators = [terminators]
+        while not self.eof():
+            save_cursor = self.cursor
+            s = self.read()
+            if not same_file or fname == self.cursor[0]:
+                for reo in terminators:
+                    if reo.match(s):
+                        self.unread(self.cursor)
+                        self.cursor = save_cursor
+                        return tuple(result)
+            result.append(s)
+        return tuple(result)
+
+class Writer:
+    """Writes lines to output file."""
+    def __init__(self):
+        self.newline = '\r\n'            # End of line terminator.
+        self.f = None                    # Output file object.
+        self.fname = None                # Output file name.
+        self.lines_out = 0               # Number of lines written.
+        self.skip_blank_lines = False    # If True don't output blank lines.
+    def open(self,fname,bom=None):
+        '''
+        bom is optional byte order mark.
+        http://en.wikipedia.org/wiki/Byte-order_mark
+        '''
+        self.fname = fname
+        if fname == '<stdout>':
+            self.f = sys.stdout
+        else:
+            self.f = open(fname,'wb+')
+        message.verbose('writing: '+writer.fname,False)
+        if bom:
+            self.f.write(bom)
+        self.lines_out = 0
+    def close(self):
+        if self.fname != '<stdout>':
+            self.f.close()
+    def write_line(self, line=None):
+        if not (self.skip_blank_lines and (not line or not line.strip())):
+            self.f.write((line or '') + self.newline)
+            self.lines_out = self.lines_out + 1
+    def write(self,*args,**kwargs):
+        """Iterates arguments, writes tuple and list arguments one line per
+        element, else writes argument as single line. If no arguments writes
+        blank line. If argument is None nothing is written. self.newline is
+        appended to each line."""
+        if 'trace' in kwargs and len(args) > 0:
+            trace(kwargs['trace'],args[0])
+        if len(args) == 0:
+            self.write_line()
+            self.lines_out = self.lines_out + 1
+        else:
+            for arg in args:
+                if is_array(arg):
+                    for s in arg:
+                        self.write_line(s)
+                elif arg is not None:
+                    self.write_line(arg)
+    def write_tag(self,tag,content,subs=None,d=None,**kwargs):
+        """Write content enveloped by tag.
+        Substitutions specified in the 'subs' list are perform on the
+        'content'."""
+        if subs is None:
+            subs = config.subsnormal
+        stag,etag = subs_tag(tag,d)
+        content = Lex.subs(content,subs)
+        if 'trace' in kwargs:
+            trace(kwargs['trace'],[stag]+content+[etag])
+        if stag:
+            self.write(stag)
+        if content:
+            self.write(content)
+        if etag:
+            self.write(etag)
+
+#---------------------------------------------------------------------------
+# Configuration file processing.
+#---------------------------------------------------------------------------
+def _subs_specialwords(mo):
+    """Special word substitution function called by
+    Config.subs_specialwords()."""
+    word = mo.re.pattern                    # The special word.
+    template = config.specialwords[word]    # The corresponding markup template.
+    if not template in config.sections:
+        raise EAsciiDoc,'missing special word template [%s]' % template
+    if mo.group()[0] == '\\':
+        return mo.group()[1:]   # Return escaped word.
+    args = {}
+    args['words'] = mo.group()  # The full match string is argument 'words'.
+    args.update(mo.groupdict()) # Add other named match groups to the arguments.
+    # Delete groups that didn't participate in match.
+    for k,v in args.items():
+        if v is None: del args[k]
+    lines = subs_attrs(config.sections[template],args)
+    if len(lines) == 0:
+        result = ''
+    elif len(lines) == 1:
+        result = lines[0]
+    else:
+        result = writer.newline.join(lines)
+    return result
+
+class Config:
+    """Methods to process configuration files."""
+    # Non-template section name regexp's.
+    ENTRIES_SECTIONS= ('tags','miscellaneous','attributes','specialcharacters',
+            'specialwords','macros','replacements','quotes','titles',
+            r'paradef-.+',r'listdef-.+',r'blockdef-.+',r'tabledef-.+',
+            r'tabletags-.+',r'listtags-.+','replacements2',
+            r'old_tabledef-.+')
+    def __init__(self):
+        self.sections = OrderedDict()   # Keyed by section name containing
+                                        # lists of section lines.
+        # Command-line options.
+        self.verbose = False
+        self.header_footer = True       # -s, --no-header-footer option.
+        # [miscellaneous] section.
+        self.tabsize = 8
+        self.textwidth = 70             # DEPRECATED: Old tables only.
+        self.newline = '\r\n'
+        self.pagewidth = None
+        self.pageunits = None
+        self.outfilesuffix = ''
+        self.subsnormal = SUBS_NORMAL
+        self.subsverbatim = SUBS_VERBATIM
+
+        self.tags = {}          # Values contain (stag,etag) tuples.
+        self.specialchars = {}  # Values of special character substitutions.
+        self.specialwords = {}  # Name is special word pattern, value is macro.
+        self.replacements = OrderedDict()   # Key is find pattern, value is
+                                            #replace pattern.
+        self.replacements2 = OrderedDict()
+        self.specialsections = {} # Name is special section name pattern, value
+                                  # is corresponding section name.
+        self.quotes = OrderedDict()    # Values contain corresponding tag name.
+        self.fname = ''         # Most recently loaded configuration file name.
+        self.conf_attrs = {}    # Attributes entries from conf files.
+        self.cmd_attrs = {}     # Attributes from command-line -a options.
+        self.loaded = []        # Loaded conf files.
+        self.include1 = {}      # Holds include1::[] files for {include1:}.
+        self.dumping = False    # True if asciidoc -c option specified.
+
+    def init(self, cmd):
+        """
+        Check Python version and locate the executable and configuration files
+        directory.
+        cmd is the asciidoc command or asciidoc.py path.
+        """
+        if float(sys.version[:3]) < MIN_PYTHON_VERSION:
+            message.stderr('FAILED: Python 2.3 or better required')
+            sys.exit(1)
+        if not os.path.exists(cmd):
+            message.stderr('FAILED: Missing asciidoc command: %s' % cmd)
+            sys.exit(1)
+        global APP_FILE
+        APP_FILE = os.path.realpath(cmd)
+        global APP_DIR
+        APP_DIR = os.path.dirname(APP_FILE)
+        global USER_DIR
+        USER_DIR = userdir()
+        if USER_DIR is not None:
+            USER_DIR = os.path.join(USER_DIR,'.asciidoc')
+            if not os.path.isdir(USER_DIR):
+                USER_DIR = None
+
+    def load_file(self, fname, dir=None, include=[], exclude=[]):
+        """
+        Loads sections dictionary with sections from file fname.
+        Existing sections are overlaid.
+        The 'include' list contains the section names to be loaded.
+        The 'exclude' list contains section names not to be loaded.
+        Return False if no file was found in any of the locations.
+        """
+        if dir:
+            fname = os.path.join(dir, fname)
+        # Sliently skip missing configuration file.
+        if not os.path.isfile(fname):
+            return False
+        # Don't load conf files twice (local and application conf files are the
+        # same if the source file is in the application directory).
+        if os.path.realpath(fname) in self.loaded:
+            return True
+        rdr = Reader()  # Reader processes system macros.
+        message.linenos = False         # Disable document line numbers.
+        rdr.open(fname)
+        message.linenos = None
+        self.fname = fname
+        reo = re.compile(r'(?u)^\[(?P<section>[^\W\d][\w-]*)\]\s*$')
+        sections = OrderedDict()
+        section,contents = '',[]
+        while not rdr.eof():
+            s = rdr.read()
+            if s and s[0] == '#':       # Skip comment lines.
+                continue
+            if s[:2] == '\\#':          # Unescape lines starting with '#'.
+                s = s[1:]
+            s = s.rstrip()
+            found = reo.findall(s)
+            if found:
+                if section:             # Store previous section.
+                    if section in sections \
+                        and self.entries_section(section):
+                        if ''.join(contents):
+                            # Merge entries.
+                            sections[section] = sections[section] + contents
+                        else:
+                            del sections[section]
+                    else:
+                        sections[section] = contents
+                section = found[0].lower()
+                contents = []
+            else:
+                contents.append(s)
+        if section and contents:        # Store last section.
+            if section in sections \
+                and self.entries_section(section):
+                if ''.join(contents):
+                    # Merge entries.
+                    sections[section] = sections[section] + contents
+                else:
+                    del sections[section]
+            else:
+                sections[section] = contents
+        rdr.close()
+        if include:
+            for s in set(sections) - set(include):
+                del sections[s]
+        if exclude:
+            for s in set(sections) & set(exclude):
+                del sections[s]
+        attrs = {}
+        self.load_sections(sections,attrs)
+        if not include:
+            # If all sections are loaded mark this file as loaded.
+            self.loaded.append(os.path.realpath(fname))
+        document.update_attributes(attrs) # So they are available immediately.
+        return True
+
+    def load_sections(self,sections,attrs=None):
+        """
+        Loads sections dictionary. Each dictionary entry contains a
+        list of lines.
+        Updates 'attrs' with parsed [attributes] section entries.
+        """
+        # Delete trailing blank lines from sections.
+        for k in sections.keys():
+            for i in range(len(sections[k])-1,-1,-1):
+                if not sections[k][i]:
+                    del sections[k][i]
+                elif not self.entries_section(k):
+                    break
+        # Add/overwrite new sections.
+        self.sections.update(sections)
+        self.parse_tags()
+        # Internally [miscellaneous] section entries are just attributes.
+        d = {}
+        parse_entries(sections.get('miscellaneous',()), d, unquote=True,
+                allow_name_only=True)
+        parse_entries(sections.get('attributes',()), d, unquote=True,
+                allow_name_only=True)
+        update_attrs(self.conf_attrs,d)
+        if attrs is not None:
+            attrs.update(d)
+        d = {}
+        parse_entries(sections.get('titles',()),d)
+        Title.load(d)
+        parse_entries(sections.get('specialcharacters',()),self.specialchars,escape_delimiter=False)
+        parse_entries(sections.get('quotes',()),self.quotes)
+        self.parse_specialwords()
+        self.parse_replacements()
+        self.parse_replacements('replacements2')
+        self.parse_specialsections()
+        paragraphs.load(sections)
+        lists.load(sections)
+        blocks.load(sections)
+        tables_OLD.load(sections)
+        tables.load(sections)
+        macros.load(sections.get('macros',()))
+
+    def get_load_dirs(self):
+        """
+        Return list of well known paths with conf files.
+        """
+        result = []
+        if localapp():
+            # Load from folders in asciidoc executable directory.
+            result.append(APP_DIR)
+        else:
+            # Load from global configuration directory.
+            result.append(CONF_DIR)
+        # Load configuration files from ~/.asciidoc if it exists.
+        if USER_DIR is not None:
+            result.append(USER_DIR)
+        return result
+
+    def find_in_dirs(self, filename, dirs=None):
+        """
+        Find conf files from dirs list.
+        Return list of found file paths.
+        Return empty list if not found in any of the locations.
+        """
+        result = []
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            f = os.path.join(d,filename)
+            if os.path.isfile(f):
+                result.append(f)
+        return result
+
+    def load_from_dirs(self, filename, dirs=None, include=[]):
+        """
+        Load conf file from dirs list.
+        If dirs not specified try all the well known locations.
+        Return False if no file was sucessfully loaded.
+        """
+        count = 0
+        for f in self.find_in_dirs(filename,dirs):
+            if self.load_file(f, include=include):
+                count += 1
+        return count != 0
+
+    def load_backend(self, dirs=None):
+        """
+        Load the backend configuration files from dirs list.
+        If dirs not specified try all the well known locations.
+        """
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            conf = document.backend + '.conf'
+            self.load_file(conf,d)
+            conf = document.backend + '-' + document.doctype + '.conf'
+            self.load_file(conf,d)
+
+    def load_filters(self, dirs=None):
+        """
+        Load filter configuration files from 'filters' directory in dirs list.
+        If dirs not specified try all the well known locations.
+        """
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            # Load filter .conf files.
+            filtersdir = os.path.join(d,'filters')
+            for dirpath,dirnames,filenames in os.walk(filtersdir):
+                for f in filenames:
+                    if re.match(r'^.+\.conf$',f):
+                        self.load_file(f,dirpath)
+
+    def load_miscellaneous(self,d):
+        """Set miscellaneous configuration entries from dictionary 'd'."""
+        def set_misc(name,rule='True',intval=False):
+            if name in d:
+                errmsg = 'illegal [miscellaneous] %s entry' % name
+                if intval:
+                    setattr(self, name, int(validate(d[name],rule,errmsg)))
+                else:
+                    setattr(self, name, validate(d[name],rule,errmsg))
+        set_misc('tabsize','int($)>0',intval=True)
+        set_misc('textwidth','int($)>0',intval=True) # DEPRECATED: Old tables only.
+        set_misc('pagewidth','"%f" % $')
+        if 'pagewidth' in d:
+            self.pagewidth = float(self.pagewidth)
+        set_misc('pageunits')
+        set_misc('outfilesuffix')
+        if 'newline' in d:
+            # Convert escape sequences to their character values.
+            self.newline = eval('"'+d['newline']+'"')
+        if 'subsnormal' in d:
+            self.subsnormal = parse_options(d['subsnormal'],SUBS_OPTIONS,
+                    'illegal [%s] %s: %s' %
+                    ('miscellaneous','subsnormal',d['subsnormal']))
+        if 'subsverbatim' in d:
+            self.subsverbatim = parse_options(d['subsverbatim'],SUBS_OPTIONS,
+                    'illegal [%s] %s: %s' %
+                    ('miscellaneous','subsverbatim',d['subsverbatim']))
+
+    def validate(self):
+        """Check the configuration for internal consistancy. Called after all
+        configuration files have been loaded."""
+        message.linenos = False     # Disable document line numbers.
+        # Heuristic to validate that at least one configuration file was loaded.
+        if not self.specialchars or not self.tags or not lists:
+            raise EAsciiDoc,'incomplete configuration files'
+        # Check special characters are only one character long.
+        for k in self.specialchars.keys():
+            if len(k) != 1:
+                raise EAsciiDoc,'[specialcharacters] ' \
+                                'must be a single character: %s' % k
+        # Check all special words have a corresponding inline macro body.
+        for macro in self.specialwords.values():
+            if not is_name(macro):
+                raise EAsciiDoc,'illegal special word name: %s' % macro
+            if not macro in self.sections:
+                message.warning('missing special word macro: [%s]' % macro)
+        # Check all text quotes have a corresponding tag.
+        for q in self.quotes.keys()[:]:
+            tag = self.quotes[q]
+            if not tag:
+                del self.quotes[q]  # Undefine quote.
+            else:
+                if tag[0] == '#':
+                    tag = tag[1:]
+                if not tag in self.tags:
+                    message.warning('[quotes] %s missing tag definition: %s' % (q,tag))
+        # Check all specialsections section names exist.
+        for k,v in self.specialsections.items():
+            if not v:
+                del self.specialsections[k]
+            elif not v in self.sections:
+                message.warning('missing specialsections section: [%s]' % v)
+        paragraphs.validate()
+        lists.validate()
+        blocks.validate()
+        tables_OLD.validate()
+        tables.validate()
+        macros.validate()
+        message.linenos = None
+
+    def entries_section(self,section_name):
+        """
+        Return True if conf file section contains entries, not a markup
+        template.
+        """
+        for name in self.ENTRIES_SECTIONS:
+            if re.match(name,section_name):
+                return True
+        return False
+
+    def dump(self):
+        """Dump configuration to stdout."""
+        # Header.
+        hdr = ''
+        hdr = hdr + '#' + writer.newline
+        hdr = hdr + '# Generated by AsciiDoc %s for %s %s.%s' % \
+            (VERSION,document.backend,document.doctype,writer.newline)
+        t = time.asctime(time.localtime(time.time()))
+        hdr = hdr + '# %s%s' % (t,writer.newline)
+        hdr = hdr + '#' + writer.newline
+        sys.stdout.write(hdr)
+        # Dump special sections.
+        # Dump only the configuration file and command-line attributes.
+        # [miscellanous] entries are dumped as part of the [attributes].
+        d = {}
+        d.update(self.conf_attrs)
+        d.update(self.cmd_attrs)
+        dump_section('attributes',d)
+        Title.dump()
+        dump_section('quotes',self.quotes)
+        dump_section('specialcharacters',self.specialchars)
+        d = {}
+        for k,v in self.specialwords.items():
+            if v in d:
+                d[v] = '%s "%s"' % (d[v],k)   # Append word list.
+            else:
+                d[v] = '"%s"' % k
+        dump_section('specialwords',d)
+        dump_section('replacements',self.replacements)
+        dump_section('replacements2',self.replacements2)
+        dump_section('specialsections',self.specialsections)
+        d = {}
+        for k,v in self.tags.items():
+            d[k] = '%s|%s' % v
+        dump_section('tags',d)
+        paragraphs.dump()
+        lists.dump()
+        blocks.dump()
+        tables_OLD.dump()
+        tables.dump()
+        macros.dump()
+        # Dump remaining sections.
+        for k in self.sections.keys():
+            if not self.entries_section(k):
+                sys.stdout.write('[%s]%s' % (k,writer.newline))
+                for line in self.sections[k]:
+                    sys.stdout.write('%s%s' % (line,writer.newline))
+                sys.stdout.write(writer.newline)
+
+    def subs_section(self,section,d):
+        """Section attribute substitution using attributes from
+        document.attributes and 'd'.  Lines containing undefinded
+        attributes are deleted."""
+        if section in self.sections:
+            return subs_attrs(self.sections[section],d)
+        else:
+            message.warning('missing section: [%s]' % section)
+            return ()
+
+    def parse_tags(self):
+        """Parse [tags] section entries into self.tags dictionary."""
+        d = {}
+        parse_entries(self.sections.get('tags',()),d)
+        for k,v in d.items():
+            if v is None:
+                if k in self.tags:
+                    del self.tags[k]
+            elif v == '':
+                self.tags[k] = (None,None)
+            else:
+                mo = re.match(r'(?P<stag>.*)\|(?P<etag>.*)',v)
+                if mo:
+                    self.tags[k] = (mo.group('stag'), mo.group('etag'))
+                else:
+                    raise EAsciiDoc,'[tag] %s value malformed' % k
+
+    def tag(self, name, d=None):
+        """Returns (starttag,endtag) tuple named name from configuration file
+        [tags] section. Raise error if not found. If a dictionary 'd' is
+        passed then merge with document attributes and perform attribute
+        substitution on tags."""
+        if not name in self.tags:
+            raise EAsciiDoc, 'missing tag: %s' % name
+        stag,etag = self.tags[name]
+        if d is not None:
+            # TODO: Should we warn if substitution drops a tag?
+            if stag:
+                stag = subs_attrs(stag,d)
+            if etag:
+                etag = subs_attrs(etag,d)
+        if stag is None: stag = ''
+        if etag is None: etag = ''
+        return (stag,etag)
+
+    def parse_specialsections(self):
+        """Parse specialsections section to self.specialsections dictionary."""
+        # TODO: This is virtually the same as parse_replacements() and should
+        # be factored to single routine.
+        d = {}
+        parse_entries(self.sections.get('specialsections',()),d,unquote=True)
+        for pat,sectname in d.items():
+            pat = strip_quotes(pat)
+            if not is_re(pat):
+                raise EAsciiDoc,'[specialsections] entry ' \
+                                'is not a valid regular expression: %s' % pat
+            if sectname is None:
+                if pat in self.specialsections:
+                    del self.specialsections[pat]
+            else:
+                self.specialsections[pat] = sectname
+
+    def parse_replacements(self,sect='replacements'):
+        """Parse replacements section into self.replacements dictionary."""
+        d = OrderedDict()
+        parse_entries(self.sections.get(sect,()), d, unquote=True)
+        for pat,rep in d.items():
+            if not self.set_replacement(pat, rep, getattr(self,sect)):
+                raise EAsciiDoc,'[%s] entry in %s is not a valid' \
+                    ' regular expression: %s' % (sect,self.fname,pat)
+
+    @staticmethod
+    def set_replacement(pat, rep, replacements):
+        """Add pattern and replacement to replacements dictionary."""
+        pat = strip_quotes(pat)
+        if not is_re(pat):
+            return False
+        if rep is None:
+            if pat in replacements:
+                del replacements[pat]
+        else:
+            replacements[pat] = strip_quotes(rep)
+        return True
+
+    def subs_replacements(self,s,sect='replacements'):
+        """Substitute patterns from self.replacements in 's'."""
+        result = s
+        for pat,rep in getattr(self,sect).items():
+            result = re.sub(pat, rep, result)
+        return result
+
+    def parse_specialwords(self):
+        """Parse special words section into self.specialwords dictionary."""
+        reo = re.compile(r'(?:\s|^)(".+?"|[^"\s]+)(?=\s|$)')
+        for line in self.sections.get('specialwords',()):
+            e = parse_entry(line)
+            if not e:
+                raise EAsciiDoc,'[specialwords] entry in %s is malformed: %s' \
+                    % (self.fname,line)
+            name,wordlist = e
+            if not is_name(name):
+                raise EAsciiDoc,'[specialwords] name in %s is illegal: %s' \
+                    % (self.fname,name)
+            if wordlist is None:
+                # Undefine all words associated with 'name'.
+                for k,v in self.specialwords.items():
+                    if v == name:
+                        del self.specialwords[k]
+            else:
+                words = reo.findall(wordlist)
+                for word in words:
+                    word = strip_quotes(word)
+                    if not is_re(word):
+                        raise EAsciiDoc,'[specialwords] entry in %s ' \
+                            'is not a valid regular expression: %s' \
+                            % (self.fname,word)
+                    self.specialwords[word] = name
+
+    def subs_specialchars(self,s):
+        """Perform special character substitution on string 's'."""
+        """It may seem like a good idea to escape special characters with a '\'
+        character, the reason we don't is because the escape character itself
+        then has to be escaped and this makes including code listings
+        problematic. Use the predefined {amp},{lt},{gt} attributes instead."""
+        result = ''
+        for ch in s:
+            result = result + self.specialchars.get(ch,ch)
+        return result
+
+    def subs_specialchars_reverse(self,s):
+        """Perform reverse special character substitution on string 's'."""
+        result = s
+        for k,v in self.specialchars.items():
+            result = result.replace(v, k)
+        return result
+
+    def subs_specialwords(self,s):
+        """Search for word patterns from self.specialwords in 's' and
+        substitute using corresponding macro."""
+        result = s
+        for word in self.specialwords.keys():
+            result = re.sub(word, _subs_specialwords, result)
+        return result
+
+    def expand_templates(self,entries):
+        """Expand any template::[] macros in a list of section entries."""
+        result = []
+        for line in entries:
+            mo = macros.match('+',r'template',line)
+            if mo:
+                s = mo.group('attrlist')
+                if s in self.sections:
+                    result += self.expand_templates(self.sections[s])
+                else:
+                    message.warning('missing section: [%s]' % s)
+                    result.append(line)
+            else:
+                result.append(line)
+        return result
+
+    def expand_all_templates(self):
+        for k,v in self.sections.items():
+            self.sections[k] = self.expand_templates(v)
+
+    def section2tags(self, section, d={}, skipstart=False, skipend=False):
+        """Perform attribute substitution on 'section' using document
+        attributes plus 'd' attributes. Return tuple (stag,etag) containing
+        pre and post | placeholder tags. 'skipstart' and 'skipend' are
+        used to suppress substitution."""
+        assert section is not None
+        if section in self.sections:
+            body = self.sections[section]
+        else:
+            message.warning('missing section: [%s]' % section)
+            body = ()
+        # Split macro body into start and end tag lists.
+        stag = []
+        etag = []
+        in_stag = True
+        for s in body:
+            if in_stag:
+                mo = re.match(r'(?P<stag>.*)\|(?P<etag>.*)',s)
+                if mo:
+                    if mo.group('stag'):
+                        stag.append(mo.group('stag'))
+                    if mo.group('etag'):
+                        etag.append(mo.group('etag'))
+                    in_stag = False
+                else:
+                    stag.append(s)
+            else:
+                etag.append(s)
+        # Do attribute substitution last so {brkbar} can be used to escape |.
+        # But don't do attribute substitution on title -- we've already done it.
+        title = d.get('title')
+        if title:
+            d['title'] = chr(0)  # Replace with unused character.
+        if not skipstart:
+            stag = subs_attrs(stag, d)
+        if not skipend:
+            etag = subs_attrs(etag, d)
+        # Put the {title} back.
+        if title:
+            stag = map(lambda x: x.replace(chr(0), title), stag)
+            etag = map(lambda x: x.replace(chr(0), title), etag)
+            d['title'] = title
+        return (stag,etag)
+
+
+#---------------------------------------------------------------------------
+# Deprecated old table classes follow.
+# Naming convention is an _OLD name suffix.
+# These will be removed from future versions of AsciiDoc
+
+def join_lines_OLD(lines):
+    """Return a list in which lines terminated with the backslash line
+    continuation character are joined."""
+    result = []
+    s = ''
+    continuation = False
+    for line in lines:
+        if line and line[-1] == '\\':
+            s = s + line[:-1]
+            continuation = True
+            continue
+        if continuation:
+            result.append(s+line)
+            s = ''
+            continuation = False
+        else:
+            result.append(line)
+    if continuation:
+        result.append(s)
+    return result
+
+class Column_OLD:
+    """Table column."""
+    def __init__(self):
+        self.colalign = None    # 'left','right','center'
+        self.rulerwidth = None
+        self.colwidth = None    # Output width in page units.
+
+class Table_OLD(AbstractBlock):
+    COL_STOP = r"(`|'|\.)"  # RE.
+    ALIGNMENTS = {'`':'left', "'":'right', '.':'center'}
+    FORMATS = ('fixed','csv','dsv')
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('template','fillchar','format','colspec',
+                              'headrow','footrow','bodyrow','headdata',
+                              'footdata', 'bodydata')
+        # Configuration parameters.
+        self.fillchar=None
+        self.format=None    # 'fixed','csv','dsv'
+        self.colspec=None
+        self.headrow=None
+        self.footrow=None
+        self.bodyrow=None
+        self.headdata=None
+        self.footdata=None
+        self.bodydata=None
+        # Calculated parameters.
+        self.underline=None     # RE matching current table underline.
+        self.isnumeric=False    # True if numeric ruler.
+        self.tablewidth=None    # Optional table width scale factor.
+        self.columns=[]         # List of Columns.
+        # Other.
+        self.check_msg=''       # Message set by previous self.validate() call.
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+        """Update table definition from section entries in 'entries'."""
+        for k,v in entries.items():
+            if k == 'fillchar':
+                if v and len(v) == 1:
+                    self.fillchar = v
+                else:
+                    raise EAsciiDoc,'malformed table fillchar: %s' % v
+            elif k == 'format':
+                if v in Table_OLD.FORMATS:
+                    self.format = v
+                else:
+                    raise EAsciiDoc,'illegal table format: %s' % v
+            elif k == 'colspec':
+                self.colspec = v
+            elif k == 'headrow':
+                self.headrow = v
+            elif k == 'footrow':
+                self.footrow = v
+            elif k == 'bodyrow':
+                self.bodyrow = v
+            elif k == 'headdata':
+                self.headdata = v
+            elif k == 'footdata':
+                self.footdata = v
+            elif k == 'bodydata':
+                self.bodydata = v
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('fillchar='+self.fillchar)
+        write('format='+self.format)
+        if self.colspec:
+            write('colspec='+self.colspec)
+        if self.headrow:
+            write('headrow='+self.headrow)
+        if self.footrow:
+            write('footrow='+self.footrow)
+        write('bodyrow='+self.bodyrow)
+        if self.headdata:
+            write('headdata='+self.headdata)
+        if self.footdata:
+            write('footdata='+self.footdata)
+        write('bodydata='+self.bodydata)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        """Check table definition and set self.check_msg if invalid else set
+        self.check_msg to blank string."""
+        # Check global table parameters.
+        if config.textwidth is None:
+            self.check_msg = 'missing [miscellaneous] textwidth entry'
+        elif config.pagewidth is None:
+            self.check_msg = 'missing [miscellaneous] pagewidth entry'
+        elif config.pageunits is None:
+            self.check_msg = 'missing [miscellaneous] pageunits entry'
+        elif self.headrow is None:
+            self.check_msg = 'missing headrow entry'
+        elif self.footrow is None:
+            self.check_msg = 'missing footrow entry'
+        elif self.bodyrow is None:
+            self.check_msg = 'missing bodyrow entry'
+        elif self.headdata is None:
+            self.check_msg = 'missing headdata entry'
+        elif self.footdata is None:
+            self.check_msg = 'missing footdata entry'
+        elif self.bodydata is None:
+            self.check_msg = 'missing bodydata entry'
+        else:
+            # No errors.
+            self.check_msg = ''
+    def isnext(self):
+        return AbstractBlock.isnext(self)
+    def parse_ruler(self,ruler):
+        """Parse ruler calculating underline and ruler column widths."""
+        fc = re.escape(self.fillchar)
+        # Strip and save optional tablewidth from end of ruler.
+        mo = re.match(r'^(.*'+fc+r'+)([\d\.]+)$',ruler)
+        if mo:
+            ruler = mo.group(1)
+            self.tablewidth = float(mo.group(2))
+            self.attributes['tablewidth'] = str(float(self.tablewidth))
+        else:
+            self.tablewidth = None
+            self.attributes['tablewidth'] = '100.0'
+        # Guess whether column widths are specified numerically or not.
+        if ruler[1] != self.fillchar:
+            # If the first column does not start with a fillchar then numeric.
+            self.isnumeric = True
+        elif ruler[1:] == self.fillchar*len(ruler[1:]):
+            # The case of one column followed by fillchars is numeric.
+            self.isnumeric = True
+        else:
+            self.isnumeric = False
+        # Underlines must be 3 or more fillchars.
+        self.underline = r'^' + fc + r'{3,}$'
+        splits = re.split(self.COL_STOP,ruler)[1:]
+        # Build self.columns.
+        for i in range(0,len(splits),2):
+            c = Column_OLD()
+            c.colalign = self.ALIGNMENTS[splits[i]]
+            s = splits[i+1]
+            if self.isnumeric:
+                # Strip trailing fillchars.
+                s = re.sub(fc+r'+$','',s)
+                if s == '':
+                    c.rulerwidth = None
+                else:
+                    c.rulerwidth = int(validate(s,'int($)>0',
+                        'malformed ruler: bad width'))
+            else:   # Calculate column width from inter-fillchar intervals.
+                if not re.match(r'^'+fc+r'+$',s):
+                    raise EAsciiDoc,'malformed ruler: illegal fillchars'
+                c.rulerwidth = len(s)+1
+            self.columns.append(c)
+        # Fill in unspecified ruler widths.
+        if self.isnumeric:
+            if self.columns[0].rulerwidth is None:
+                prevwidth = 1
+            for c in self.columns:
+                if c.rulerwidth is None:
+                    c.rulerwidth = prevwidth
+                prevwidth = c.rulerwidth
+    def build_colspecs(self):
+        """Generate colwidths and colspecs. This can only be done after the
+        table arguments have been parsed since we use the table format."""
+        self.attributes['cols'] = len(self.columns)
+        # Calculate total ruler width.
+        totalwidth = 0
+        for c in self.columns:
+            totalwidth = totalwidth + c.rulerwidth
+        if totalwidth <= 0:
+            raise EAsciiDoc,'zero width table'
+        # Calculate marked up colwidths from rulerwidths.
+        for c in self.columns:
+            # Convert ruler width to output page width.
+            width = float(c.rulerwidth)
+            if self.format == 'fixed':
+                if self.tablewidth is None:
+                    # Size proportional to ruler width.
+                    colfraction = width/config.textwidth
+                else:
+                    # Size proportional to page width.
+                    colfraction = width/totalwidth
+            else:
+                    # Size proportional to page width.
+                colfraction = width/totalwidth
+            c.colwidth = colfraction * config.pagewidth # To page units.
+            if self.tablewidth is not None:
+                c.colwidth = c.colwidth * self.tablewidth   # Scale factor.
+                if self.tablewidth > 1:
+                    c.colwidth = c.colwidth/100 # tablewidth is in percent.
+        # Build colspecs.
+        if self.colspec:
+            cols = []
+            i = 0
+            for c in self.columns:
+                i += 1
+                self.attributes['colalign'] = c.colalign
+                self.attributes['colwidth'] = str(int(c.colwidth))
+                self.attributes['colnumber'] = str(i + 1)
+                s = subs_attrs(self.colspec,self.attributes)
+                if not s:
+                    message.warning('colspec dropped: contains undefined attribute')
+                else:
+                    cols.append(s)
+            self.attributes['colspecs'] = writer.newline.join(cols)
+    def split_rows(self,rows):
+        """Return a two item tuple containing a list of lines up to but not
+        including the next underline (continued lines are joined ) and the
+        tuple of all lines after the underline."""
+        reo = re.compile(self.underline)
+        i = 0
+        while not reo.match(rows[i]):
+            i = i+1
+        if i == 0:
+            raise EAsciiDoc,'missing table rows'
+        if i >= len(rows):
+            raise EAsciiDoc,'closing [%s] underline expected' % self.name
+        return (join_lines_OLD(rows[:i]), rows[i+1:])
+    def parse_rows(self, rows, rtag, dtag):
+        """Parse rows list using the row and data tags. Returns a substituted
+        list of output lines."""
+        result = []
+        # Source rows are parsed as single block, rather than line by line, to
+        # allow the CSV reader to handle multi-line rows.
+        if self.format == 'fixed':
+            rows = self.parse_fixed(rows)
+        elif self.format == 'csv':
+            rows = self.parse_csv(rows)
+        elif self.format == 'dsv':
+            rows = self.parse_dsv(rows)
+        else:
+            assert True,'illegal table format'
+        # Substitute and indent all data in all rows.
+        stag,etag = subs_tag(rtag,self.attributes)
+        for row in rows:
+            result.append('  '+stag)
+            for data in self.subs_row(row,dtag):
+                result.append('    '+data)
+            result.append('  '+etag)
+        return result
+    def subs_row(self, data, dtag):
+        """Substitute the list of source row data elements using the data tag.
+        Returns a substituted list of output table data items."""
+        result = []
+        if len(data) < len(self.columns):
+            message.warning('fewer row data items then table columns')
+        if len(data) > len(self.columns):
+            message.warning('more row data items than table columns')
+        for i in range(len(self.columns)):
+            if i > len(data) - 1:
+                d = ''  # Fill missing column data with blanks.
+            else:
+                d = data[i]
+            c = self.columns[i]
+            self.attributes['colalign'] = c.colalign
+            self.attributes['colwidth'] = str(int(c.colwidth))
+            self.attributes['colnumber'] = str(i + 1)
+            stag,etag = subs_tag(dtag,self.attributes)
+            # Insert AsciiDoc line break (' +') where row data has newlines
+            # ('\n').  This is really only useful when the table format is csv
+            # and the output markup is HTML. It's also a bit dubious in that it
+            # assumes the user has not modified the shipped line break pattern.
+            subs = self.get_subs()[0]
+            if 'replacements' in subs:
+                # Insert line breaks in cell data.
+                d = re.sub(r'(?m)\n',r' +\n',d)
+                d = d.split('\n')    # So writer.newline is written.
+            else:
+                d = [d]
+            result = result + [stag] + Lex.subs(d,subs) + [etag]
+        return result
+    def parse_fixed(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        result = []
+        for row in rows:
+            data = []
+            start = 0
+            # build an encoded representation
+            row = char_decode(row)
+            for c in self.columns:
+                end = start + c.rulerwidth
+                if c is self.columns[-1]:
+                    # Text in last column can continue forever.
+                    # Use the encoded string to slice, but convert back
+                    # to plain string before further processing
+                    data.append(char_encode(row[start:]).strip())
+                else:
+                    data.append(char_encode(row[start:end]).strip())
+                start = end
+            result.append(data)
+        return result
+    def parse_csv(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        import StringIO
+        import csv
+        result = []
+        rdr = csv.reader(StringIO.StringIO('\r\n'.join(rows)),
+            skipinitialspace=True)
+        try:
+            for row in rdr:
+                result.append(row)
+        except Exception:
+            raise EAsciiDoc,'csv parse error: %s' % row
+        return result
+    def parse_dsv(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        separator = self.attributes.get('separator',':')
+        separator = eval('"'+separator+'"')
+        if len(separator) != 1:
+            raise EAsciiDoc,'malformed dsv separator: %s' % separator
+        # TODO If separator is preceeded by an odd number of backslashes then
+        # it is escaped and should not delimit.
+        result = []
+        for row in rows:
+            # Skip blank lines
+            if row == '': continue
+            # Unescape escaped characters.
+            row = eval('"'+row.replace('"','\\"')+'"')
+            data = row.split(separator)
+            data = [s.strip() for s in data]
+            result.append(data)
+        return result
+    def translate(self):
+        message.deprecated('old tables syntax')
+        AbstractBlock.translate(self)
+        # Reset instance specific properties.
+        self.underline = None
+        self.columns = []
+        attrs = {}
+        BlockTitle.consume(attrs)
+        # Add relevant globals to table substitutions.
+        attrs['pagewidth'] = str(config.pagewidth)
+        attrs['pageunits'] = config.pageunits
+        # Mix in document attribute list.
+        AttributeList.consume(attrs)
+        # Validate overridable attributes.
+        for k,v in attrs.items():
+            if k == 'format':
+                if v not in self.FORMATS:
+                    raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.name,k,v)
+                self.format = v
+            elif k == 'tablewidth':
+                try:
+                    self.tablewidth = float(attrs['tablewidth'])
+                except Exception:
+                    raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.name,k,v)
+        self.merge_attributes(attrs)
+        # Parse table ruler.
+        ruler = reader.read()
+        assert re.match(self.delimiter,ruler)
+        self.parse_ruler(ruler)
+        # Read the entire table.
+        table = []
+        while True:
+            line = reader.read_next()
+            # Table terminated by underline followed by a blank line or EOF.
+            if len(table) > 0 and re.match(self.underline,table[-1]):
+                if line in ('',None):
+                    break;
+            if line is None:
+                raise EAsciiDoc,'closing [%s] underline expected' % self.name
+            table.append(reader.read())
+        # EXPERIMENTAL: The number of lines in the table, requested by Benjamin Klum.
+        self.attributes['rows'] = str(len(table))
+        if self.check_msg:  # Skip if table definition was marked invalid.
+            message.warning('skipping %s table: %s' % (self.name,self.check_msg))
+            return
+        # Generate colwidths and colspecs.
+        self.build_colspecs()
+        # Generate headrows, footrows, bodyrows.
+        # Headrow, footrow and bodyrow data replaces same named attributes in
+        # the table markup template. In order to ensure this data does not get
+        # a second attribute substitution (which would interfere with any
+        # already substituted inline passthroughs) unique placeholders are used
+        # (the tab character does not appear elsewhere since it is expanded on
+        # input) which are replaced after template attribute substitution.
+        headrows = footrows = []
+        bodyrows,table = self.split_rows(table)
+        if table:
+            headrows = bodyrows
+            bodyrows,table = self.split_rows(table)
+            if table:
+                footrows,table = self.split_rows(table)
+        if headrows:
+            headrows = self.parse_rows(headrows, self.headrow, self.headdata)
+            headrows = writer.newline.join(headrows)
+            self.attributes['headrows'] = '\x07headrows\x07'
+        if footrows:
+            footrows = self.parse_rows(footrows, self.footrow, self.footdata)
+            footrows = writer.newline.join(footrows)
+            self.attributes['footrows'] = '\x07footrows\x07'
+        bodyrows = self.parse_rows(bodyrows, self.bodyrow, self.bodydata)
+        bodyrows = writer.newline.join(bodyrows)
+        self.attributes['bodyrows'] = '\x07bodyrows\x07'
+        table = subs_attrs(config.sections[self.template],self.attributes)
+        table = writer.newline.join(table)
+        # Before we finish replace the table head, foot and body place holders
+        # with the real data.
+        if headrows:
+            table = table.replace('\x07headrows\x07', headrows, 1)
+        if footrows:
+            table = table.replace('\x07footrows\x07', footrows, 1)
+        table = table.replace('\x07bodyrows\x07', bodyrows, 1)
+        writer.write(table,trace='table')
+
+class Tables_OLD(AbstractBlocks):
+    """List of tables."""
+    BLOCK_TYPE = Table_OLD
+    PREFIX = 'old_tabledef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        # Does not call AbstractBlocks.validate().
+        # Check we have a default table definition,
+        for i in range(len(self.blocks)):
+            if self.blocks[i].name == 'old_tabledef-default':
+                default = self.blocks[i]
+                break
+        else:
+            raise EAsciiDoc,'missing section: [OLD_tabledef-default]'
+        # Set default table defaults.
+        if default.format is None: default.subs = 'fixed'
+        # Propagate defaults to unspecified table parameters.
+        for b in self.blocks:
+            if b is not default:
+                if b.fillchar is None: b.fillchar = default.fillchar
+                if b.format is None: b.format = default.format
+                if b.template is None: b.template = default.template
+                if b.colspec is None: b.colspec = default.colspec
+                if b.headrow is None: b.headrow = default.headrow
+                if b.footrow is None: b.footrow = default.footrow
+                if b.bodyrow is None: b.bodyrow = default.bodyrow
+                if b.headdata is None: b.headdata = default.headdata
+                if b.footdata is None: b.footdata = default.footdata
+                if b.bodydata is None: b.bodydata = default.bodydata
+        # Check all tables have valid fill character.
+        for b in self.blocks:
+            if not b.fillchar or len(b.fillchar) != 1:
+                raise EAsciiDoc,'[%s] missing or illegal fillchar' % b.name
+        # Build combined tables delimiter patterns and assign defaults.
+        delimiters = []
+        for b in self.blocks:
+            # Ruler is:
+            #   (ColStop,(ColWidth,FillChar+)?)+, FillChar+, TableWidth?
+            b.delimiter = r'^(' + Table_OLD.COL_STOP \
+                + r'(\d*|' + re.escape(b.fillchar) + r'*)' \
+                + r')+' \
+                + re.escape(b.fillchar) + r'+' \
+                + '([\d\.]*)$'
+            delimiters.append(b.delimiter)
+            if not b.headrow:
+                b.headrow = b.bodyrow
+            if not b.footrow:
+                b.footrow = b.bodyrow
+            if not b.headdata:
+                b.headdata = b.bodydata
+            if not b.footdata:
+                b.footdata = b.bodydata
+        self.delimiters = re_join(delimiters)
+        # Check table definitions are valid.
+        for b in self.blocks:
+            b.validate()
+            if config.verbose:
+                if b.check_msg:
+                    message.warning('[%s] table definition: %s' % (b.name,b.check_msg))
+
+# End of deprecated old table classes.
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# Filter commands.
+#---------------------------------------------------------------------------
+import shutil, zipfile
+
+def die(msg):
+    message.stderr(msg)
+    sys.exit(1)
+
+def unzip(zip_file, destdir):
+    """
+    Unzip Zip file to destination directory.
+    Throws exception if error occurs.
+    """
+    zipo = zipfile.ZipFile(zip_file, 'r')
+    try:
+        for zi in zipo.infolist():
+            outfile = zi.filename
+            if not outfile.endswith('/'):
+                d, outfile = os.path.split(outfile)
+                directory = os.path.normpath(os.path.join(destdir, d))
+                if not os.path.isdir(directory):
+                    os.makedirs(directory)
+                outfile = os.path.join(directory, outfile)
+                perms = (zi.external_attr >> 16) & 0777
+                message.verbose('extracting: %s' % outfile)
+                fh = os.open(outfile, os.O_CREAT | os.O_WRONLY, perms)
+                try:
+                    os.write(fh, zipo.read(zi.filename))
+                finally:
+                    os.close(fh)
+    finally:
+        zipo.close()
+
+class Filter:
+    """
+    --filter option commands.
+    """
+
+    @staticmethod
+    def get_filters_dir():
+        """
+        Return path of .asciidoc/filters in user's home direcory or None if
+        user home not defined.
+        """
+        result = userdir()
+        if result:
+            result = os.path.join(result,'.asciidoc','filters')
+        return result
+
+    @staticmethod
+    def install(args):
+        """
+        Install filter Zip file.
+        args[0] is filter zip file path.
+        args[1] is optional destination filters directory.
+        """
+        if len(args) not in (1,2):
+            die('invalid number of arguments: --filter install %s'
+                    % ' '.join(args))
+        zip_file = args[0]
+        if not os.path.isfile(zip_file):
+            die('file not found: %s' % zip_file)
+        reo = re.match(r'^\w+',os.path.split(zip_file)[1])
+        if not reo:
+            die('filter file name does not start with legal filter name: %s'
+                    % zip_file)
+        filter_name = reo.group()
+        if len(args) == 2:
+            filters_dir = args[1]
+            if not os.path.isdir(filters_dir):
+                die('directory not found: %s' % filters_dir)
+        else:
+            filters_dir = Filter.get_filters_dir()
+            if not filters_dir:
+                die('user home directory is not defined')
+        filter_dir = os.path.join(filters_dir, filter_name)
+        if os.path.exists(filter_dir):
+            die('filter is already installed: %s' % filter_dir)
+        try:
+            os.makedirs(filter_dir)
+        except Exception,e:
+            die('failed to create filter directory: %s' % str(e))
+        try:
+            unzip(zip_file, filter_dir)
+        except Exception,e:
+            die('failed to extract filter: %s' % str(e))
+
+    @staticmethod
+    def remove(args):
+        """
+        Delete filter from .asciidoc/filters/ in user's home directory.
+        args[0] is filter name.
+        args[1] is optional filters directory.
+        """
+        if len(args) not in (1,2):
+            die('invalid number of arguments: --filter remove %s'
+                    % ' '.join(args))
+        filter_name = args[0]
+        if not re.match(r'^\w+$',filter_name):
+            die('illegal filter name: %s' % filter_name)
+        if len(args) == 2:
+            d = args[1]
+            if not os.path.isdir(d):
+                die('directory not found: %s' % d)
+        else:
+            d = Filter.get_filters_dir()
+            if not d:
+                die('user directory is not defined')
+        filter_dir = os.path.join(d, filter_name)
+        if not os.path.isdir(filter_dir):
+            die('cannot find filter: %s' % filter_dir)
+        try:
+            message.verbose('removing: %s' % filter_dir)
+            shutil.rmtree(filter_dir)
+        except Exception,e:
+            die('failed to delete filter: %s' % str(e))
+
+    @staticmethod
+    def list():
+        """
+        List all filter directories (global and local).
+        """
+        for d in [os.path.join(d,'filters') for d in config.get_load_dirs()]:
+            if os.path.isdir(d):
+                for f in os.walk(d).next()[1]:
+                    message.stdout(os.path.join(d,f))
+
+
+#---------------------------------------------------------------------------
+# Application code.
+#---------------------------------------------------------------------------
+# Constants
+# ---------
+APP_FILE = None             # This file's full path.
+APP_DIR = None              # This file's directory.
+USER_DIR = None             # ~/.asciidoc
+# Global configuration files directory (set by Makefile build target).
+CONF_DIR = '/etc/asciidoc'
+HELP_FILE = 'help.conf'     # Default (English) help file.
+
+# Globals
+# -------
+document = Document()       # The document being processed.
+config = Config()           # Configuration file reader.
+reader = Reader()           # Input stream line reader.
+writer = Writer()           # Output stream line writer.
+message = Message()         # Message functions.
+paragraphs = Paragraphs()   # Paragraph definitions.
+lists = Lists()             # List definitions.
+blocks = DelimitedBlocks()  # DelimitedBlock definitions.
+tables_OLD = Tables_OLD()   # Table_OLD definitions.
+tables = Tables()           # Table definitions.
+macros = Macros()           # Macro definitions.
+calloutmap = CalloutMap()   # Coordinates callouts and callout list.
+trace = Trace()             # Implements trace attribute processing.
+
+### Used by asciidocapi.py ###
+# List of message strings written to stderr.
+messages = message.messages
+
+
+def asciidoc(backend, doctype, confiles, infile, outfile, options):
+    """Convert AsciiDoc document to DocBook document of type doctype
+    The AsciiDoc document is read from file object src the translated
+    DocBook file written to file object dst."""
+    def load_conffiles(include=[], exclude=[]):
+        # Load conf files specified on the command-line and by the conf-files attribute.
+        files = document.attributes.get('conf-files','')
+        files = [f.strip() for f in files.split('|') if f.strip()]
+        files += confiles
+        if files:
+            for f in files:
+                if os.path.isfile(f):
+                    config.load_file(f, include=include, exclude=exclude)
+                else:
+                    raise EAsciiDoc,'configuration file %s missing' % f
+
+    try:
+        if doctype not in (None,'article','manpage','book'):
+            raise EAsciiDoc,'illegal document type'
+        # Set processing options.
+        for o in options:
+            if o == '-c': config.dumping = True
+            if o == '-s': config.header_footer = False
+            if o == '-v': config.verbose = True
+        document.update_attributes()
+        if '-e' not in options:
+            # Load asciidoc.conf files in two passes: the first for attributes
+            # the second for everything. This is so that locally set attributes
+            # available are in the global asciidoc.conf
+            if not config.load_from_dirs('asciidoc.conf',include=['attributes']):
+                raise EAsciiDoc,'configuration file asciidoc.conf missing'
+            load_conffiles(include=['attributes'])
+            config.load_from_dirs('asciidoc.conf')
+            if infile != '<stdin>':
+                indir = os.path.dirname(infile)
+                config.load_file('asciidoc.conf', indir,
+                                include=['attributes','titles','specialchars'])
+        else:
+            load_conffiles(include=['attributes','titles','specialchars'])
+        document.update_attributes()
+        # Check the infile exists.
+        if infile != '<stdin>':
+            if not os.path.isfile(infile):
+                raise EAsciiDoc,'input file %s missing' % infile
+        document.infile = infile
+        AttributeList.initialize()
+        # Open input file and parse document header.
+        reader.tabsize = config.tabsize
+        reader.open(infile)
+        has_header = document.parse_header(doctype,backend)
+        # doctype is now finalized.
+        document.attributes['doctype-'+document.doctype] = ''
+        # Load backend configuration files.
+        if '-e' not in options:
+            f = document.backend + '.conf'
+            if not config.find_in_dirs(f):
+                message.warning('missing backend conf file: %s' % f, linenos=False)
+            config.load_backend()
+        # backend is now known.
+        document.attributes['backend-'+document.backend] = ''
+        document.attributes[document.backend+'-'+document.doctype] = ''
+        doc_conffiles = []
+        if '-e' not in options:
+            # Load filters and language file.
+            config.load_filters()
+            document.load_lang()
+            if infile != '<stdin>':
+                # Load local conf files (files in the source file directory).
+                config.load_file('asciidoc.conf', indir)
+                config.load_backend([indir])
+                config.load_filters([indir])
+                # Load document specific configuration files.
+                f = os.path.splitext(infile)[0]
+                doc_conffiles = [
+                        f for f in (f+'.conf', f+'-'+document.backend+'.conf')
+                        if os.path.isfile(f) ]
+                for f in doc_conffiles:
+                    config.load_file(f)
+        load_conffiles()
+        # Build asciidoc-args attribute.
+        args = ''
+        # Add custom conf file arguments.
+        for f in doc_conffiles + confiles:
+            args += ' --conf-file "%s"' % f
+        # Add command-line and header attributes.
+        attrs = {}
+        attrs.update(AttributeEntry.attributes)
+        attrs.update(config.cmd_attrs)
+        if 'title' in attrs:    # Don't pass the header title.
+            del attrs['title']
+        for k,v in attrs.items():
+            if v:
+                args += ' --attribute "%s=%s"' % (k,v)
+            else:
+                args += ' --attribute "%s"' % k
+        document.attributes['asciidoc-args'] = args
+        # Build outfile name.
+        if outfile is None:
+            outfile = os.path.splitext(infile)[0] + '.' + document.backend
+            if config.outfilesuffix:
+                # Change file extension.
+                outfile = os.path.splitext(outfile)[0] + config.outfilesuffix
+        document.outfile = outfile
+        # Document header attributes override conf file attributes.
+        document.attributes.update(AttributeEntry.attributes)
+        document.update_attributes()
+        # Configuration is fully loaded so can expand templates.
+        config.expand_all_templates()
+        # Check configuration for consistency.
+        config.validate()
+        paragraphs.initialize()
+        lists.initialize()
+        if config.dumping:
+            config.dump()
+        else:
+            writer.newline = config.newline
+            try:
+                writer.open(outfile, reader.bom)
+                try:
+                    document.translate(has_header) # Generate the output.
+                finally:
+                    writer.close()
+            finally:
+                reader.closefile()
+    except KeyboardInterrupt:
+        raise
+    except Exception,e:
+        # Cleanup.
+        if outfile and outfile != '<stdout>' and os.path.isfile(outfile):
+            os.unlink(outfile)
+        # Build and print error description.
+        msg = 'FAILED: '
+        if reader.cursor:
+            msg = message.format('', msg)
+        if isinstance(e, EAsciiDoc):
+            message.stderr('%s%s' % (msg,str(e)))
+        else:
+            if __name__ == '__main__':
+                message.stderr(msg+'unexpected error:')
+                message.stderr('-'*60)
+                traceback.print_exc(file=sys.stderr)
+                message.stderr('-'*60)
+            else:
+                message.stderr('%sunexpected error: %s' % (msg,str(e)))
+        sys.exit(1)
+
+def usage(msg=''):
+    if msg:
+        message.stderr(msg)
+    show_help('default', sys.stderr)
+
+def show_help(topic, f=None):
+    """Print help topic to file object f."""
+    if f is None:
+        f = sys.stdout
+    # Select help file.
+    lang = config.cmd_attrs.get('lang')
+    if lang and lang != 'en':
+        help_file = 'help-' + lang + '.conf'
+    else:
+        help_file = HELP_FILE
+    # Print [topic] section from help file.
+    config.load_from_dirs(help_file)
+    if len(config.sections) == 0:
+        # Default to English if specified language help files not found.
+        help_file = HELP_FILE
+        config.load_from_dirs(help_file)
+    if len(config.sections) == 0:
+        message.stderr('no help topics found')
+        sys.exit(1)
+    n = 0
+    for k in config.sections:
+        if re.match(re.escape(topic), k):
+            n += 1
+            lines = config.sections[k]
+    if n == 0:
+        if topic != 'topics':
+            message.stderr('help topic not found: [%s] in %s' % (topic, help_file))
+        message.stderr('available help topics: %s' % ', '.join(config.sections.keys()))
+        sys.exit(1)
+    elif n > 1:
+        message.stderr('ambiguous help topic: %s' % topic)
+    else:
+        for line in lines:
+            print >>f, line
+
+### Used by asciidocapi.py ###
+def execute(cmd,opts,args):
+    """
+    Execute asciidoc with command-line options and arguments.
+    cmd is asciidoc command or asciidoc.py path.
+    opts and args conform to values returned by getopt.getopt().
+    Raises SystemExit if an error occurs.
+
+    Doctests:
+
+    1. Check execution:
+
+       >>> import StringIO
+       >>> infile = StringIO.StringIO('Hello *{author}*')
+       >>> outfile = StringIO.StringIO()
+       >>> opts = []
+       >>> opts.append(('--backend','html4'))
+       >>> opts.append(('--no-header-footer',None))
+       >>> opts.append(('--attribute','author=Joe Bloggs'))
+       >>> opts.append(('--out-file',outfile))
+       >>> execute(__file__, opts, [infile])
+       >>> print outfile.getvalue()
+       <p>Hello <strong>Joe Bloggs</strong></p>
+
+       >>>
+
+    """
+    config.init(cmd)
+    if len(args) > 1:
+        usage('To many arguments')
+        sys.exit(1)
+    backend = None
+    doctype = None
+    confiles = []
+    outfile = None
+    options = []
+    help_option = False
+    for o,v in opts:
+        if o in ('--help','-h'):
+            help_option = True
+        #DEPRECATED: --unsafe option.
+        if o == '--unsafe':
+            document.safe = False
+        if o == '--safe':
+            document.safe = True
+        if o == '--version':
+            print('asciidoc %s' % VERSION)
+            sys.exit(0)
+        if o in ('-b','--backend'):
+            backend = v
+#            config.cmd_attrs['backend'] = v
+        if o in ('-c','--dump-conf'):
+            options.append('-c')
+        if o in ('-d','--doctype'):
+            doctype = v
+#            config.cmd_attrs['doctype'] = v
+        if o in ('-e','--no-conf'):
+            options.append('-e')
+        if o in ('-f','--conf-file'):
+            confiles.append(v)
+        if o in ('-n','--section-numbers'):
+            o = '-a'
+            v = 'numbered'
+        if o in ('-a','--attribute'):
+            e = parse_entry(v, allow_name_only=True)
+            if not e:
+                usage('Illegal -a option: %s' % v)
+                sys.exit(1)
+            k,v = e
+            # A @ suffix denotes don't override existing document attributes.
+            if v and v[-1] == '@':
+                document.attributes[k] = v[:-1]
+            else:
+                config.cmd_attrs[k] = v
+        if o in ('-o','--out-file'):
+            outfile = v
+        if o in ('-s','--no-header-footer'):
+            options.append('-s')
+        if o in ('-v','--verbose'):
+            options.append('-v')
+    if help_option:
+        if len(args) == 0:
+            show_help('default')
+        else:
+            show_help(args[-1])
+        sys.exit(0)
+    if len(args) == 0 and len(opts) == 0:
+        usage()
+        sys.exit(0)
+    if len(args) == 0:
+        usage('No source file specified')
+        sys.exit(1)
+#    if not backend:
+#        usage('No --backend option specified')
+#        sys.exit(1)
+    stdin,stdout = sys.stdin,sys.stdout
+    try:
+        infile = args[0]
+        if infile == '-':
+            infile = '<stdin>'
+        elif isinstance(infile, str):
+            infile = os.path.abspath(infile)
+        else:   # Input file is file object from API call.
+            sys.stdin = infile
+            infile = '<stdin>'
+        if outfile == '-':
+            outfile = '<stdout>'
+        elif isinstance(outfile, str):
+            outfile = os.path.abspath(outfile)
+        elif outfile is None:
+            if infile == '<stdin>':
+                outfile = '<stdout>'
+        else:   # Output file is file object from API call.
+            sys.stdout = outfile
+            outfile = '<stdout>'
+        # Do the work.
+        asciidoc(backend, doctype, confiles, infile, outfile, options)
+        if document.has_errors:
+            sys.exit(1)
+    finally:
+        sys.stdin,sys.stdout = stdin,stdout
+
+if __name__ == '__main__':
+    # Process command line options.
+    import getopt
+    try:
+        #DEPRECATED: --unsafe option.
+        opts,args = getopt.getopt(sys.argv[1:],
+            'a:b:cd:ef:hno:svw:',
+            ['attribute=','backend=','conf-file=','doctype=','dump-conf',
+            'help','no-conf','no-header-footer','out-file=',
+            'section-numbers','verbose','version','safe','unsafe',
+            'doctest','filter'])
+    except getopt.GetoptError:
+        message.stderr('illegal command options')
+        sys.exit(1)
+    if '--doctest' in [opt[0] for opt in opts]:
+        # Run module doctests.
+        import doctest
+        options = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS
+        failures,tries = doctest.testmod(optionflags=options)
+        if failures == 0:
+            message.stderr('All doctests passed')
+            sys.exit(0)
+        else:
+            sys.exit(1)
+    if '--filter' in [opt[0] for opt in opts]:
+        config.init(sys.argv[0])
+        config.verbose = bool(set(['-v','--verbose']) & set([opt[0] for opt in opts]))
+        if not args:
+            die('missing --filter command')
+        elif args[0] == 'install':
+            Filter.install(args[1:])
+        elif args[0] == 'remove':
+            Filter.remove(args[1:])
+        elif args[0] == 'list':
+            Filter.list()
+        else:
+            die('illegal --filter command: %s' % args[0])
+        sys.exit(0)
+    try:
+        execute(sys.argv[0],opts,args)
+    except KeyboardInterrupt:
+        sys.exit(1)
diff --git a/doc/www/create-relnotes.sh b/doc/www/create-relnotes.sh
new file mode 100755 (executable)
index 0000000..9e731e8
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+# 
+# This script requires asciidoc http://www.methods.co.nz/asciidoc/
+
+# (echo -e "Netatalk NEWS\n-------------\n\n" ; cat NEWS) | ./asciidoc.py -f netatalk-news.conf -b css -o NEWS.html - && chmod g+w NEWS.html
+./asciidoc.py -f netatalk-relnotes.conf -b html5 -o ReleaseNotes.html ReleaseNotes && chmod g+w ReleaseNotes.html
diff --git a/doc/www/html5.conf b/doc/www/html5.conf
new file mode 100644 (file)
index 0000000..cedc3fd
--- /dev/null
@@ -0,0 +1,686 @@
+#
+# html5.conf
+#
+# Asciidoc configuration file.
+# html5 backend.
+#
+
+[miscellaneous]
+outfilesuffix=.html
+
+[attributes]
+basebackend=html
+basebackend-html=
+basebackend-html5=
+
+[replacements2]
+# Line break.
+(?m)^(.*)\s\+$=\1<br>
+
+[replacements]
+ifdef::asciidoc7compatible[]
+# Superscripts.
+\^(.+?)\^=<sup>\1</sup>
+# Subscripts.
+~(.+?)~=<sub>\1</sub>
+endif::asciidoc7compatible[]
+
+[ruler-blockmacro]
+<hr>
+
+[pagebreak-blockmacro]
+<div style="page-break-after:always"></div>
+
+[blockdef-pass]
+asciimath-style=template="asciimathblock",subs=[]
+latexmath-style=template="latexmathblock",subs=[]
+
+[macros]
+(?u)^(?P<name>audio|video)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
+# math macros.
+# Special characters are escaped in HTML math markup.
+(?su)[\\]?(?P<name>asciimath|latexmath):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[specialcharacters]
+(?u)^(?P<name>asciimath|latexmath)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#[specialcharacters]
+
+[asciimath-inlinemacro]
+`{passtext}`
+
+[asciimath-blockmacro]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+`{passtext}`
+</div></div>
+
+[asciimathblock]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+`|`
+</div></div>
+
+[latexmath-inlinemacro]
+{passtext}
+
+[latexmath-blockmacro]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+{passtext}
+</div></div>
+
+[latexmathblock]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+|
+</div></div>
+
+[image-inlinemacro]
+<span class="image{role? {role}}">
+<a class="image" href="{link}">
+{data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"}>
+{data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
+{data-uri#}{sys3:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}">
+{link#}</a>
+</span>
+
+[image-blockmacro]
+<div class="imageblock{style? {style}}{role? {role}}"{id? id="{id}"}{align? style="text-align:{align};"}{float? style="float:{float};"}>
+<div class="content">
+<a class="image" href="{link}">
+{data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}>
+{data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
+{data-uri#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}">
+{link#}</a>
+</div>
+<div class="title">{caption={figure-caption} {counter:figure-number}. }{title}</div>
+</div>
+
+[audio-blockmacro]
+<div class="audioblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content">
+<audio src="{imagesdir=}{imagesdir?/}{target}"{autoplay-option? autoplay}{nocontrols-option! controls}{loop-option? loop}>
+Your browser does not support the audio tag.
+</audio>
+</div></div>
+
+[video-blockmacro]
+<div class="videoblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content">
+<video src="{imagesdir=}{imagesdir?/}{target}"{width? width="{width}"}{height? height="{height}"}{poster? poster="{poster}"}{autoplay-option? autoplay}{nocontrols-option! controls}{loop-option? loop}>
+Your browser does not support the video tag.
+</video>
+</div></div>
+
+[unfloat-blockmacro]
+<div style="clear:both;"></div>
+
+[indexterm-inlinemacro]
+# Index term.
+{empty}
+
+[indexterm2-inlinemacro]
+# Index term.
+# Single entry index term that is visible in the primary text flow.
+{1}
+
+[footnote-inlinemacro]
+# footnote:[<text>].
+<span class="footnote"><br>[{0}]<br></span>
+
+[footnoteref-inlinemacro]
+# footnoteref:[<id>], create reference to footnote.
+{2%}<span class="footnoteref"><br><a href="#_footnote_{1}">[{1}]</a><br></span>
+# footnoteref:[<id>,<text>], create footnote with ID.
+{2#}<span class="footnote" id="_footnote_{1}"><br>[{2}]<br></span>
+
+[callout-inlinemacro]
+ifndef::icons[]
+<b>&lt;{index}&gt;</b>
+endif::icons[]
+ifdef::icons[]
+ifndef::data-uri[]
+<img src="{icon={iconsdir}/callouts/{index}.png}" alt="{index}">
+endif::data-uri[]
+ifdef::data-uri[]
+<img alt="{index}" src="data:image/png;base64,
+{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{index}.png}")}"}">
+endif::data-uri[]
+endif::icons[]
+
+# Comment line macros.
+[comment-inlinemacro]
+{showcomments#}<br><span class="comment">{passtext}</span><br>
+
+[comment-blockmacro]
+{showcomments#}<p><span class="comment">{passtext}</span></p>
+
+[literal-inlinemacro]
+# Inline literal.
+<span class="monospaced">{passtext}</span>
+
+# List tags.
+[listtags-bulleted]
+list=<div class="ulist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[listtags-numbered]
+# The start attribute is not valid XHTML 1.1 but all browsers support it.
+list=<div class="olist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol class="{style}"{start? start="{start}"}>|</ol></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[listtags-labeled]
+list=<div class="dlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
+entry=
+label=
+term=<dt class="hdlist1{strong-option? strong}">|</dt>
+item=<dd>|</dd>
+text=<p>|</p>
+
+[listtags-horizontal]
+list=<div class="hdlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>{labelwidth?<col width="{labelwidth}%">}{itemwidth?<col width="{itemwidth}%">}|</table></div>
+label=<td class="hdlist1{strong-option? strong}">|</td>
+term=|<br>
+entry=<tr>|</tr>
+item=<td class="hdlist2">|</td>
+text=<p style="margin-top: 0;">|</p>
+
+[listtags-qanda]
+list=<div class="qlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
+entry=<li>|</li>
+label=
+term=<p><em>|</em></p>
+item=
+text=<p>|</p>
+
+[listtags-callout]
+ifndef::icons[]
+list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
+item=<li>|</li>
+text=<p>|</p>
+endif::icons[]
+ifdef::icons[]
+list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>|</table></div>
+ifndef::data-uri[]
+item=<tr><td><img src="{iconsdir}/callouts/{listindex}.png" alt="{listindex}"></td><td>|</td></tr>
+endif::data-uri[]
+ifdef::data-uri[]
+item=<tr><td><img alt="{listindex}" src="data:image/png;base64, {sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{listindex}.png}")}"}"></td><td>|</td></tr>
+endif::data-uri[]
+text=|
+endif::icons[]
+
+[listtags-glossary]
+list=<div class="dlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
+label=
+entry=
+term=<dt>|</dt>
+item=<dd>|</dd>
+text=<p>|</p>
+
+[listtags-bibliography]
+list=<div class="ulist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[tags]
+# Quoted text.
+emphasis=<em>{1?<span class="{1}">}|{1?</span>}</em>
+strong=<strong>{1?<span class="{1}">}|{1?</span>}</strong>
+monospaced=<span class="monospaced{1? {1}}">|</span>
+singlequoted={lsquo}{1?<span class="{1}">}|{1?</span>}{rsquo}
+doublequoted={ldquo}{1?<span class="{1}">}|{1?</span>}{rdquo}
+unquoted={1?<span class="{1}">}|{1?</span>}
+superscript=<sup>{1?<span class="{1}">}|{1?</span>}</sup>
+subscript=<sub>{1?<span class="{1}">}|{1?</span>}</sub>
+
+ifdef::deprecated-quotes[]
+# Override with deprecated quote attributes.
+emphasis={role?<span class="{role}">}<em{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</em>{role?</span>}
+strong={role?<span class="{role}">}<strong{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</strong>{role?</span>}
+monospaced=<span class="monospaced{role? {role}}"{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</span>
+singlequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8216;|{amp}#8217;{1,2,3?</span>}{role?</span>}
+doublequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8220;|{amp}#8221;{1,2,3?</span>}{role?</span>}
+unquoted={role?<span class="{role}">}{1,2,3?<span style="{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}">}|{1,2,3?</span>}{role?</span>}
+superscript={role?<span class="{role}">}<sup{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sup>{role?</span>}
+subscript={role?<span class="{role}">}<sub{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sub>{role?</span>}
+endif::deprecated-quotes[]
+
+# Inline macros
+[http-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[https-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[ftp-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[file-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[irc-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[mailto-inlinemacro]
+<a href="mailto:{target}">{0={target}}</a>
+[link-inlinemacro]
+<a href="{target}">{0={target}}</a>
+[callto-inlinemacro]
+<a href="{name}:{target}">{0={target}}</a>
+# anchor:id[text]
+[anchor-inlinemacro]
+<a id="{target}"></a>
+# [[id,text]]
+[anchor2-inlinemacro]
+<a id="{1}"></a>
+# [[[id]]]
+[anchor3-inlinemacro]
+<a id="{1}"></a>[{1}]
+# xref:id[text]
+[xref-inlinemacro]
+<a href="#{target}">{0=[{target}]}</a>
+# <<id,text>>
+[xref2-inlinemacro]
+<a href="#{1}">{2=[{1}]}</a>
+
+# Special word substitution.
+[emphasizedwords]
+<em>{words}</em>
+[monospacedwords]
+<span class="monospaced">{words}</span>
+[strongwords]
+<strong>{words}</strong>
+
+# Paragraph substitution.
+[paragraph]
+<div class="paragraph{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<p>
+|
+</p></div>
+
+[admonitionparagraph]
+template::[admonitionblock]
+
+# Delimited blocks.
+[listingblock]
+<div class="listingblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content monospaced">
+<pre>
+|
+</pre>
+</div></div>
+
+[literalblock]
+<div class="literalblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content monospaced">
+<pre>
+|
+</pre>
+</div></div>
+
+[sidebarblock]
+<div class="sidebarblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+|
+</div></div>
+
+[openblock]
+<div class="openblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content">
+|
+</div></div>
+
+[partintroblock]
+template::[openblock]
+
+[abstractblock]
+template::[quoteblock]
+
+[quoteblock]
+<div class="quoteblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content">
+|
+</div>
+<div class="attribution">
+<em>{citetitle}</em>{attribution?<br>}
+&#8212; {attribution}
+</div></div>
+
+[verseblock]
+<div class="verseblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<pre class="content">
+|
+</pre>
+<div class="attribution">
+<em>{citetitle}</em>{attribution?<br>}
+&#8212; {attribution}
+</div></div>
+
+[exampleblock]
+<div class="exampleblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption={example-caption} {counter:example-number}. }{title}</div>
+<div class="content">
+|
+</div></div>
+
+[admonitionblock]
+<div class="admonitionblock{role? {role}}"{id? id="{id}"}>
+<table><tr>
+<td class="icon">
+{data-uri%}{icons#}<img src="{icon={iconsdir}/{name}.png}" alt="{caption}">
+{data-uri#}{icons#}<img alt="{caption}" src="data:image/png;base64,
+{data-uri#}{icons#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/{name}.png}")}"}">
+{icons%}<div class="title">{caption}</div>
+</td>
+<td class="content">
+<div class="title">{title}</div>
+|
+</td>
+</tr></table>
+</div>
+
+# Tables.
+[tabletags-default]
+colspec=<col{autowidth-option! style="width:{colpcwidth}%;"}>
+bodyrow=<tr>|</tr>
+headdata=<th class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }>|</th>
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }>|</td>
+paragraph=<p class="tableblock">|</p>
+
+[tabletags-header]
+paragraph=<p class="tableblock header">|</p>
+
+[tabletags-emphasis]
+paragraph=<p class="tableblock"><em>|</em></p>
+
+[tabletags-strong]
+paragraph=<p class="tableblock"><strong>|</strong></p>
+
+[tabletags-monospaced]
+paragraph=<p class="tableblock monospaced">|</p>
+
+[tabletags-verse]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div class="verse">|</div></td>
+paragraph=
+
+[tabletags-literal]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div class="literal monospaced"><pre>|</pre></div></td>
+paragraph=
+
+[tabletags-asciidoc]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div>|</div></td>
+paragraph=
+
+[table]
+<table class="tableblock frame-{frame=all} grid-{grid=all}{role? {role}}"{id? id="{id}"}
+style="
+margin-left:{align@left:0}{align@center|right:auto}; margin-right:{align@left|center:auto}{align@right:0};
+float:{float};
+{autowidth-option%}width:{tablepcwidth}%;
+{autowidth-option#}{width#style=width:{tablepcwidth}%;}
+">
+<caption class="title">{caption={table-caption} {counter:table-number}. }{title}</caption>
+{colspecs}
+{headrows#}<thead>
+{headrows}
+{headrows#}</thead>
+{footrows#}<tfoot>
+{footrows}
+{footrows#}</tfoot>
+<tbody>
+{bodyrows}
+</tbody>
+</table>
+
+#--------------------------------------------------------------------
+# Deprecated old table definitions.
+#
+
+[miscellaneous]
+# Screen width in pixels.
+pagewidth=800
+pageunits=px
+
+[old_tabledef-default]
+template=old_table
+colspec=<col style="width:{colwidth}{pageunits};" />
+bodyrow=<tr>|</tr>
+headdata=<th class="tableblock halign-{colalign=left}">|</th>
+footdata=<td class="tableblock halign-{colalign=left}">|</td>
+bodydata=<td class="tableblock halign-{colalign=left}">|</td>
+
+[old_table]
+<table class="tableblock frame-{frame=all} grid-{grid=all}"{id? id="{id}"}>
+<caption class="title">{caption={table-caption}}{title}</caption>
+{colspecs}
+{headrows#}<thead>
+{headrows}
+{headrows#}</thead>
+{footrows#}<tfoot>
+{footrows}
+{footrows#}</tfoot>
+<tbody style="vertical-align:top;">
+{bodyrows}
+</tbody>
+</table>
+
+# End of deprecated old table definitions.
+#--------------------------------------------------------------------
+
+[floatingtitle]
+<h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}{id? id="{id}"} class="float">{title}</h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}>
+
+[preamble]
+# Untitled elements between header and first section title.
+<div id="preamble">
+<div class="sectionbody">
+|
+</div>
+</div>
+
+# Document sections.
+[sect0]
+<h1{id? id="{id}"}>{title}</h1>
+|
+
+[sect1]
+<div class="sect1{style? {style}}{role? {role}}">
+<h2{id? id="{id}"}>{numbered?{sectnum} }{title}</h2>
+<div class="sectionbody">
+|
+</div>
+</div>
+
+[sect2]
+<div class="sect2{style? {style}}{role? {role}}">
+<h3{id? id="{id}"}>{numbered?{sectnum} }{title}</h3>
+|
+</div>
+
+[sect3]
+<div class="sect3{style? {style}}{role? {role}}">
+<h4{id? id="{id}"}>{numbered?{sectnum} }{title}</h4>
+|
+</div>
+
+[sect4]
+<div class="sect4{style? {style}}{role? {role}}">
+<h5{id? id="{id}"}>{title}</h5>
+|
+</div>
+
+[appendix]
+<div class="sect1{style? {style}}{role? {role}}">
+<h2{id? id="{id}"}>{numbered?{sectnum} }{appendix-caption} {counter:appendix-number:A}: {title}</h2>
+<div class="sectionbody">
+|
+</div>
+</div>
+
+[toc]
+<div id="toc">
+  <div id="toctitle">{toc-title}</div>
+  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
+</div>
+
+[header]
+<!DOCTYPE html>
+<html lang="{lang=en}">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={encoding}">
+<meta name="generator" content="AsciiDoc {asciidoc-version}">
+<meta name="description" content="{description}">
+<meta name="keywords" content="{keywords}">
+<title>{title}</title>
+{title%}<title>{doctitle=}</title>
+ifdef::linkcss[]
+<link rel="stylesheet" href="{stylesdir=.}/{theme=asciidoc}.css" type="text/css">
+{doctype-manpage}<link rel="stylesheet" href="{stylesdir=.}/{theme=asciidoc}-manpage.css" type="text/css">
+ifdef::pygments[<link rel="stylesheet" href="{stylesdir=.}/pygments.css" type="text/css">]
+ifdef::toc2[<link rel="stylesheet" href="{stylesdir=.}/toc2.css" type="text/css" />]
+<link rel="stylesheet" href="{stylesdir=.}/{stylesheet}" type="text/css">
+endif::linkcss[]
+ifndef::linkcss[]
+<style type="text/css">
+include1::{stylesdir=./stylesheets}/{theme=asciidoc}.css[]
+ifdef::doctype-manpage[]
+include1::{stylesdir=./stylesheets}/{theme=asciidoc}-manpage.css[]
+endif::doctype-manpage[]
+ifdef::pygments[]
+include1::{stylesdir=./stylesheets}/pygments.css[]
+endif::pygments[]
+ifdef::toc2[]
+include1::{stylesdir=./stylesheets}/toc2.css[]
+endif::toc2[]
+include1::{stylesheet}[]
+</style>
+endif::linkcss[]
+ifndef::disable-javascript[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/asciidoc.js"></script>
+<script type="text/javascript">
+#TODO: Escape not necessary in HTML5?
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+asciidoc.install({toc,toc2?{toclevels}});
+/*]]>*/
+</script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/asciidoc.js[]
+asciidoc.install({toc,toc2?{toclevels}});
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::disable-javascript[]
+ifdef::asciimath[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/ASCIIMathML.js"></script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/ASCIIMathML.js[]
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::asciimath[]
+ifdef::latexmath[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/LaTeXMathML.js"></script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/LaTeXMathML.js[]
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::latexmath[]
+{docinfo1,docinfo2#}{include:{docdir}/docinfo.html}
+{docinfo,docinfo2#}{include:{docdir}/{docname}-docinfo.html}
+</head>
+<body class="{doctype}"{max-width? style="max-width:{max-width}"}>
+# Article, book header.
+ifndef::doctype-manpage[]
+<div id="header">
+ifndef::notitle[<h1>{doctitle}</h1>]
+ifdef::doctitle[]
+<span id="author">{author}</span><br>
+<span id="email" class="monospaced">&lt;<a href="mailto:{email}">{email}</a>&gt;</span><br>
+<span id="revnumber">version {revnumber}{revdate?,}</span>
+<span id="revdate">{revdate}</span>
+<br><span id="revremark">{revremark}</span>
+endif::doctitle[]
+ifdef::toc,toc2[{template:toc}]
+</div>
+endif::doctype-manpage[]
+# Man page header.
+ifdef::doctype-manpage[]
+<div id="header">
+<h1>
+{doctitle} Manual Page
+</h1>
+ifdef::toc,toc2[{template:toc}]
+<h2>{manname-title}</h2>
+<div class="sectionbody">
+<p>{manname} -
+   {manpurpose}
+</p>
+</div>
+</div>
+endif::doctype-manpage[]
+<div id="content">
+
+[footer]
+</div>
+{disable-javascript%<div id="footnotes"><hr></div>}
+<div id="footer">
+<div id="footer-text">
+template::[footer-text]
+</div>
+ifdef::badges[]
+<div id="footer-badges">
+ifndef::icons[]
+Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a>
+and <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>.
+endif::icons[]
+ifdef::icons[]
+<a href="http://validator.w3.org/check?uri=referer">
+  <img style="border:0;width:88px;height:31px"
+    src="http://www.w3.org/Icons/valid-xhtml11-blue"
+    alt="Valid XHTML 1.1" height="31" width="88">
+</a>
+<a href="http://jigsaw.w3.org/css-validator/">
+  <img style="border:0;width:88px;height:31px"
+    src="http://jigsaw.w3.org/css-validator/images/vcss-blue"
+    alt="Valid CSS!">
+</a>
+<a href="http://www.mozilla.org/products/firefox/">
+  <img style="border:none; width:110px; height:32px;"
+       src="http://www.spreadfirefox.com/community/images/affiliates/Buttons/110x32/safer.gif"
+       alt="Get Firefox!">
+</a>
+endif::icons[]
+</div>
+endif::badges[]
+</div>
+</body>
+</html>
+
+ifdef::doctype-manpage[]
+[synopsis]
+template::[sect1]
+endif::doctype-manpage[]
+
diff --git a/doc/www/javascripts/asciidoc.js b/doc/www/javascripts/asciidoc.js
new file mode 100644 (file)
index 0000000..2ad6c41
--- /dev/null
@@ -0,0 +1,189 @@
+var asciidoc = {  // Namespace.
+
+/////////////////////////////////////////////////////////////////////
+// Table Of Contents generator
+/////////////////////////////////////////////////////////////////////
+
+/* Author: Mihai Bazon, September 2002
+ * http://students.infoiasi.ro/~mishoo
+ *
+ * Table Of Content generator
+ * Version: 0.4
+ *
+ * Feel free to use this script under the terms of the GNU General Public
+ * License, as long as you do not remove or alter this notice.
+ */
+
+ /* modified by Troy D. Hanson, September 2006. License: GPL */
+ /* modified by Stuart Rackham, 2006, 2009. License: GPL */
+
+// toclevels = 1..4.
+toc: function (toclevels) {
+
+  function getText(el) {
+    var text = "";
+    for (var i = el.firstChild; i != null; i = i.nextSibling) {
+      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
+        text += i.data;
+      else if (i.firstChild != null)
+        text += getText(i);
+    }
+    return text;
+  }
+
+  function TocEntry(el, text, toclevel) {
+    this.element = el;
+    this.text = text;
+    this.toclevel = toclevel;
+  }
+
+  function tocEntries(el, toclevels) {
+    var result = new Array;
+    var re = new RegExp('[hH]([2-'+(toclevels+1)+'])');
+    // Function that scans the DOM tree for header elements (the DOM2
+    // nodeIterator API would be a better technique but not supported by all
+    // browsers).
+    var iterate = function (el) {
+      for (var i = el.firstChild; i != null; i = i.nextSibling) {
+        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
+          var mo = re.exec(i.tagName);
+          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
+            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
+          }
+          iterate(i);
+        }
+      }
+    }
+    iterate(el);
+    return result;
+  }
+
+  var toc = document.getElementById("toc");
+  if (!toc) {
+    return;
+  }
+
+  // Delete existing TOC entries in case we're reloading the TOC.
+  var tocEntriesToRemove = [];
+  var i;
+  for (i = 0; i < toc.childNodes.length; i++) {
+    var entry = toc.childNodes[i];
+    if (entry.nodeName == 'DIV'
+     && entry.getAttribute("class")
+     && entry.getAttribute("class").match(/^toclevel/))
+      tocEntriesToRemove.push(entry);
+  }
+  for (i = 0; i < tocEntriesToRemove.length; i++) {
+    toc.removeChild(tocEntriesToRemove[i]);
+  }
+  
+  // Rebuild TOC entries.
+  var entries = tocEntries(document.getElementById("content"), toclevels);
+  for (var i = 0; i < entries.length; ++i) {
+    var entry = entries[i];
+    if (entry.element.id == "")
+      entry.element.id = "_toc_" + i;
+    var a = document.createElement("a");
+    a.href = "#" + entry.element.id;
+    a.appendChild(document.createTextNode(entry.text));
+    var div = document.createElement("div");
+    div.appendChild(a);
+    div.className = "toclevel" + entry.toclevel;
+    toc.appendChild(div);
+  }
+  if (entries.length == 0)
+    toc.parentNode.removeChild(toc);
+},
+
+
+/////////////////////////////////////////////////////////////////////
+// Footnotes generator
+/////////////////////////////////////////////////////////////////////
+
+/* Based on footnote generation code from:
+ * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
+ */
+
+footnotes: function () {
+  // Delete existing footnote entries in case we're reloading the footnodes.
+  var i;
+  var noteholder = document.getElementById("footnotes");
+  if (!noteholder) {
+    return;
+  }
+  var entriesToRemove = [];
+  for (i = 0; i < noteholder.childNodes.length; i++) {
+    var entry = noteholder.childNodes[i];
+    if (entry.nodeName == 'DIV' && entry.getAttribute("class") == "footnote")
+      entriesToRemove.push(entry);
+  }
+  for (i = 0; i < entriesToRemove.length; i++) {
+    noteholder.removeChild(entriesToRemove[i]);
+  }
+
+  // Rebuild footnote entries.
+  var cont = document.getElementById("content");
+  var spans = cont.getElementsByTagName("span");
+  var refs = {};
+  var n = 0;
+  for (i=0; i<spans.length; i++) {
+    if (spans[i].className == "footnote") {
+      n++;
+      var note = spans[i].getAttribute("data-note");
+      if (!note) {
+        // Use [\s\S] in place of . so multi-line matches work.
+        // Because JavaScript has no s (dotall) regex flag.
+        note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
+        spans[i].innerHTML =
+          "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
+          "' title='View footnote' class='footnote'>" + n + "</a>]";
+        spans[i].setAttribute("data-note", note);
+      }
+      noteholder.innerHTML +=
+        "<div class='footnote' id='_footnote_" + n + "'>" +
+        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
+        n + "</a>. " + note + "</div>";
+      var id =spans[i].getAttribute("id");
+      if (id != null) refs["#"+id] = n;
+    }
+  }
+  if (n == 0)
+    noteholder.parentNode.removeChild(noteholder);
+  else {
+    // Process footnoterefs.
+    for (i=0; i<spans.length; i++) {
+      if (spans[i].className == "footnoteref") {
+        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
+        href = href.match(/#.*/)[0];  // Because IE return full URL.
+        n = refs[href];
+        spans[i].innerHTML =
+          "[<a href='#_footnote_" + n +
+          "' title='View footnote' class='footnote'>" + n + "</a>]";
+      }
+    }
+  }
+},
+
+install: function(toclevels) {
+  var timerId;
+
+  function reinstall() {
+    asciidoc.footnotes();
+    if (toclevels) {
+      asciidoc.toc(toclevels);
+    }
+  }
+
+  function reinstallAndRemoveTimer() {
+    clearInterval(timerId);
+    reinstall();
+  }
+
+  timerId = setInterval(reinstall, 500);
+  if (document.addEventListener)
+    document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
+  else
+    window.onload = reinstallAndRemoveTimer;
+}
+
+}
diff --git a/doc/www/lang-en.conf b/doc/www/lang-en.conf
new file mode 100644 (file)
index 0000000..9d1c6d4
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# AsciiDoc English language configuration file.
+#
+
+[attributes]
+# Captions, used by (X)HTML backends.
+# Captions on RHS are displayed in outputs.
+ifdef::basebackend-html[]
+
+caution-caption=Caution
+important-caption=Important
+note-caption=Note
+tip-caption=Tip
+warning-caption=Warning
+figure-caption=Figure
+table-caption=Table
+example-caption=Example
+toc-title=Table of Contents
+appendix-caption=Appendix
+# Man page NAME section title.
+manname-title=NAME
+
+[footer-text]
+Version {revnumber}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>}
+Last updated {docdate} {doctime}
+
+endif::basebackend-html[]
+
+
+[specialsections]
+# DocBook special sections.
+# The regular expression on LHS is matched against source titles.
+ifdef::basebackend-docbook[]
+
+ifdef::doctype-article[]
+^Abstract$=abstract
+endif::doctype-article[]
+
+ifdef::doctype-book[]
+^Colophon$=colophon
+^Dedication$=dedication
+^Preface$=preface
+endif::doctype-book[]
+
+^Index$=index
+^(Bibliography|References)$=bibliography
+^Glossary$=glossary
+^Appendix [A-Z][:.](?P<title>.*)$=appendix
+
+endif::basebackend-docbook[]
+
+ifdef::doctype-manpage[]
+(?i)^SYNOPSIS$=synopsis
+endif::doctype-manpage[]
diff --git a/doc/www/netatalk-relnotes.conf b/doc/www/netatalk-relnotes.conf
new file mode 100644 (file)
index 0000000..594ee2d
--- /dev/null
@@ -0,0 +1,285 @@
+#\r
+# netatalk.conf\r
+#\r
+# Asciidoc global configuration file.\r
+# css backend, generates XHTML 1.0 conformant markup.\r
+#\r
+# Included in css-embedded.conf\r
+#\r
+\r
+# Start with the html backend configuration.\r
+# include::html.conf[]\r
+\r
+[titles]\r
+underlines="--","==","~~","^^","++"\r
+\r
+[glossary]\r
+basebackend=css\r
+basebackend-css=\r
+basebackend-html\r
+dtddecl=PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"\r
+\r
+[tags]\r
+# Add title class.\r
+ilist={title?<p class="listtitle"><b>{title}</b></p>}<ul>|</ul>\r
+olist={title?<p class="listtitle"><b>{title}</b></p>}<ol>|</ol>\r
+vlist={title?<p class="listtitle"><b>{title}</b></p>}<dl>|</dl>\r
+qlist={title?<p class="listtitle"><b>{title}</b></p>}<ol>|</ol>\r
+\r
+[under-construction-blockmacro]\r
+<p class="under-construction">\r
+This page is currently under construction.<br/>\r
+Please return soon.\r
+</p>\r
+\r
+[image-inlinemacro]\r
+<a class="imagelink" href="{link}">\r
+# border="0" so broken IE6 does not put border round linked image.\r
+  <img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"} border="0"/>\r
+{link#}</a>\r
+\r
+[image-blockmacro]\r
+<div class="image">\r
+  <p>{link?<a class="imagelink" href="{link}">}\r
+# border="0" so broken IE6 does not put border round linked image.\r
+    <img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"} border="0"/>\r
+  {link?</a>}</p>\r
+  <p class="imagetitle"><b>Figure:</b> {title}</p>\r
+</div>\r
+\r
+# DEPRECATED.\r
+[graphic]\r
+<div class="graphic">\r
+  <p><img src="{target}" alt="{caption={target}}"/></p>\r
+  <p class="graphictitle"><b>Figure:</b> {title}</p>\r
+</div>\r
+\r
+# Paragraph substitution.\r
+[indentedparagraph]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+#<div class="indentedparagraph"><pre>|</pre></div>\r
+&nbsp;&nbsp;&nbsp;<a href="|">|</a>\r
+\r
+# Delimited block substitution.\r
+[indentedblock]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+<div class="indentedblock"><pre>\r
+|\r
+</pre></div>\r
+\r
+[verbatimblock]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+<div class="verbatimblock"><pre>\r
+|\r
+</pre></div>\r
+\r
+[sidebarblock]\r
+<div class="sidebarblock">\r
+<p class="sidebartitle">{title}</p>\r
+|\r
+</div>\r
+\r
+[customblock]\r
+|\r
+\r
+[table]\r
+# Table captions not used because IE6 is broken.\r
+<p class="tabletitle"><b>Table:</b> {title}</p>\r
+# If you want styled table borders in IE use the following table tag:\r
+# 1. Border style specified here rather than in CSS because IE6 is broken.\r
+# 2. bordercolor attribute is IE specific and not valid XHTML 1.0.\r
+#<table rules="groups" border="2" bordercolor="green" frame="hsides"\r
+#      cellspacing="0" cellpadding="4">\r
+#\r
+# Use this in preference to be strictly XHTML 1.0 conformant.\r
+<table rules="groups" frame="{noborders?void}{noborders!hsides}" cellspacing="0" cellpadding="4">\r
+{headrows#}<thead{noborders? style="border-width: 0;"}>\r
+{headrows}\r
+{headrows#}</thead>\r
+{footrows#}<tfoot{noborders? style="border-width: 0;"}>\r
+{footrows}\r
+{footrows#}</tfoot>\r
+<tbody{noborders? style="border-width: 0;"}>\r
+{bodyrows}\r
+</tbody>\r
+</table>\r
+\r
+#-------------------------\r
+# article and book document types\r
+# Both use the article.css stylesheet\r
+#-------------------------\r
+ifndef::doctype-manpage[]\r
+\r
+[header]\r
+<!DOCTYPE html {dtddecl}>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<meta name="description" content="Netatalk - Unix file and print services for Apple clients" />\r
+<meta name="keywords" content="Netatalk, AFP, AFP Server, File Server, PAP, Print Server, Appletalk, Mac, OSX, OS X, OS9, OS 9" />\r
+<meta name="language" content="EN" />\r
+<meta name="publisher" content="netatalk.sourceforge.net" />\r
+<meta name="robots" content="Follow" />\r
+<link rel="stylesheet" type="text/css" charset="iso-8859-1" href="/css/site.css" />\r
+<link rel="stylesheet" type="text/css" charset="iso-8859-1" href="/css/printer.css" media="print" />\r
+<link rel="alternate stylesheet" type="text/css" charset="iso-8859-1" href="/css/printer.css" title="Printer" />\r
+<link rel="copyright" title="GNU General Public License" href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt" />\r
+<link rel="author" title="The Netatalk Development Team" href="http://netatalk.sf.net" />\r
+<link rel="home" href="index.php" title="Netatalk Home" />\r
+<link rel="home" href="http://www.sourceforge.net/projects/netatalk" title="Netatalk Sourceforge" />\r
+<link rel="bookmark" href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Downloads" />\r
+\r
+<title>Netatalk Release Notes</title>\r
+</head>\r
+<body>\r
+<div id="header">\r
+<div id="logo"></div>\r
+<div id="menlinks">\r
+        <a href="/" title="Return to Netatalk home">[main]</a>\r
+        <a href="https://sourceforge.net/apps/mediawiki/netatalk/index.php?title=Main_Page" title="Netatalk Wiki">[wiki]</a>\r
+        <a href="/3.0/htmldocs" title="Netatalk Manual">[documentation]</a>\r
+        <a href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Download Netatalk from sourceforge">[downloads]</a>\r
+        <a href="/support.php" title="Support">[support]</a>\r
+        <a href="/links.php" title="Netatalk related links">[links]</a>\r
+        <img src="/gfx/end.gif" alt="" width="125" height="7" />\r
+</div>\r
+</div>\r
+<div id="header-print">\r
+<h1>netatalk.sourceforge.net</h1>\r
+</div>\r
+<div class="search">\r
+<h4> search netatalk.sf.net</h4>\r
+<form method="get" action="http://www.google.com/search">\r
+<p>\r
+<input type="text" name="q" size="10" maxlength="255" value="" />\r
+<input type="hidden" name="hl" value="de" />\r
+<input type="hidden" name="sitesearch" value="netatalk.sourceforge.net" />\r
+<input type="submit" name="btnG" value="Go" />\r
+</p>\r
+</form>\r
+<span class="italic">powered by Google</span>\r
+</div>\r
+<div id="content">\r
+<h1>{doctitle}</h1>\r
+\r
+[footer]\r
+</div>\r
+<div class="footer">\r
+<span class="italic">webspace sponsored by</span><br />\r
+<!-- use a table for now -->\r
+<table>\r
+<tr>\r
+<td><a href="http://www.sf.net"><img src="http://sourceforge.net/sflogo.php?group_id=8642&amp;type=1" style="border:0;width:88px;height:31px" width="88" height="31" alt="SourceForge.net Logo" /></a></td>\r
+<td><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></td>\r
+<td><a href="http://jigsaw.w3.org/css-validator/"><img style="border:0;width:88px;height:31px" src="http://jigsaw.w3.org/css-validator/images/vcss" alt="Valid CSS!" /></a></td>\r
+</tr>\r
+</table>\r
+</div>\r
+</body>\r
+</html>\r
+\r
+[preamble]\r
+# Untitled elements between header and first section title.\r
+<div id="body">\r
+|\r
+</div>\r
+\r
+[sect0]\r
+<h2 class="sect0">{1?<a name="{1}"></a>}{title}</h2>\r
+|\r
+\r
+[sect1]\r
+<h2>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h2>\r
+|\r
+\r
+[sect2]\r
+<h3>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h3>\r
+|\r
+\r
+[sect3]\r
+<h4>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h4>\r
+|\r
+\r
+[sect4]\r
+<h5>{1?<a name="{1}"></a>}{title}</h5>\r
+|\r
+\r
+endif::doctype-manpage[]\r
+\r
+#-------------------------\r
+# manpage document type\r
+#-------------------------\r
+ifdef::doctype-manpage[]\r
+\r
+[header]\r
+<!DOCTYPE html {dtddecl}>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />\r
+<meta name="generator" content="AsciiDoc {asciidoc-version}" />\r
+<meta name="author" content="{author}" />\r
+<meta name="author-email" content="{email}" />\r
+<link rel="stylesheet" href="manpage.css" type="text/css" />\r
+<title>{mantitle}</title>\r
+</head>\r
+<body>\r
+<div id="content">\r
+<h1>{doctitle} Manual Page</h1>\r
+<h2>NAME</h2>\r
+<p>{manname} -\r
+   {manpurpose}\r
+</p>\r
+\r
+[footer]\r
+<div id="footer">\r
+<p>\r
+Version {revision}<br/>\r
+Last updated {localdate} {localtime}\r
+</p>\r
+<p><span class="ahem">This document might look funny (or very plain) to you, since you're not using a browser which (correctly) supports CSS.</span></p>\r
+<div id="badges">\r
+<table border="0" cellpadding="8" summary="Badges">\r
+<tr>\r
+<td><a href="http://validator.w3.org/check/referer"><img\r
+# Source badge locally.\r
+#src="http://www.w3.org/Icons/valid-xhtml10"\r
+src="valid-xhtml10.png"\r
+alt="Valid XHTML 1.0" height="31" width="88" /></a></td>\r
+\r
+<td><a href="http://jigsaw.w3.org/css-validator/"> <img border="0"\r
+# Source badge locally.\r
+#src="http://jigsaw.w3.org/css-validator/images/vcss"\r
+src="vcss.png"\r
+alt="Valid CSS" width="88" height="31" /></a></td>\r
+</tr>\r
+</table>\r
+</div>\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
+\r
+# Section macros\r
+[sect-synopsis]\r
+<div id="synopsis">\r
+<h2>SYNOPSIS</h2>\r
+|\r
+</div>\r
+\r
+[sect1]\r
+<h2>{1?<a name="{1}"></a>}{title}</h2>\r
+|\r
+\r
+[sect2]\r
+<h3>{1?<a name="{1}"></a>}{title}</h3>\r
+|\r
+\r
+[sect3]\r
+<h4>{1?<a name="{1}"></a>}{title}</h4>\r
+|\r
+\r
+[sect4]\r
+<h5>{1?<a name="{1}"></a>}{title}</h5>\r
+|\r
+\r
+endif::doctype-manpage[]\r
diff --git a/doc/www/stylesheets/asciidoc.css b/doc/www/stylesheets/asciidoc.css
new file mode 100644 (file)
index 0000000..1475be7
--- /dev/null
@@ -0,0 +1,508 @@
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
+
+/* Default font. */
+body {
+  font-family: Georgia,serif;
+}
+
+/* Title font. */
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+  font-family: Arial,Helvetica,sans-serif;
+}
+
+body {
+  margin: 1em 5% 1em 5%;
+}
+
+a {
+  color: blue;
+  text-decoration: underline;
+}
+a:visited {
+  color: fuchsia;
+}
+
+em {
+  font-style: italic;
+  color: navy;
+}
+
+strong {
+  font-weight: bold;
+  color: #083194;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  color: #527bbd;
+  margin-top: 1.2em;
+  margin-bottom: 0.5em;
+  line-height: 1.3;
+}
+
+h1, h2, h3 {
+  border-bottom: 2px solid silver;
+}
+h2 {
+  padding-top: 0.5em;
+}
+h3 {
+  float: left;
+}
+h3 + * {
+  clear: left;
+}
+h5 {
+  font-size: 1.0em;
+}
+
+div.sectionbody {
+  margin-left: 0;
+}
+
+hr {
+  border: 1px solid silver;
+}
+
+p {
+  margin-top: 0.5em;
+  margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+  margin-top: 0;
+}
+ul > li     { color: #aaa; }
+ul > li > * { color: black; }
+
+pre {
+  padding: 0;
+  margin: 0;
+}
+
+#author {
+  color: #527bbd;
+  font-weight: bold;
+  font-size: 1.1em;
+}
+#email {
+}
+#revnumber, #revdate, #revremark {
+}
+
+#footer {
+  font-size: small;
+  border-top: 2px solid silver;
+  padding-top: 0.5em;
+  margin-top: 4.0em;
+}
+#footer-text {
+  float: left;
+  padding-bottom: 0.5em;
+}
+#footer-badges {
+  float: right;
+  padding-bottom: 0.5em;
+}
+
+#preamble {
+  margin-top: 1.5em;
+  margin-bottom: 1.5em;
+}
+div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+div.admonitionblock {
+  margin-top: 2.0em;
+  margin-bottom: 2.0em;
+  margin-right: 10%;
+  color: #606060;
+}
+
+div.content { /* Block element content. */
+  padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+  color: #527bbd;
+  font-weight: bold;
+  text-align: left;
+  margin-top: 1.0em;
+  margin-bottom: 0.5em;
+}
+div.title + * {
+  margin-top: 0;
+}
+
+td div.title:first-child {
+  margin-top: 0.0em;
+}
+div.content div.title:first-child {
+  margin-top: 0.0em;
+}
+div.content + div.title {
+  margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+  background: #ffffee;
+  border: 1px solid #dddddd;
+  border-left: 4px solid #f0f0f0;
+  padding: 0.5em;
+}
+
+div.listingblock > div.content {
+  border: 1px solid #dddddd;
+  border-left: 5px solid #f0f0f0;
+  background: #f8f8f8;
+  padding: 0.5em;
+}
+
+div.quoteblock, div.verseblock {
+  padding-left: 1.0em;
+  margin-left: 1.0em;
+  margin-right: 10%;
+  border-left: 5px solid #f0f0f0;
+  color: #777777;
+}
+
+div.quoteblock > div.attribution {
+  padding-top: 0.5em;
+  text-align: right;
+}
+
+div.verseblock > pre.content {
+  font-family: inherit;
+  font-size: inherit;
+}
+div.verseblock > div.attribution {
+  padding-top: 0.75em;
+  text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+  text-align: left;
+}
+
+div.admonitionblock .icon {
+  vertical-align: top;
+  font-size: 1.1em;
+  font-weight: bold;
+  text-decoration: underline;
+  color: #527bbd;
+  padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+  padding-left: 0.5em;
+  border-left: 3px solid #dddddd;
+}
+
+div.exampleblock > div.content {
+  border-left: 3px solid #dddddd;
+  padding-left: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+  margin-top: 0.8em;
+  margin-bottom: 0.8em;
+}
+dt {
+  margin-top: 0.5em;
+  margin-bottom: 0;
+  font-style: normal;
+  color: navy;
+}
+dd > *:first-child {
+  margin-top: 0.1em;
+}
+
+ul, ol {
+    list-style-position: outside;
+}
+ol.arabic {
+  list-style-type: decimal;
+}
+ol.loweralpha {
+  list-style-type: lower-alpha;
+}
+ol.upperalpha {
+  list-style-type: upper-alpha;
+}
+ol.lowerroman {
+  list-style-type: lower-roman;
+}
+ol.upperroman {
+  list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+  margin-top: 0.1em;
+  margin-bottom: 0.1em;
+}
+
+tfoot {
+  font-weight: bold;
+}
+td > div.verse {
+  white-space: pre;
+}
+
+div.hdlist {
+  margin-top: 0.8em;
+  margin-bottom: 0.8em;
+}
+div.hdlist tr {
+  padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+  font-weight: bold;
+}
+td.hdlist1 {
+  vertical-align: top;
+  font-style: normal;
+  padding-right: 0.8em;
+  color: navy;
+}
+td.hdlist2 {
+  vertical-align: top;
+}
+div.hdlist.compact tr {
+  margin: 0;
+  padding-bottom: 0;
+}
+
+.comment {
+  background: yellow;
+}
+
+.footnote, .footnoteref {
+  font-size: 0.8em;
+}
+
+span.footnote, span.footnoteref {
+  vertical-align: super;
+}
+
+#footnotes {
+  margin: 20px 0 20px 0;
+  padding: 7px 0 0 0;
+}
+
+#footnotes div.footnote {
+  margin: 0 0 5px 0;
+}
+
+#footnotes hr {
+  border: none;
+  border-top: 1px solid silver;
+  height: 1px;
+  text-align: left;
+  margin-left: 0;
+  width: 20%;
+  min-width: 100px;
+}
+
+div.colist td {
+  padding-right: 0.5em;
+  padding-bottom: 0.3em;
+  vertical-align: top;
+}
+div.colist td img {
+  margin-top: 0.3em;
+}
+
+@media print {
+  #footer-badges { display: none; }
+}
+
+#toc {
+  margin-bottom: 2.5em;
+}
+
+#toctitle {
+  color: #527bbd;
+  font-size: 1.1em;
+  font-weight: bold;
+  margin-top: 1.0em;
+  margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+div.toclevel2 {
+  margin-left: 2em;
+  font-size: 0.9em;
+}
+div.toclevel3 {
+  margin-left: 4em;
+  font-size: 0.9em;
+}
+div.toclevel4 {
+  margin-left: 6em;
+  font-size: 0.9em;
+}
+
+span.aqua { color: aqua; }
+span.black { color: black; }
+span.blue { color: blue; }
+span.fuchsia { color: fuchsia; }
+span.gray { color: gray; }
+span.green { color: green; }
+span.lime { color: lime; }
+span.maroon { color: maroon; }
+span.navy { color: navy; }
+span.olive { color: olive; }
+span.purple { color: purple; }
+span.red { color: red; }
+span.silver { color: silver; }
+span.teal { color: teal; }
+span.white { color: white; }
+span.yellow { color: yellow; }
+
+span.aqua-background { background: aqua; }
+span.black-background { background: black; }
+span.blue-background { background: blue; }
+span.fuchsia-background { background: fuchsia; }
+span.gray-background { background: gray; }
+span.green-background { background: green; }
+span.lime-background { background: lime; }
+span.maroon-background { background: maroon; }
+span.navy-background { background: navy; }
+span.olive-background { background: olive; }
+span.purple-background { background: purple; }
+span.red-background { background: red; }
+span.silver-background { background: silver; }
+span.teal-background { background: teal; }
+span.white-background { background: white; }
+span.yellow-background { background: yellow; }
+
+span.big { font-size: 2em; }
+span.small { font-size: 0.6em; }
+
+span.underline { text-decoration: underline; }
+span.overline { text-decoration: overline; }
+span.line-through { text-decoration: line-through; }
+
+
+/*
+ * xhtml11 specific
+ *
+ * */
+
+tt {
+  font-family: monospace;
+  font-size: inherit;
+  color: navy;
+}
+
+div.tableblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+div.tableblock > table {
+  border: 3px solid #527bbd;
+}
+thead, p.table.header {
+  font-weight: bold;
+  color: #527bbd;
+}
+p.table {
+  margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+  border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+  border-left-style: none;
+  border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+  border-top-style: none;
+  border-bottom-style: none;
+}
+
+
+/*
+ * html5 specific
+ *
+ * */
+
+.monospaced {
+  font-family: monospace;
+  font-size: inherit;
+  color: navy;
+}
+
+table.tableblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+  font-weight: bold;
+  color: #527bbd;
+}
+p.tableblock {
+  margin-top: 0;
+}
+table.tableblock {
+  border-width: 3px;
+  border-spacing: 0px;
+  border-style: solid;
+  border-color: #527bbd;
+  border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+  border-width: 1px;
+  padding: 4px;
+  border-style: solid;
+  border-color: #527bbd;
+}
+
+table.tableblock.frame-topbot {
+  border-left-style: hidden;
+  border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+  border-top-style: hidden;
+  border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+  border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+  text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+  text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+  text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+  vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+  vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+  vertical-align: bottom;
+}
index e5367734d3bc584baa574f0eaceb1897dd66f81e..6877a7611052062e0e211b3d21f7e27aec35b89f 100644 (file)
@@ -1,3 +1,3 @@
 # Makefile.am for etc/
 
-SUBDIRS = afpd cnid_dbd netatalk uams
+SUBDIRS = afpd cnid_dbd netatalk spotlight uams
index 5d0c03fa85cb4a199ed3beead2cdc679b820512b..4c401a6eb13144487f0a39403a5499d990920f7c 100644 (file)
@@ -3,8 +3,10 @@ Makefile.in
 afpd
 fce
 hash
-test_parse_mtab
+spot
+srp
 .deps
 .libs
-.gitignore
-afpd-afp_asp.o afpd-afp_config.o afpd-afp_dsi.o afpd-afp_options.o afpd-afprun.o afpd-afp_util.o afpd-afs.o afpd-appl.o afpd-auth.o afpd-catsearch.o afpd-desktop.o afpd-directory.o afpd-enumerate.o afpd-extattrs.o afpd-filedir.o afpd-file.o afpd-fork.o afpd-gettok.o afpd-hash.o afpd-main.o afpd-mangle.o afpd-messages.o afpd-nfsquota.o afpd-ofork.o afpd-quota.o afpd-status.o afpd-switch.o afpd-uam.o afpd-uid.o afpd-unix.o afpd-volume.o hash-hash.o
+*.o
+*.la
+*.lo
\ No newline at end of file
index 85b51dc86bdca8971ecb85ce94c66c1273c28e21..a0b210e170e05a4e2029881c8a5a269760d573cd 100644 (file)
@@ -1,9 +1,13 @@
 # Makefile.am for etc/afpd/
 
 pkgconfdir = @PKGCONFDIR@
+BUILT_SOURCES =
+EXTRA_DIST = afpstats-service.xml afpstats_service_glue.h
+CLEANFILES =
+DISTCLEANFILES =
 
 sbin_PROGRAMS = afpd
-noinst_PROGRAMS = hash fce
+noinst_PROGRAMS = hash fce spot
 
 afpd_SOURCES = \
        afp_avahi.c \
@@ -28,7 +32,6 @@ afpd_SOURCES = \
        file.c \
        filedir.c \
        fork.c \
-       gettok.c \
        hash.c \
        main.c \
        mangle.c \
@@ -36,6 +39,8 @@ afpd_SOURCES = \
        nfsquota.c \
        ofork.c \
        quota.c \
+       spotlight.c \
+       spotlight_marshalling.c \
        status.c \
        switch.c \
        uam.c \
@@ -43,14 +48,15 @@ afpd_SOURCES = \
        unix.c \
        volume.c
 
+
 afpd_LDADD =  \
        $(top_builddir)/libatalk/libatalk.la \
-       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@
+       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
 
 afpd_LDFLAGS = -export-dynamic
 
 afpd_CFLAGS = \
-       @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@\
+       @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
        -DAPPLCNAME \
        -DSERVERTEXT=\"$(SERVERTEXT)/\" \
        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
@@ -62,11 +68,36 @@ if HAVE_ACLS
 afpd_SOURCES += acls.c
 endif
 
+if HAVE_DBUS_GLIB
+BUILT_SOURCES += afpstats_service_glue.h
+DISTCLEANFILES += afpstats_service_glue.h
+
+afpstats_service_glue.h: afpstats-service.xml
+       $(LIBTOOL) --mode=execute \
+               dbus-binding-tool \
+                       --prefix=afpstats_obj \
+                       --mode=glib-server \
+                       --output=afpstats_service_glue.h \
+                       $(top_srcdir)/etc/afpd/afpstats-service.xml
+
+afpd_SOURCES += afpstats.c afpstats_obj.c
+afpd_CFLAGS  += $(DBUS_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GTHREAD_CFLAGS) -DDBUS_COMPILATION
+afpd_LDFLAGS += $(DBUS_LIBS) $(DBUS_GLIB_LIBS) $(DBUS_GTHREAD_LIBS) -ldbus-glib-1
+endif
+
+if WITH_DTRACE
+DTRACE_OBJ = afpd-afp_dsi.o afpd-fork.o afpd-appl.o afpd-catsearch.o afpd-directory.o afpd-enumerate.o afpd-file.o afpd-filedir.o
+afp_dtrace.o: $(top_srcdir)/include/atalk/afp_dtrace.d $(DTRACE_OBJ)
+       if test -f afp_dtrace.o ; then rm -f afp_dtrace.o ; fi
+       $(LIBTOOL) --mode=execute dtrace -G -s $(top_srcdir)/include/atalk/afp_dtrace.d -o afp_dtrace.o $(DTRACE_OBJ)
+afpd_LDADD += afp_dtrace.o @DTRACE_LIBS@
+CLEANFILES += afp_dtrace.o
+endif
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
         filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
         uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
-        dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h
+        dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h afpstats.h afpstats_obj.h
 
 hash_SOURCES = hash.c
 hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
@@ -74,3 +105,7 @@ hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
 fce_SOURCES = fce_api.c fce_util.c
 fce_CFLAGS = -DFCE_TEST_MAIN -I$(top_srcdir)/include
 fce_LDADD = $(top_builddir)/libatalk/libatalk.la
+
+spot_SOURCES = spotlight.c spotlight_marshalling.c
+spot_CFLAGS = -DSPOT_TEST_MAIN
+spot_LDADD = $(top_builddir)/libatalk/libatalk.la
index 87ff5decefdc75d9c09feab9af8c8d971fa54b14..7906aeec6769f036fd2eddad9b0c0de249693820 100644 (file)
@@ -18,6 +18,9 @@
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
 
 #include "acls.h"
 
@@ -35,7 +38,7 @@ struct ace_rights_map {
     uint32_t to;
 };
 
-#ifdef HAVE_SOLARIS_ACLS
+#if (defined HAVE_SOLARIS_ACLS || defined HAVE_FREEBSD_SUNACL)
 struct ace_rights_map nfsv4_to_darwin_rights[] = {
     {ACE_READ_DATA,         DARWIN_ACE_READ_DATA},
     {ACE_WRITE_DATA,        DARWIN_ACE_WRITE_DATA},
@@ -97,6 +100,6 @@ struct darwin_to_nfsv4_flags_map darwin_to_nfsv4_flags[] = {
     {DARWIN_ACE_FLAGS_INHERITED,         ACE_INHERITED_ACE},
     {0,0}
 };
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_SOLARIS_ACLS || HAVE_FREEBSD_SUNACL */
 
 #endif /* ACL_MAPPINGS */
index 305ef06fed6c65a7cf15e6dbe8e50f325f3f8736..f914c8e4754753e55033f5b77222edd6688808bf 100644 (file)
@@ -27,6 +27,9 @@
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
 #ifdef HAVE_POSIX_ACLS
 #include <sys/acl.h>
 #endif
@@ -76,7 +79,7 @@
  * Solaris funcs
  ********************************************************/
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 
 /*! 
  * Compile access rights for a user to one file-system object
  *
  * @param obj            (r) handle
  * @param path           (r) path to filesystem object
- * @param sb             (r) struct stat of path
- * @param result         (w) resulting Darwin allow ACE
+ * @param sb             (rw) struct stat of path
+ * @param ma             (rw) UARights struct
+ * @param rights_out     (w) mapped Darwin ACL rights
  *
  * @returns                  0 or -1 on error
  */
 static int solaris_acl_rights(const AFPObj *obj,
                               const char *path,
-                              const struct stat *sb,
-                              uint32_t *result)
+                              struct stat *sb,
+                              struct maccess *ma,
+                              uint32_t *rights_out)
 {
     EC_INIT;
     int      i, ace_count, checkgroup;
@@ -168,7 +173,28 @@ static int solaris_acl_rights(const AFPObj *obj,
             darwin_rights |= nfsv4_to_darwin_rights[i].to;
     }
 
-    *result |= darwin_rights;
+    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", darwin_rights);
+
+    if (rights_out)
+        *rights_out = darwin_rights;
+
+    if (ma && obj->options.flags & OPTION_ACL2MACCESS) {
+        if (darwin_rights & DARWIN_ACE_READ_DATA)
+            ma->ma_user |= AR_UREAD;
+        if (darwin_rights & DARWIN_ACE_WRITE_DATA)
+            ma->ma_user |= AR_UWRITE;
+        if (darwin_rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
+            ma->ma_user |= AR_USEARCH;
+    }
+
+    if (sb && obj->options.flags & OPTION_ACL2MODE) {
+        if (darwin_rights & DARWIN_ACE_READ_DATA)
+            sb->st_mode |= S_IRUSR;
+        if (darwin_rights & DARWIN_ACE_WRITE_DATA)
+            sb->st_mode |= S_IWUSR;
+        if (darwin_rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
+            sb->st_mode |= S_IXUSR;
+    }
 
 EC_CLEANUP:
     if (aces) free(aces);
@@ -339,7 +365,7 @@ EC_CLEANUP:
         free(name);
     EC_EXIT;
 }
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 /********************************************************
  * POSIX 1e funcs
@@ -607,21 +633,23 @@ static int posix_acls_to_uaperms(const AFPObj *obj, const char *path, struct sta
                 break;
         }
     }
-    /* apply the mask and adjust user and group permissions */
-    ma->ma_user |= (acl_rights & mask);
-    ma->ma_group = (group_rights & mask);
 
-    /* update st_mode to properly reflect group permissions */
-    sb->st_mode &= ~S_IRWXG;
-
-    if (ma->ma_group & AR_USEARCH)
-        sb->st_mode |= S_IXGRP;
-
-    if (ma->ma_group & AR_UWRITE)
-        sb->st_mode |= S_IWGRP;
+    if (obj->options.flags & OPTION_ACL2MACCESS) {
+        /* apply the mask and adjust user and group permissions */
+        ma->ma_user |= (acl_rights & mask);
+        ma->ma_group = (group_rights & mask);
+    }
 
-    if (ma->ma_group & AR_UREAD)
-        sb->st_mode |= S_IRGRP;
+    if (obj->options.flags & OPTION_ACL2MODE) {
+        /* update st_mode to properly reflect group permissions */
+        sb->st_mode &= ~S_IRWXG;
+        if (ma->ma_group & AR_USEARCH)
+            sb->st_mode |= S_IXGRP;
+        if (ma->ma_group & AR_UWRITE)
+            sb->st_mode |= S_IWGRP;
+        if (ma->ma_group & AR_UREAD)
+            sb->st_mode |= S_IRGRP;
+    }
 
 EC_CLEANUP:
     if (acl) acl_free(acl);
@@ -629,6 +657,7 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
+#if 0
 /*!
  * Add entries of one acl to another acl
  *
@@ -651,6 +680,7 @@ static int acl_add_acl(acl_t *aclp, const acl_t acl)
 EC_CLEANUP:
     EC_EXIT;
 }
+#endif
 
 /*!
  * Map Darwin ACE rights to POSIX 1e perm
@@ -948,7 +978,7 @@ static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)
 
     switch (type & MAP_MASK) {
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     case SOLARIS_2_DARWIN:
         mapped_aces = map_aces_solaris_to_darwin( acl, buf, ace_count);
         break;
@@ -956,7 +986,7 @@ static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)
     case DARWIN_2_SOLARIS:
         mapped_aces = map_aces_darwin_to_solaris( buf, acl, ace_count);
         break;
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
     case POSIX_DEFAULT_2_DARWIN:
@@ -992,7 +1022,7 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     int mapped_aces = 0;
     int dirflag;
     char *darwin_ace_count = rbuf;
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     int ace_count = 0;
     ace_t *aces = NULL;
 #endif
@@ -1006,10 +1036,10 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     *rbuf = 0;
     rbuf += 4;
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     EC_NEG1(ace_count = get_nfsv4_acl(name, &aces));
     EC_NEG1(mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count));
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
     acl_t defacl = NULL , accacl = NULL;
@@ -1047,7 +1077,7 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     EC_STATUS(0);
 
 EC_CLEANUP:
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     if (aces) free(aces);
 #endif
 #ifdef HAVE_POSIX_ACLS
@@ -1065,7 +1095,7 @@ static int remove_acl(const struct vol *vol,const char *path, int dir)
 {
     int ret = AFP_OK;
 
-#if (defined HAVE_SOLARIS_ACLS || defined HAVE_POSIX_ACLS)
+#if (defined HAVE_NFSV4_ACLS || defined HAVE_POSIX_ACLS)
     /* Ressource etc. first */
     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
         return ret;
@@ -1083,7 +1113,7 @@ static int remove_acl(const struct vol *vol,const char *path, int dir)
   We will store inherited ACEs first, which is Darwins canonical order.
   - returns AFPerror code
 */
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 static int set_acl(const struct vol *vol,
                    char *name,
                    int inherit,
@@ -1155,28 +1185,31 @@ static int set_acl(const struct vol *vol,
     }
     LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
 
-    /* Ressourcefork first.
-       Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
-       might be strange for ACE_DELETE_CHILD and for inheritance flags. */
+    /* Ressourcefork first */
     if ((ret = (vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
-        LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
-        if (errno == (EACCES | EPERM))
-            EC_STATUS(AFPERR_ACCESS);
-        else if (errno == ENOENT)
-            EC_STATUS(AFPERR_NOITEM);
-        else
-            EC_STATUS(AFPERR_MISC);
-        goto EC_CLEANUP;
+        LOG(log_debug, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+        switch (errno) {
+        case ENOENT:
+            break;
+        case EACCES:
+        case EPERM:
+            EC_EXIT_STATUS(AFPERR_ACCESS);
+        default:
+            EC_EXIT_STATUS(AFPERR_MISC);
+        }
     }
+
     if ((ret = (acl(name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
-        if (errno == (EACCES | EPERM))
-            EC_STATUS(AFPERR_ACCESS);
-        else if (errno == ENOENT)
-            EC_STATUS(AFPERR_NOITEM);
-        else
-            EC_STATUS(AFPERR_MISC);
-        goto EC_CLEANUP;
+        switch (errno) {
+        case EACCES:
+        case EPERM:
+            EC_EXIT_STATUS(AFPERR_ACCESS);
+        case ENOENT:
+            EC_EXIT_STATUS(AFPERR_NOITEM);
+        default:
+            EC_EXIT_STATUS(AFPERR_MISC);
+        }
     }
 
     EC_STATUS(AFP_OK);
@@ -1188,7 +1221,7 @@ EC_CLEANUP:
     LOG(log_debug9, logtype_afpd, "set_acl: END");
     EC_EXIT;
 }
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
 #ifndef HAVE_ACL_FROM_MODE
@@ -1261,7 +1294,6 @@ static int set_acl(const struct vol *vol,
     acl_entry_t entry;
     acl_tag_t tag;
     int entry_id = ACL_FIRST_ENTRY;
-    int has_def_acl = 0;
     /* flags to indicate if the object has a minimal default acl and/or an extended
      * default acl.
      */
@@ -1355,7 +1387,6 @@ static int check_acl_access(const AFPObj *obj,
     int            ret;
     uint32_t       allowed_rights = 0;
     char           *username = NULL;
-    uuidtype_t     uuidtype;
     struct stat    st;
     bstring        parent = NULL;
     int            is_dir;
@@ -1363,6 +1394,8 @@ static int check_acl_access(const AFPObj *obj,
     LOG(log_maxdebug, logtype_afpd, "check_acl_access(dir: \"%s\", path: \"%s\", curdir: \"%s\", 0x%08x)",
         cfrombstr(dir->d_fullpath), path, getcwdpath(), requested_rights);
 
+    AFP_ASSERT(vol);
+
     /* This check is not used anymore, as OS X Server seems to be ignoring too */
 #if 0
     /* Get uid or gid from UUID */
@@ -1380,7 +1413,7 @@ static int check_acl_access(const AFPObj *obj,
     }
 #endif
 
-    EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
+    EC_ZERO_LOG_ERR(ostat(path, &st, vol_syml_opt(vol)), AFPERR_PARAM);
 
     is_dir = !strcmp(path, ".");
 
@@ -1389,8 +1422,8 @@ static int check_acl_access(const AFPObj *obj,
         allowed_rights = curdir->d_rights_cache;
         LOG(log_debug, logtype_afpd, "check_access: allowed rights from dircache: 0x%08x", allowed_rights);
     } else {
-#ifdef HAVE_SOLARIS_ACLS
-        EC_ZERO_LOG(solaris_acl_rights(obj, path, &st, &allowed_rights));
+#ifdef HAVE_NFSV4_ACLS
+        EC_ZERO_LOG(solaris_acl_rights(obj, path, &st, NULL, &allowed_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
         EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &allowed_rights));
@@ -1422,8 +1455,8 @@ static int check_acl_access(const AFPObj *obj,
             LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
             EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
 
-#ifdef HAVE_SOLARIS_ACLS
-            EC_ZERO_LOG(solaris_acl_rights(obj, cfrombstr(parent), &st, &parent_rights));
+#ifdef HAVE_NFSV4_ACLS
+            EC_ZERO_LOG(solaris_acl_rights(obj, cfrombstr(parent), &st, NULL, &parent_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
             EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &parent_rights));
@@ -1577,10 +1610,10 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
         if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
             LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
-            localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
+            localuuid_from_id((unsigned char *)rbuf, UUID_USER, s_path->st.st_uid);
         } else {
             LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
-            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, (unsigned char *)rbuf)) != 0)
                 return AFPERR_MISC;
         }
         rbuf += UUID_BINSIZE;
@@ -1592,10 +1625,10 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
         if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
             LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
-            localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
+            localuuid_from_id((unsigned char *)rbuf, UUID_GROUP, s_path->st.st_gid);
         } else {
             LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
-            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, (unsigned char *)rbuf)) != 0)
                 return AFPERR_MISC;
         }
         rbuf += UUID_BINSIZE;
@@ -1727,26 +1760,16 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 int acltoownermode(const AFPObj *obj, const struct vol *vol, char *path, struct stat *st, struct maccess *ma)
 {
     EC_INIT;
-    uint32_t rights = 0;
 
-    if ( ! (obj->options.flags & OPTION_ACL2MACCESS)
+    if ( ! (obj->options.flags & (OPTION_ACL2MACCESS | OPTION_ACL2MODE))
          || ! (vol->v_flags & AFPVOL_ACLS))
          return 0;
 
     LOG(log_maxdebug, logtype_afpd, "acltoownermode(\"%s/%s\", 0x%02x)",
         getcwdpath(), path, ma->ma_user);
 
-#ifdef HAVE_SOLARIS_ACLS
-    EC_ZERO_LOG(solaris_acl_rights(obj, path, st, &rights));
-
-    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
-
-    if (rights & DARWIN_ACE_READ_DATA)
-        ma->ma_user |= AR_UREAD;
-    if (rights & DARWIN_ACE_WRITE_DATA)
-        ma->ma_user |= AR_UWRITE;
-    if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
-        ma->ma_user |= AR_USEARCH;
+#ifdef HAVE_NFSV4_ACLS
+    EC_ZERO_LOG(solaris_acl_rights(obj, path, st, ma, NULL));
 #endif
 
 #ifdef HAVE_POSIX_ACLS
index c48f03a9b4ab6da3b3e7e4c4950f8c7868617ac9..4d423af5df73ff715d1f44871e5b3c1a581ada49 100644 (file)
@@ -18,6 +18,9 @@
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
 
 #include <atalk/uuid.h>                /* for atalk_uuid_t */
 
  * the wire! We will ignore and spoil em.
  */
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 /* Some stuff for the handling of NFSv4 ACLs */
 #define ACE_TRIVIAL (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 /* FPGet|Set Bitmap */
 enum {
index a61fe1022f551ccc84b9fef03c860cfe16c51a30..c7246f1f2d4c4206292fb69ac3c063d5dd39865b 100644 (file)
@@ -17,6 +17,9 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
 
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include "volume.h"
 #include "afp_zeroconf.h"
 
-
 /*!
- * Free and cleanup all linked DSI objects from config
+ * Free and cleanup config and DSI
  *
- * Preserve object pointed to by "dsi".
- * "dsi" can be NULL in which case all DSI objects _and_ the options object are freed 
+ * "dsi" can be NULL in which case all DSI objects and the config object is freed,
+ * otherwise its an afpd session child and only any unneeded DSI objects are freed
  */
 void configfree(AFPObj *obj, DSI *dsi)
 {
     DSI *p, *q;
 
-    /* the master loaded the volumes for zeroconf, get rid of that */
+    if (!dsi) {
+        /* Master afpd reloading config */
+        auth_unload();
+        if (! (obj->options.flags & OPTION_NOZEROCONF)) {
+            zeroconf_deregister();
+        }
+    }
+
     unload_volumes(obj);
 
+    /* Master and child releasing unneeded DSI handles */
     for (p = obj->dsi; p; p = q) {
         q = p->next;
         if (p == dsi)
             continue;
-        close(p->socket);
+        dsi_free(p);
         free(p);
     }
+    obj->dsi = NULL;
 
+    /* afpd session child passes dsi handle to obj handle */
     if (dsi) {
         dsi->next = NULL;
         obj->dsi = dsi;
-    } else {
-        afp_options_free(&obj->options);
     }
 }
 
@@ -75,30 +85,113 @@ void configfree(AFPObj *obj, DSI *dsi)
 int configinit(AFPObj *obj)
 {
     EC_INIT;
-    DSI *dsi, **next = &obj->dsi;
+    DSI *dsi = NULL;
+    DSI **next = &obj->dsi;
     char *p = NULL, *q = NULL, *savep;
     const char *r;
+    struct ifaddrs *ifaddr, *ifa;
+    int family, s;
+    static char interfaddr[NI_MAXHOST];
 
-    auth_load(obj->options.uampath, obj->options.uamlist);
+    auth_load(obj, obj->options.uampath, obj->options.uamlist);
     set_signature(&obj->options);
+#ifdef HAVE_LDAP
+    acl_ldap_freeconfig();
+#endif /* HAVE_LDAP */
 
-    LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, port: %s",
+    LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, interfaces: %s, port: %s",
         obj->options.hostname,
-        obj->options.listen ? obj->options.listen : "(default: hostname)",
+        obj->options.listen ? obj->options.listen : "-",
+        obj->options.interfaces ? obj->options.interfaces : "-",
         obj->options.port);
 
-    /* obj->options->listen is of the from "IP[:port][,IP:[PORT], ...]" */
-    /* obj->options->port is the default port to listen (548) */
-
+    /*
+     * Setup addresses we listen on from hostname and/or "afp listen" option
+     */
     if (obj->options.listen) {
         EC_NULL( q = p = strdup(obj->options.listen) );
         EC_NULL( p = strtok_r(p, ", ", &savep) );
+        while (p) {
+            if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL)
+                break;
+
+            status_init(obj, dsi);
+            *next = dsi;
+            next = &dsi->next;
+            dsi->AFPobj = obj;
+
+            LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
+                getip_string((struct sockaddr *)&dsi->server),
+                getip_port((struct sockaddr *)&dsi->server));
+
+            p = strtok_r(NULL, ", ", &savep);
+        }
+        if (q) {
+            free(q);
+            q = NULL;
+        }
     }
 
-    while (1) {
-        if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL)
-            break;
+   /*
+    * Setup addresses we listen on from "afp interfaces".
+    * We use getifaddrs() instead of if_nameindex() because the latter appears still
+    * to be unable to return ipv4 addresses
+    */
+    if (obj->options.interfaces) {
+#ifndef HAVE_GETIFADDRS
+        LOG(log_error, logtype_afpd, "option \"afp interfaces\" not supported");
+#else
+        if (getifaddrs(&ifaddr) == -1) {
+            LOG(log_error, logtype_afpd, "getinterfaddr: getifaddrs() failed: %s", strerror(errno));
+            EC_FAIL;
+        }
+
+        EC_NULL( q = p = strdup(obj->options.interfaces) );
+        EC_NULL( p = strtok_r(p, ", ", &savep) );
+        while (p) {
+            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+                if (ifa->ifa_addr == NULL)
+                    continue;
+                if (STRCMP(ifa->ifa_name, !=, p))
+                    continue;
+
+                family = ifa->ifa_addr->sa_family;
+                if (family == AF_INET || family == AF_INET6) {
+                    if (getnameinfo(ifa->ifa_addr,
+                                    (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
+                                    interfaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
+                        LOG(log_error, logtype_afpd, "getinterfaddr: getnameinfo() failed %s", gai_strerror(errno));
+                        continue;
+                    }
+
+                    if ((dsi = dsi_init(obj, obj->options.hostname, interfaddr, obj->options.port)) == NULL)
+                        continue;
+
+                    status_init(obj, dsi);
+                    *next = dsi;
+                    next = &dsi->next;
+                    dsi->AFPobj = obj;
+
+                    LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on interface %s with address %s:%d",
+                        p,
+                        getip_string((struct sockaddr *)&dsi->server),
+                        getip_port((struct sockaddr *)&dsi->server));
+                } /* if (family == AF_INET || family == AF_INET6) */
+            } /* for (ifa != NULL) */
+            p = strtok_r(NULL, ", ", &savep);
+        }
+        freeifaddrs(ifaddr);
+#endif
+    }
 
+    /*
+     * Check whether we got a valid DSI from options.listen or options.interfaces,
+     * if not add a DSI that accepts all connections and goes though the list of
+     * network interaces for determining an IP we can advertise in DSIStatus
+     */
+    if (dsi == NULL) {
+        if ((dsi = dsi_init(obj, obj->options.hostname, NULL, obj->options.port)) == NULL)
+            EC_FAIL_LOG("no suitable network address found, use \"afp listen\" or \"afp interfaces\"", 0);
         status_init(obj, dsi);
         *next = dsi;
         next = &dsi->next;
@@ -107,12 +200,6 @@ int configinit(AFPObj *obj)
         LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
             getip_string((struct sockaddr *)&dsi->server),
             getip_port((struct sockaddr *)&dsi->server));
-
-        if (p)
-            /* p is NULL if ! obj->options.listen */
-            p = strtok_r(NULL, ", ", &savep);
-        if (!p)
-            break;
     }
 
 #ifdef HAVE_LDAP
@@ -122,19 +209,19 @@ int configinit(AFPObj *obj)
 
     /* Now register with zeroconf, we also need the volumes for that */
     if (! (obj->options.flags & OPTION_NOZEROCONF)) {
-        load_volumes(obj, NULL);
+        load_volumes(obj, lv_all);
         zeroconf_register(obj);
     }
 
-    if ((r = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce listener", NULL))) {
+    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce listener", NULL))) {
                LOG(log_note, logtype_afpd, "Adding FCE listener: %s", r);
                fce_add_udp_socket(r);
     }
-    if ((r = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce coalesce", NULL))) {
+    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce coalesce", NULL))) {
                LOG(log_note, logtype_afpd, "Fce coalesce: %s", r);
                fce_set_coalesce(r);
     }
-    if ((r = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce events", NULL))) {
+    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce events", NULL))) {
                LOG(log_note, logtype_afpd, "Fce events: %s", r);
                fce_set_events(r);
     }
index 3e5cd47c3cbf4de4438bc6e3e0e2585a9be72417..697aa435c9a06f4274516f29fe472e42a184bf79 100644 (file)
@@ -39,6 +39,7 @@
 #include <atalk/fce_api.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/spotlight.h>
 
 #include "switch.h"
 #include "auth.h"
@@ -140,25 +141,6 @@ static void afp_dsi_die(int sig)
     }
 }
 
-/* SIGQUIT handler */
-static void ipc_reconnect_handler(int sig _U_)
-{
-    DSI *dsi = (DSI *)AFPobj->dsi;
-
-    if (reconnect_ipc(AFPobj) != 0) {
-        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC reconnect");
-        afp_dsi_close(AFPobj);
-        exit(EXITERR_SYS);        
-    }
-
-    if (ipc_child_write(AFPobj->ipc_fd, IPC_GETSESSION, AFPobj->sinfo.clientid_len, AFPobj->sinfo.clientid) != 0) {
-        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC ID resend");
-        afp_dsi_close(AFPobj);
-        exit(EXITERR_SYS);        
-    }
-    LOG(log_note, logtype_afpd, "ipc_reconnect_handler: IPC reconnect done");
-}
-
 /* SIGURG handler (primary reconnect) */
 static void afp_dsi_transfer_session(int sig _U_)
 {
@@ -340,9 +322,10 @@ static void alarm_handler(int sig _U_)
         return;
     }
 
-    if ((err = pollvoltime(AFPobj)) == 0)
+    if ((err = pollvoltime(AFPobj)) == 0) {
         LOG(log_debug, logtype_afpd, "afp_alarm: sending DSI tickle");
         err = dsi_tickle(AFPobj->dsi);
+    }
     if (err <= 0) {
         if (geteuid() == 0) {
             LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
@@ -409,7 +392,7 @@ void afp_over_dsi_sighandlers(AFPObj *obj)
     }
 
     /* install SIGQUIT */
-    action.sa_handler = ipc_reconnect_handler;
+    action.sa_handler = afp_dsi_die;
     if ( sigaction(SIGQUIT, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
@@ -492,6 +475,12 @@ void afp_over_dsi(AFPObj *obj)
     int flag = 1;
     setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
 
+    /* Initialize Spotlight */
+    if ((obj->options.flags & OPTION_SPOTLIGHT) && (obj->options.slmod_path))
+        sl_mod_load(obj);
+
+    ipc_child_state(obj, DSI_RUNNING);
+
     /* get stuck here until the end */
     while (1) {
         if (sigsetjmp(recon_jmp, 1) != 0)
@@ -516,15 +505,6 @@ void afp_over_dsi(AFPObj *obj)
                 exit(0);
             }
 
-#if 0
-            /*  got ECONNRESET in read from client => exit*/
-            if (dsi->flags & DSI_GOT_ECONNRESET) {
-                LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
-                afp_dsi_close(obj);
-                exit(0);
-            }
-#endif
-
             if (dsi->flags & DSI_RECONINPROG) {
                 LOG(log_note, logtype_afpd, "afp_over_dsi: failed reconnect");
                 afp_dsi_close(obj);
@@ -535,8 +515,11 @@ void afp_over_dsi(AFPObj *obj)
             if (dsi_disconnect(dsi) != 0)
                 afp_dsi_die(EXITERR_CLNT);
 
+            ipc_child_state(obj, DSI_DISCONNECTED);
+
             while (dsi->flags & DSI_DISCONNECTED)
                 pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+            ipc_child_state(obj, DSI_RUNNING);
             continue; /* continue receiving until disconnect timer expires
                        * or a primary reconnect succeeds  */
         }
@@ -545,11 +528,12 @@ void afp_over_dsi(AFPObj *obj)
             LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
             dsi->flags &= ~DSI_SLEEPING;
             dsi->tickle = 0;
+            ipc_child_state(obj, DSI_RUNNING);
         }
 
         if (reload_request) {
             reload_request = 0;
-            load_volumes(AFPobj, closevol);
+            load_volumes(AFPobj, lv_none);
         }
 
         /* The first SIGINT enables debugging, the next restores the config */
@@ -625,10 +609,12 @@ void afp_over_dsi(AFPObj *obj)
 
                     LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
+                    AFP_AFPFUNC_START(function, (char *)AfpNum2name(function));
                     err = (*afp_switch[function])(obj,
-                                                  dsi->commands, dsi->cmdlen,
+                                                  (char *)dsi->commands, dsi->cmdlen,
                                                   (char *)&dsi->data, &dsi->datalen);
 
+                    AFP_AFPFUNC_DONE(function, (char *)AfpNum2name(function));
                     LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                         AfpNum2name(function), AfpErr2name(err));
 
@@ -666,10 +652,14 @@ void afp_over_dsi(AFPObj *obj)
 
                 LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
+                AFP_AFPFUNC_START(function, (char *)AfpNum2name(function));
+
                 err = (*afp_switch[function])(obj,
-                                              dsi->commands, dsi->cmdlen,
+                                              (char *)dsi->commands, dsi->cmdlen,
                                               (char *)&dsi->data, &dsi->datalen);
 
+                AFP_AFPFUNC_DONE(function, (char *)AfpNum2name(function));
+
                 LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                     AfpNum2name(function), AfpErr2name(err));
 
index e14791fee2dd93c4db6b8deef1c65f92ba168b77..d3e6cd8403b407d57ac397ad35020613146834bf 100644 (file)
@@ -36,29 +36,68 @@ static pthread_t       poller;
 /*
  * Its easier to use asprintf to set the TXT record values
  */
-#define TXTRecordPrintf(rec, key, args...) {            \
-        char *str;                                      \
-        asprintf(&str, args);                           \
-        TXTRecordSetValue(rec, key, strlen(str), str);  \
-        free(str);                                      \
+
+int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... ) 
+{
+    int ret = 0;
+    char *str;
+    va_list ap;
+    va_start( ap, fmt );
+
+    if( 0 > vasprintf(&str, fmt, ap ) ) {
+        va_end(ap);
+        return -1;    
+    }
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
     }
-#define TXTRecordKeyPrintf(rec, k, var, args...) {      \
-        char *key, *str;                                \
-        asprintf(&key, k, var);                         \
-        asprintf(&str, args);                           \
-        TXTRecordSetValue(rec, key, strlen(str), str);  \
-        free(str); free(key);                           \
+
+    free(str);
+    return ret;
+}
+
+int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...) 
+{
+    int ret = 0;
+    char *key = NULL, *str = NULL;
+    va_list ap;
+
+    if( 0 > asprintf(&key, key_fmt, key_var))
+        return -1;
+
+    va_start( ap, fmt );
+    if( 0 > vasprintf(&str, fmt, ap )) {
+        va_end(ap);
+        ret = -1;
+        goto exit;
+    }
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
+        goto exit;
     }
 
+exit:
+    if (str)
+        free(str);
+    if (key)
+        free(key);
+    return ret;
+}
+
+static struct pollfd *fds;
 
 /*
  * This is the thread that polls the filehandles
  */
-void *polling_thread(void *arg) {
+static void *polling_thread(void *arg) {
     // First we loop through getting the filehandles and adding them to our poll, we
     // need to allocate our pollfd's
     DNSServiceErrorType error;
-    struct pollfd           *fds = calloc(svc_ref_count, sizeof(struct pollfd));
+    fds = calloc(svc_ref_count, sizeof(struct pollfd));
     assert(fds);
 
     for(int i=0; i < svc_ref_count; i++) {
@@ -78,28 +117,32 @@ void *polling_thread(void *arg) {
     return(NULL);
 }
 
-
 /*
  * This is the callback for the service register function ... actually there isn't a lot
  * we can do if we get problems, so we don't really need to do anything other than report
  * the issue.
  */
-void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
-                   const char *name, const char *regtype, const char *domain, void *context) {
-
-    if(errorCode != kDNSServiceErr_NoError) {
+static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
+                          const char *name, const char *regtype, const char *domain, void *context)
+{
+    if (errorCode != kDNSServiceErr_NoError) {
         LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
             name, regtype, domain, errorCode);
     }
 }
 
-
 /*
  * This function unregisters anything we have already
  * registered and frees associated memory
  */
 static void unregister_stuff() {
-    pthread_kill(poller, SIGKILL);
+    pthread_cancel(poller);    
+
+    for (int i = 0; i < svc_ref_count; i++)
+        close(fds[i].fd);
+    free(fds);
+    fds = NULL;
+
     if(svc_refs) {
         for(int i=0; i < svc_ref_count; i++) {
             DNSServiceRefDeallocate(svc_refs[i]);
@@ -129,7 +172,10 @@ static void register_stuff(const AFPObj *obj) {
 
     /* Register our service, prepare the TXT record */
     TXTRecordCreate(&txt_adisk, 0, NULL);
-    TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
+    if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
+        LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
+        goto fail;
+    }
 
     /* Build AFP volumes list */
     int i = 0;
@@ -145,12 +191,18 @@ static void register_stuff(const AFPObj *obj) {
             if (volume->v_uuid) {
                 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
                     volume->v_localname, volume->v_uuid);
-                TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
-                                   tmpname, volume->v_uuid);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
+                                   tmpname, volume->v_uuid) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
             } else {
                 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
                     volume->v_localname);
-                TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
             }
         }
     }
@@ -165,7 +217,7 @@ static void register_stuff(const AFPObj *obj) {
 
     // Allocate the memory to store our service refs
     svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
-    assert(svc_ref);
+    assert(svc_refs);
     svc_ref_count = 0;
 
     /* AFP server */
@@ -232,7 +284,11 @@ static void register_stuff(const AFPObj *obj) {
             LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
                 dsi->bonjourname, obj->options.mimicmodel);
             TXTRecordCreate(&txt_devinfo, 0, NULL);
-            TXTRecordPrintf(&txt_devinfo, "model=%s", obj->options.mimicmodel);
+            if( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
+              LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
+              goto fail;
+            }
+
             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
                                        0,               // no flags
                                        0,               // all network interfaces
@@ -240,7 +296,14 @@ static void register_stuff(const AFPObj *obj) {
                                        DEV_INFO_SERVICE_TYPE,
                                        "",            // default domains
                                        NULL,            // default host name
-                                       0,
+                                       /*
+                                        * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
+                                        *   "A value of 0 for a port is passed to register placeholder services.
+                                        *    Place holder services are not found  when browsing, but other
+                                        *    clients cannot register with the same name as the placeholder service."
+                                        * We therefor use port 9 which is used by the adisk service type.
+                                        */
+                                       htons(9),
                                        TXTRecordGetLength(&txt_devinfo),
                                        TXTRecordGetBytesPtr(&txt_devinfo),
                                        RegisterReply,           // callback
index 223f117881e20ca8b485290b4577ec6a999708d3..95faf80807aa078f2b9fc005c8c30a729ec14104 100644 (file)
 
 #define LENGTH 512
 
-/* get rid of any allocated afp_option buffers. */
-void afp_options_free(struct afp_options *opt)
-{
-       if (opt->hostname)
-        free(opt->hostname);
-       if (opt->adminauthuser)
-        free(opt->adminauthuser);
-       if (opt->configfile)
-        free(opt->configfile);
-    if (opt->fqdn)
-        free(opt->fqdn);
-    if (opt->guest)
-        free(opt->guest);
-    if (opt->listen)
-        free(opt->listen);
-    if (opt->k5realm)
-        free(opt->k5realm);
-    if (opt->k5keytab)
-        free(opt->k5keytab);
-    if (opt->k5service)
-        free(opt->k5service);
-    if (opt->logconfig)
-        free(opt->logconfig);
-    if (opt->logfile)
-        free(opt->logfile);
-    if (opt->loginmesg)
-        free(opt->loginmesg);
-    if (opt->maccodepage)
-        free(opt->maccodepage);
-       if (opt->mimicmodel)
-        free(opt->mimicmodel);
-    if (opt->ntdomain)
-        free(opt->ntdomain);
-    if (opt->ntseparator)
-        free(opt->ntseparator);
-    if (opt->passwdfile)
-        free(opt->passwdfile);
-    if (opt->port)
-        free(opt->port);
-    if (opt->signatureopt)
-        free(opt->signatureopt);
-    if (opt->uamlist)
-        free(opt->uamlist);
-    if (opt->uampath)
-        free(opt->uampath);
-    if (opt->unixcodepage)
-        free(opt->unixcodepage);
-}
-
 /*
  * Show version information about afpd.
  * Used by "afp -v".
@@ -142,6 +93,9 @@ static void show_version( void )
 #endif
 #ifdef CNID_BACKEND_TDB
        printf( "tdb " );
+#endif
+#ifdef CNID_BACKEND_MYSQL
+       printf( "mysql " );
 #endif
        puts( "" );
 }
@@ -214,6 +168,27 @@ static void show_version_extended(void )
 #else
        puts( "No" );
 #endif
+
+       printf( "         D-Bus support:\t" );
+#ifdef HAVE_DBUS_GLIB
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
+       printf( "     Spotlight support:\t" );
+#ifdef HAVE_TRACKER
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
+       printf( "         DTrace probes:\t" );
+#ifdef WITH_DTRACE
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
 }
 
 /*
@@ -222,6 +197,7 @@ static void show_version_extended(void )
 static void show_paths( void )
 {
        printf( "              afp.conf:\t%s\n", _PATH_CONFDIR "afp.conf");
+       printf( "           extmap.conf:\t%s\n", _PATH_CONFDIR "extmap.conf");
        printf( "       state directory:\t%s\n", _PATH_STATEDIR);
        printf( "    afp_signature.conf:\t%s\n", _PATH_STATEDIR "afp_signature.conf");
        printf( "      afp_voluuid.conf:\t%s\n", _PATH_STATEDIR "afp_voluuid.conf");
index a8282c22b3fb38e57a4ddb94af1e529004a7022f..b7bc00f8f51709f1673410cecf773605ca3a5876 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: afp_util.c,v 1.10 2010-01-23 14:44:42 franklahm Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
diff --git a/etc/afpd/afpstats-service.xml b/etc/afpd/afpstats-service.xml
new file mode 100644 (file)
index 0000000..099c778
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/org/netatalk/AFPStats">
+  <interface name="org.netatalk.AFPStats">
+    <method name="GetUsers">
+       <arg name="ret" type="as" direction="out"/>
+    </method>
+  </interface>
+</node>
diff --git a/etc/afpd/afpstats.c b/etc/afpd/afpstats.c
new file mode 100644 (file)
index 0000000..80d3e6f
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <atalk/logger.h>
+#include <atalk/compat.h>
+#include <atalk/errchk.h>
+#include <atalk/server_child.h>
+
+#include "afpstats_obj.h"
+#include "afpstats_service_glue.h"
+
+/*
+ * Beware: this struct is accessed and modified from the main thread
+ * and from this thread, thus be careful to lock and unlock the mutex.
+ */
+static server_child_t *childs;
+
+static gpointer afpstats_thread(gpointer _data)
+{
+    DBusGConnection *bus;
+    DBusGProxy *bus_proxy;
+    GError *error = NULL;
+    GMainContext *ctxt;
+    GMainLoop *thread_loop;
+    guint request_name_result;
+    sigset_t sigs;
+
+    /* Block all signals in this thread */
+    sigfillset(&sigs);
+    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+
+    ctxt = g_main_context_new();
+    thread_loop = g_main_loop_new(ctxt, FALSE);
+
+    dbus_g_object_type_install_info(AFPSTATS_TYPE_OBJECT, &dbus_glib_afpstats_obj_object_info);
+   
+    if (!(bus = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, ctxt, &error))) {
+        LOG(log_error, logtype_afpd,"Couldn't connect to system bus: %s", error->message);
+        return NULL;
+    }
+
+    if (!(bus_proxy = dbus_g_proxy_new_for_name(bus, "org.freedesktop.DBus",
+                                                "/org/freedesktop/DBus",
+                                                "org.freedesktop.DBus"))) {
+        LOG(log_error, logtype_afpd,"Couldn't create bus proxy");
+        return NULL;
+    }
+
+    if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error,
+                           G_TYPE_STRING, "org.netatalk.AFPStats",
+                           G_TYPE_UINT, 0,
+                           G_TYPE_INVALID,
+                           G_TYPE_UINT, &request_name_result,
+                           G_TYPE_INVALID)) {
+        LOG(log_error, logtype_afpd, "Failed to acquire DBUS name: %s", error->message);
+        return NULL;
+    }
+
+    AFPStatsObj *obj = g_object_new(AFPSTATS_TYPE_OBJECT, NULL);
+    dbus_g_connection_register_g_object(bus, "/org/netatalk/AFPStats", G_OBJECT(obj));
+
+    g_main_loop_run(thread_loop);
+    return thread_loop;
+}
+
+static void my_glib_log(const gchar *log_domain,
+                        GLogLevelFlags log_level,
+                        const gchar *message,
+                        gpointer user_data)
+{
+    LOG(log_error, logtype_afpd, "%s: %s", log_domain, message);
+}
+
+server_child_t *afpstats_get_and_lock_childs(void)
+{
+    pthread_mutex_lock(&childs->servch_lock);
+    return childs;
+}
+
+void afpstats_unlock_childs(void)
+{
+    pthread_mutex_unlock(&childs->servch_lock);
+}
+
+int afpstats_init(server_child_t *childs_in)
+{
+    GThread *thread;
+
+    childs = childs_in;
+    g_type_init();
+    g_thread_init(NULL);
+    dbus_g_thread_init();
+    (void)g_log_set_default_handler(my_glib_log, NULL);
+
+    thread = g_thread_create(afpstats_thread, NULL, TRUE, NULL);
+
+    return 0;
+}
diff --git a/etc/afpd/afpstats.h b/etc/afpd/afpstats.h
new file mode 100644 (file)
index 0000000..9f742ad
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AFPD_AFPSTATS_H
+#define AFPD_AFPSTATS_H
+
+#include <atalk/server_child.h>
+
+extern int afpstats_init(server_child_t *);
+extern server_child_t *afpstats_get_and_lock_childs(void);
+extern void afpstats_unlock_childs(void);
+#endif
diff --git a/etc/afpd/afpstats_obj.c b/etc/afpd/afpstats_obj.c
new file mode 100644 (file)
index 0000000..755d7a5
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <strings.h>
+#include <pwd.h>
+
+#include <glib-object.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <atalk/logger.h>
+#include <atalk/dsi.h>
+
+#include "afpstats.h"
+#include "afpstats_obj.h"
+
+struct AFPStatsObj
+{
+  GObject parent;
+};
+
+struct AFPStatsObjClass
+{
+  GObjectClass parent;
+};
+
+static void afpstats_obj_init(AFPStatsObj *obj)
+{
+}
+
+static void afpstats_obj_class_init(AFPStatsObjClass *klass)
+{
+}
+
+static gpointer afpstats_obj_parent_class = NULL;
+
+static void afpstats_obj_class_intern_init(gpointer klass)
+{
+       afpstats_obj_parent_class = g_type_class_peek_parent(klass);
+       afpstats_obj_class_init((AFPStatsObjClass *)klass);
+}
+
+GType afpstats_obj_get_type(void)
+{
+       static volatile gsize g_define_type_id__volatile = 0;
+       if (g_once_init_enter(&g_define_type_id__volatile)) {
+               GType g_define_type_id = g_type_register_static_simple(
+            G_TYPE_OBJECT,
+            g_intern_static_string("AFPStatsObj"),
+            sizeof(AFPStatsObjClass),
+            (GClassInitFunc)afpstats_obj_class_intern_init,
+                       sizeof(AFPStatsObj),
+                       (GInstanceInitFunc)afpstats_obj_init,
+                       (GTypeFlags)0);
+               g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+       }
+       return g_define_type_id__volatile;
+}
+
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error)
+{
+    gchar **names;
+    server_child_t *childs = afpstats_get_and_lock_childs();
+    afp_child_t *child;
+    struct passwd *pw;
+    int i = 0, j;
+    char buf[256];
+
+    names = g_new(char *, childs->servch_count + 1);
+
+    for (j = 0; j < CHILD_HASHSIZE && i < childs->servch_count; j++) {
+        child = childs->servch_table[j];
+        while (child) {
+            if (child->afpch_valid && (pw = getpwuid(child->afpch_uid))) {
+                time_t time = child->afpch_logintime;
+                strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime(&time));
+                names[i++] = g_strdup_printf("name: %s, pid: %d, logintime: %s, state: %s, volumes: %s",
+                                             pw->pw_name, child->afpch_pid, buf,
+                                             child->afpch_state == DSI_RUNNING ? "active" :
+                                             child->afpch_state == DSI_SLEEPING ? "sleeping" :
+                                             child->afpch_state == DSI_EXTSLEEP ? "sleeping" :
+                                             child->afpch_state == DSI_DISCONNECTED ? "disconnected" :
+                                             "unknown",
+                                             child->afpch_volumes ? child->afpch_volumes : "-"); 
+            }
+            child = child->afpch_next;
+        }
+    }
+    names[i] = NULL;
+    *ret = names;
+
+    afpstats_unlock_childs();
+
+    return TRUE;
+}
diff --git a/etc/afpd/afpstats_obj.h b/etc/afpd/afpstats_obj.h
new file mode 100644 (file)
index 0000000..9d83000
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef AFPSTATS_OBJ_H
+#define AFPSTATS_OBJ_H
+
+#include <glib.h>
+
+typedef struct AFPStatsObj AFPStatsObj;
+typedef struct AFPStatsObjClass AFPStatsObjClass;
+
+GType    afpstats_obj_get_type(void);
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error);
+
+#define AFPSTATS_TYPE_OBJECT              (afpstats_obj_get_type ())
+#define AFPSTATS_OBJECT(object)           (G_TYPE_CHECK_INSTANCE_CAST((object), AFPSTATS_TYPE_OBJECT, AFPStatsObj))
+#define AFPSTATS_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+#define AFPSTATS_IS_OBJECT(object)        (G_TYPE_CHECK_INSTANCE_TYPE((object), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_IS_OBJECT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_OBJECT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+
+#endif /* AFPSTATS_OBJ_H */
diff --git a/etc/afpd/afpstats_service_glue.h b/etc/afpd/afpstats_service_glue.h
new file mode 100644 (file)
index 0000000..65081c4
--- /dev/null
@@ -0,0 +1,121 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+#define __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+
+#include       <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER */
+extern void dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                                     GValue       *return_value,
+                                                                     guint         n_param_values,
+                                                                     const GValue *param_values,
+                                                                     gpointer      invocation_hint,
+                                                                     gpointer      marshal_data);
+void
+dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                         GValue       *return_value G_GNUC_UNUSED,
+                                                         guint         n_param_values,
+                                                         const GValue *param_values,
+                                                         gpointer      invocation_hint G_GNUC_UNUSED,
+                                                         gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
+                                                             gpointer     arg_1,
+                                                             gpointer     arg_2,
+                                                             gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_afpstats_obj_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_afpstats_obj_methods[] = {
+  { (GCallback) afpstats_obj_get_users, dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER, 0 },
+};
+
+const DBusGObjectInfo dbus_glib_afpstats_obj_object_info = {  1,
+  dbus_glib_afpstats_obj_methods,
+  1,
+"org.netatalk.AFPStats\0GetUsers\0S\0ret\0O\0F\0N\0as\0\0\0",
+"\0",
+"\0"
+};
+
index f11d77eb22995a400ff43247cf7ddd4441babdbb..efe2ab6392f2fe1693b2d37ee0c9255fde06c099 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: afs.c,v 1.18 2009-10-15 10:43:13 didg Exp $
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
index 3cba16e0c2ae7eab2fe07c28a4cbc6b11498ad85..f53803e0dfc5f92ebcb5188ede9a146d2922a7b9 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: appl.c,v 1.18.4.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -171,7 +170,11 @@ makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, c
         }
 
         /* next part */
-        if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) {
+        AFP_CNID_START("cnid_resolve");
+        uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen);
+        AFP_CNID_DONE();
+
+        if (uname == NULL) {
             afp_errno = AFPERR_NOOBJ;
             ret = NULL;
             goto exit;
index ce1556b6930ee26b9569ad746dc726c3a722a821..8b38b37e5f19defcd3123c85d00aff4f5fa6063a 100644 (file)
@@ -39,6 +39,8 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include <atalk/server_ipc.h>
 #include <atalk/uuid.h>
 #include <atalk/globals.h>
+#include <atalk/spotlight.h>
+#include <atalk/unix.h>
 
 #include "auth.h"
 #include "uam_auth.h"
@@ -168,6 +170,7 @@ static int set_auth_switch(const AFPObj *obj, int expired)
         afp_switch = postauth_switch;
         switch (obj->afp_version) {
 
+        case 34:
         case 33:
         case 32:
 #ifdef HAVE_ACLS
@@ -183,7 +186,7 @@ static int set_auth_switch(const AFPObj *obj, int expired)
         case 31:
             uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
             uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
-            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
+            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_spotlight_rpc, NULL);
             uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
 
         case 30:
@@ -210,23 +213,6 @@ static int set_auth_switch(const AFPObj *obj, int expired)
     return AFP_OK;
 }
 
-#define GROUPSTR_BUFSIZE 1024
-static const char *print_groups(int ngroups, gid_t *groups)
-{
-    static char groupsstr[GROUPSTR_BUFSIZE];
-    int i;
-    char *s = groupsstr;
-
-    if (ngroups == 0)
-        return "-";
-
-    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
-        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
-    }
-
-    return groupsstr;
-}
-
 static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expired)
 {
 #ifdef ADMIN_GRP
@@ -238,35 +224,16 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi
         return AFPERR_NOTAUTH;
     }
 
-    LOG(log_note, logtype_afpd, "%s Login by %s",
-        afp_versions[afp_version_index].av_name, pwd->pw_name);
-
-    if (initgroups( pwd->pw_name, pwd->pw_gid ) < 0) {
-#ifdef RUN_AS_USER
-        LOG(log_info, logtype_afpd, "running with uid %d", geteuid());
-#else /* RUN_AS_USER */
-        LOG(log_error, logtype_afpd, "login: %s", strerror(errno));
-        return AFPERR_BADUAM;
-#endif /* RUN_AS_USER */
-
+    if (obj->cnx_cnt > obj->cnx_max) {
+        LOG(log_error, logtype_dsi, "login: too many connections, limit: %d", obj->cnx_max);
+        return AFPERR_MAXSESS;
     }
 
-    /* Basically if the user is in the admin group, we stay root */
+    LOG(log_note, logtype_afpd, "Login by %s (%s)",
+        pwd->pw_name, afp_versions[afp_version_index].av_name);
 
-    if ((obj->ngroups = getgroups( 0, NULL )) < 0 ) {
-        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno) );
+    if (set_groups(obj, pwd) != 0)
         return AFPERR_BADUAM;
-    }
-
-    if ( NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
-        LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
-        return AFPERR_BADUAM;
-    }
-
-    if (( obj->ngroups = getgroups(obj->ngroups, obj->groups )) < 0 ) {
-        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno) );
-        return AFPERR_BADUAM;
-    }
 
 #ifdef ADMIN_GRP
     LOG(log_debug, logtype_afpd, "obj->options.admingid == %d", obj->options.admingid);
@@ -375,6 +342,7 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
         if (dsi->flags & DSI_EXTSLEEP) {
             LOG(log_note, logtype_afpd, "afp_zzz: waking up from extended sleep");
             dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
+            ipc_child_state(obj, DSI_RUNNING);
         }
     } else {
         /* sleep request */
@@ -382,8 +350,10 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
         if (data & AFPZZZ_EXT_SLEEP) {
             LOG(log_note, logtype_afpd, "afp_zzz: entering extended sleep");
             dsi->flags |= DSI_EXTSLEEP;
+            ipc_child_state(obj, DSI_EXTSLEEP);
         } else {
             LOG(log_note, logtype_afpd, "afp_zzz: entering normal sleep");
+            ipc_child_state(obj, DSI_SLEEPING);
         }
     }
 
@@ -912,7 +882,7 @@ int afp_changepw(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rb
         if ( ibuf[0] != '\0' || ibuf[1] != '\0')
             return AFPERR_PARAM;
         ibuf += 2;
-        len = MIN(sizeof(username), strlen(obj->username));
+        len = MIN(sizeof(username) - 1, strlen(obj->username));
         memcpy(username, obj->username, len);
         username[ len ] = '\0';
     }
@@ -1049,9 +1019,9 @@ int auth_register(const int type, struct uam_obj *uam)
 }
 
 /* load all of the modules */
-int auth_load(const char *path, const char *list)
+int auth_load(AFPObj *obj, const char *path, const char *list)
 {
-    char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p;
+    char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p, *last;
     struct uam_mod *mod;
     struct stat st;
     size_t len;
@@ -1059,8 +1029,10 @@ int auth_load(const char *path, const char *list)
     if (!path || !*path || !list || (len = strlen(path)) > sizeof(name) - 2)
         return -1;
 
+    LOG(log_debug, logtype_afpd, "auth_load: %s, %s", path, list);
+
     strlcpy(buf, list, sizeof(buf));
-    if ((p = strtok(buf, ", ")) == NULL)
+    if ((p = strtok_r(buf, ", ", &last)) == NULL)
         return -1;
 
     strcpy(name, path);
@@ -1076,7 +1048,7 @@ int auth_load(const char *path, const char *list)
           if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) {
         */
         if (stat(name, &st) == 0) {
-            if ((mod = uam_load(name, p))) {
+            if ((mod = uam_load(obj, name, p))) {
                 uam_attach(&uam_modules, mod);
                 LOG(log_debug, logtype_afpd, "uam: %s loaded", p);
             } else {
@@ -1085,7 +1057,7 @@ int auth_load(const char *path, const char *list)
         } else {
             LOG(log_info, logtype_afpd, "uam: uam not found (status=%d)", stat(name, &st));
         }
-        p = strtok(NULL, ", ");
+        p = strtok_r(NULL, ", ", &last);
     }
 
     return 0;
index 2e9d6a584e3f66a94166f9337f49e296d8ac61b0..f53513b6a7f34a4d5b37abd09607d2c9a51e9e2e 100644 (file)
@@ -20,7 +20,8 @@ static const struct afp_versions  afp_versions[] = {
     { "AFPX03", 30 },
     { "AFP3.1", 31 },
     { "AFP3.2", 32 },
-    { "AFP3.3", 33 }
+    { "AFP3.3", 33 },
+    { "AFP3.4", 34 }
 };
 
 /* for GetUserInfo */
index 71843e2cd13237863791a396e9e389a8156dcf46..bec7b15245b195e1311fde72d72eba838108a9d2 100644 (file)
@@ -29,6 +29,9 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -43,7 +46,7 @@
 #include <atalk/adouble.h>
 #include <atalk/logger.h>
 #include <atalk/cnid.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/util.h>
 #include <atalk/bstradd.h>
 #include <atalk/unicode.h>
@@ -109,12 +112,8 @@ struct scrit {
  *
  */
 struct dsitem {
-//     struct dir *dir; /* Structure describing this directory */
-//  cnid_t did;      /* CNID of this directory           */
-       int pidx;        /* Parent's dsitem structure index. */
-       int checked;     /* Have we checked this directory ? */
-       int path_len;
-       char *path;      /* absolute UNIX path to this directory */
+    cnid_t ds_did;         /* CNID of this directory           */
+    int    ds_checked;     /* Have we checked this directory ? */
 };
  
 
@@ -131,7 +130,6 @@ static void clearstack(void)
        save_cidx = -1;
        while (dsidx > 0) {
                dsidx--;
-               free(dstack[dsidx].path);
        }
 }
 
@@ -139,7 +137,6 @@ static void clearstack(void)
 static int addstack(char *uname, struct dir *dir, int pidx)
 {
        struct dsitem *ds;
-       size_t         l, u;
     struct dsitem *tmpds = NULL;
 
        /* check if we have some space on stack... */
@@ -156,23 +153,8 @@ static int addstack(char *uname, struct dir *dir, int pidx)
 
        /* Put new element. Allocate and copy lname and path. */
        ds = dstack + dsidx++;
-//     ds->did = dir->d_did;
-       ds->pidx = pidx;
-       ds->checked = 0;
-       if (pidx >= 0) {
-           l = dstack[pidx].path_len;
-           u = strlen(uname) +1;
-           if (!(ds->path = malloc(l + u + 1) ))
-                       return -1;
-               memcpy(ds->path, dstack[pidx].path, l);
-               ds->path[l] = '/';
-               memcpy(&ds->path[l+1], uname, u);
-               ds->path_len = l +u;
-       }
-       else {
-           ds->path = strdup(uname);
-               ds->path_len = strlen(uname);
-       }
+       ds->ds_did = dir->d_did;
+       ds->ds_checked = 0;
        return 0;
 }
 
@@ -187,9 +169,9 @@ static int reducestack(void)
        }
 
        while (dsidx > 0) {
-               if (dstack[dsidx-1].checked) {
+               if (dstack[dsidx-1].ds_checked) {
                        dsidx--;
-                       free(dstack[dsidx].path);
+//                     free(dstack[dsidx].path);
                } else
                        return dsidx - 1;
        } 
@@ -211,7 +193,7 @@ static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adoub
            
        isdir  = S_ISDIR(path->st.st_mode);
 
-       if (!isdir && (of = of_findname(path))) {
+       if (!isdir && (of = of_findname(vol, path))) {
                adp = of->of_ad;
        } else {
                ad_init(&ad, vol);
@@ -521,7 +503,8 @@ static int catsearch(const AFPObj *obj,
     int num_rounds = NUM_ROUNDS;
     int cwd = -1;
     int error;
-        
+    int unlen;
+
        if (*pos != 0 && *pos != cur_pos) {
                result = AFPERR_CATCHNG;
                goto catsearch_end;
@@ -555,20 +538,24 @@ static int catsearch(const AFPObj *obj,
     start_time = time(NULL);
 
        while ((cidx = reducestack()) != -1) {
-        LOG(log_debug, logtype_afpd, "catsearch: dir: \"%s\"", dstack[cidx].path);
+        if ((currentdir = dirlookup(vol, dstack[cidx].ds_did)) == NULL) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+        LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath));
 
-               error = lchdir(dstack[cidx].path);
+               error = movecwd(vol, currentdir);
 
                if (!error && dirpos == NULL)
                        dirpos = opendir(".");
 
                if (dirpos == NULL)
-                       dirpos = opendir(dstack[cidx].path);
+                       dirpos = opendir(cfrombstr(currentdir->d_fullpath));
 
                if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
-                               dstack[cidx].checked = 1;
+                               dstack[cidx].ds_checked = 1;
                                continue;
                        case EMFILE:
                        case ENFILE:
@@ -583,11 +570,6 @@ static int catsearch(const AFPObj *obj,
                        goto catsearch_end;
                }
 
-        if ((currentdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) {
-            result = AFPERR_MISC;
-            goto catsearch_end;
-        }
-        LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath));
                
                while ((entry = readdir(dirpos)) != NULL) {
                        (*pos)++;
@@ -600,7 +582,7 @@ static int catsearch(const AFPObj *obj,
 
                        memset(&path, 0, sizeof(path));
                        path.u_name = entry->d_name;
-                       if (of_stat(&path) != 0) {
+                       if (of_stat(vol, &path) != 0) {
                                switch (errno) {
                                case EACCES:
                                case ELOOP:
@@ -615,12 +597,13 @@ static int catsearch(const AFPObj *obj,
                                        goto catsearch_end;
                                } 
                        }
-                       if (S_ISDIR(path.st.st_mode)) {
+            switch (S_IFMT & path.st.st_mode) {
+            case S_IFDIR:
                                /* here we can short cut 
                                   ie if in the same loop the parent dir wasn't in the cache
                                   ALL dirsearch_byname will fail.
                                */
-                int unlen = strlen(path.u_name);
+                unlen = strlen(path.u_name);
                 path.d_dir = dircache_search_by_name(vol,
                                                      currentdir,
                                                      path.u_name,
@@ -641,8 +624,12 @@ static int catsearch(const AFPObj *obj,
                                        result = AFPERR_MISC;
                                        goto catsearch_end;
                                } 
-            } else {
+                break;
+            case S_IFREG:
                path.d_dir = currentdir;
+                break;
+            default:
+                continue;
             }
 
                        ccr = crit_check(vol, &path);
@@ -674,7 +661,7 @@ static int catsearch(const AFPObj *obj,
                } /* while ((entry=readdir(dirpos)) != NULL) */
                closedir(dirpos);
                dirpos = NULL;
-               dstack[cidx].checked = 1;
+               dstack[cidx].ds_checked = 1;
        } /* while (current_idx = reducestack()) != -1) */
 
        /* We have finished traversing our tree. Return EOF here. */
@@ -756,11 +743,14 @@ static int catsearch_db(const AFPObj *obj,
 
         LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
 
-        if ((num_matches = cnid_find(vol->v_cdb,
-                                     buffer,
-                                     strlen(uname),
-                                     resbuf,
-                                     sizeof(resbuf))) == -1) {
+        AFP_CNID_START("cnid_find");
+        num_matches = cnid_find(vol->v_cdb,
+                                buffer,
+                                strlen(uname),
+                                resbuf,
+                                sizeof(resbuf));
+        AFP_CNID_DONE();
+        if (num_matches == -1) {
             result = AFPERR_MISC;
             goto catsearch_end;
         }
@@ -776,8 +766,12 @@ static int catsearch_db(const AFPObj *obj,
         memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
         did = cnid;
 
-        if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
+        AFP_CNID_START("cnid_resolve");
+        name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1);
+        AFP_CNID_DONE();
+        if (name == NULL)
             goto next;
+
         LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
             cur_pos, name, ntohl(cnid));
         if ((dir = dirlookup(vol, did)) == NULL)
@@ -789,7 +783,7 @@ static int catsearch_db(const AFPObj *obj,
         path.u_name = name;
         path.m_name = utompath(vol, name, cnid, utf8_encoding(vol->v_obj));
 
-        if (of_stat(&path) != 0) {
+        if (of_stat(vol, &path) != 0) {
             switch (errno) {
             case EACCES:
             case ELOOP:
index c437b7e6b4adb92f2a2c03388b401b3ad2eb8082..5c56977acd3fd30e6dea0659b40b68b19ac68898 100644 (file)
@@ -31,6 +31,7 @@
 #include <atalk/netatalk_conf.h>
 #include <atalk/unix.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/errchk.h>
 
 #include "volume.h"
@@ -61,7 +62,7 @@ int setdeskmode(const struct vol *vol, const mode_t mode)
     bstring dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    EC_NEG1( chdir(bdata(dtpath)) );
+    EC_NEG1( chdir(cfrombstr(dtpath)) );
 
     if (( desk = opendir( "." )) == NULL ) {
         if ( chdir( wd ) < 0 ) {
@@ -94,17 +95,29 @@ int setdeskmode(const struct vol *vol, const mode_t mode)
             }
 
             if (S_ISDIR(st.st_mode)) {
-                if ( chmod_acl( modbuf,  (DIRBITS | mode)) < 0 && errno != EPERM ) {
+                if (ochmod(modbuf,
+                           DIRBITS | mode,
+                           &st,
+                           vol_syml_opt(vol) | vol_chmod_opt(vol)
+                        ) < 0 && errno != EPERM) {
                      LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) );
                 }
-            } else if ( chmod_acl( modbuf,  mode & ~EXEC_MODE ) < 0 && errno != EPERM ) {
+            } else if (ochmod(modbuf,
+                           mode & ~EXEC_MODE,
+                           &st,
+                           vol_syml_opt(vol) | vol_chmod_opt(vol)
+                        ) < 0 && errno != EPERM) {
                 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) );
             }
 
         }
         closedir( sub );
         /* XXX: need to preserve special modes */
-        if ( chmod_acl( deskp->d_name,  (DIRBITS | mode)) < 0 && errno != EPERM ) {
+        if (ochmod(deskp->d_name,
+                   DIRBITS | mode,
+                   NULL,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0 && errno != EPERM) {
             LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(deskp->d_name), strerror(errno) );
         }
     }
@@ -114,7 +127,11 @@ int setdeskmode(const struct vol *vol, const mode_t mode)
         EC_FAIL;
     }
     /* XXX: need to preserve special modes */
-    if ( chmod_acl(bdata(dtpath),  (DIRBITS | mode)) < 0 && errno != EPERM ) {
+    if (ochmod(bdata(dtpath),
+               DIRBITS | mode,
+               NULL,
+               vol_syml_opt(vol) | vol_chmod_opt(vol)
+            ) < 0 && errno != EPERM) {
         LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s", bdata(dtpath), strerror(errno));
     }
 
@@ -138,7 +155,7 @@ int setdeskowner(const struct vol *vol, uid_t uid, gid_t gid)
     bstring dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    EC_NEG1( chdir(bdata(dtpath)) );
+    EC_NEG1( chdir(cfrombstr(dtpath)) );
     
     if (( desk = opendir( "." )) == NULL ) {
         if ( chdir( wd ) < 0 ) {
@@ -182,7 +199,7 @@ int setdeskowner(const struct vol *vol, uid_t uid, gid_t gid)
         LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
         EC_FAIL;
     }
-    if (chown(bdata(dtpath), uid, gid ) < 0 && errno != EPERM ) {
+    if (chown(cfrombstr(dtpath), uid, gid ) < 0 && errno != EPERM ) {
         LOG(log_error, logtype_afpd, "setdeskowner: chown %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
     }
 
@@ -203,11 +220,11 @@ static void create_appledesktop_folder(const struct vol * vol)
     dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    if (lstat(bdata(dtpath), &st) != 0) {
+    if (lstat(cfrombstr(dtpath), &st) != 0) {
 
         become_root();
 
-        if (lstat(bdata(olddtpath), &st) == 0) {
+        if (lstat(cfrombstr(olddtpath), &st) == 0) {
             cmd_argv[0] = "mv";
             cmd_argv[1] = bdata(olddtpath);
             cmd_argv[2] = bdata(dtpath);
@@ -215,10 +232,10 @@ static void create_appledesktop_folder(const struct vol * vol)
             if (run_cmd("mv", cmd_argv) != 0) {
                 LOG(log_error, logtype_afpd, "moving .AppleDesktop from \"%s\" to \"%s\" failed",
                     bdata(olddtpath), bdata(dtpath));
-                mkdir(bdata(dtpath), 0777);
+                mkdir(cfrombstr(dtpath), 0777);
             }
         } else {
-            mkdir(bdata(dtpath), 0777);
+            mkdir(cfrombstr(dtpath), 0777);
         }
 
         unbecome_root();
@@ -830,7 +847,7 @@ static int ad_addcomment(const AFPObj *obj, struct vol *vol, struct path *path,
     }
     
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
@@ -905,7 +922,7 @@ static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t
 
     upath = path->u_name;
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
@@ -982,7 +999,7 @@ static int ad_rmvcomment(const AFPObj *obj, struct vol *vol, struct path *path)
     }
 
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
index 7cd08f6f4ec5182a356447306d5ddfa8baa4f173..c145616b314887ea15bcdc7b0327ae9d3b7e1cb7 100644 (file)
@@ -324,7 +324,7 @@ struct dir *dircache_search_by_did(const struct vol *vol, cnid_t cnid)
             return NULL;        /* (1b) */
 
         }
-        if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+        if (ostat(cfrombstr(cdir->d_fullpath), &st, vol_syml_opt(vol)) != 0) {
             LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {missing:\"%s\"}",
                 ntohl(cnid), cfrombstr(cdir->d_fullpath));
             (void)dir_remove(vol, cdir);
@@ -394,7 +394,7 @@ struct dir *dircache_search_by_name(const struct vol *vol,
     }
 
     if (cdir) {
-        if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+        if (ostat(cfrombstr(cdir->d_fullpath), &st, vol_syml_opt(vol)) != 0) {
             LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {missing:\"%s\"}",
                 ntohl(dir->d_did), name, cfrombstr(cdir->d_fullpath));
             (void)dir_remove(vol, cdir);
@@ -434,7 +434,6 @@ struct dir *dircache_search_by_name(const struct vol *vol,
 int dircache_add(const struct vol *vol,
                  struct dir *dir)
 {
-    struct dir *cdir = NULL;
     struct dir key;
     hnode_t *hn;
 
index ed40a26a7faf5e330ea8739386e33979e02de26f..07a2bb7d439cfa5721f148d59f6316a7a59d0369 100644 (file)
@@ -134,7 +134,7 @@ static int netatalk_mkdir(const struct vol *vol, const char *name)
 }
 
 /* ------------------- */
-static int deletedir(int dirfd, char *dir)
+static int deletedir(const struct vol *vol, int dirfd, char *dir)
 {
     char path[MAXPATHLEN + 1];
     DIR *dp;
@@ -165,11 +165,11 @@ static int deletedir(int dirfd, char *dir)
             break;
         }
         strcpy(path + len, de->d_name);
-        if (lstatat(dirfd, path, &st)) {
+        if (ostatat(dirfd, path, &st, vol_syml_opt(vol))) {
             continue;
         }
         if (S_ISDIR(st.st_mode)) {
-            err = deletedir(dirfd, path);
+            err = deletedir(vol, dirfd, path);
         } else {
             err = netatalk_unlinkat(dirfd, path);
         }
@@ -185,7 +185,7 @@ static int deletedir(int dirfd, char *dir)
 }
 
 /* do a recursive copy. */
-static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
+static int copydir(struct vol *vol, struct dir *ddir, int dirfd, char *src, char *dst)
 {
     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
     DIR *dp;
@@ -231,7 +231,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
         }
         strcpy(spath + slen, de->d_name);
 
-        if (lstatat(dirfd, spath, &st) == 0) {
+        if (ostatat(dirfd, spath, &st, vol_syml_opt(vol)) == 0) {
             if (strlen(de->d_name) > drem) {
                 err = AFPERR_PARAM;
                 break;
@@ -239,9 +239,9 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
             strcpy(dpath + dlen, de->d_name);
 
             if (S_ISDIR(st.st_mode)) {
-                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+                if (AFP_OK != (err = copydir(vol, ddir, dirfd, spath, dpath)))
                     goto copydir_done;
-            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+            } else if (AFP_OK != (err = copyfile(vol, vol, ddir, dirfd, spath, dpath, NULL, NULL))) {
                 goto copydir_done;
 
             } else {
@@ -253,7 +253,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
     }
 
     /* keep the same time stamp. */
-    if (lstatat(dirfd, src, &st) == 0) {
+    if (ostatat(dirfd, src, &st, vol_syml_opt(vol)) == 0) {
         ut.actime = ut.modtime = st.st_mtime;
         utime(dst, &ut);
     }
@@ -301,7 +301,7 @@ static int set_dir_errors(struct path *path, const char *where, int err)
  *
  * @note If the passed ret->m_name is mangled, we'll demangle it
  */
-static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
+static int cname_mtouname(const struct vol *vol, struct dir *dir, struct path *ret, int toUTF8)
 {
     static char temp[ MAXPATHLEN + 1];
     char *t;
@@ -331,6 +331,12 @@ static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct p
 
         /* check for OS X mangled filename :( */
         t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+
+        if (curdir == NULL) {
+            /* demangle_osx() calls dirlookup() which might have clobbered curdir */
+            movecwd(vol, dir);
+        }
+
         LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
             ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
 
@@ -497,12 +503,15 @@ struct dir *dirlookup_bypath(const struct vol *vol, const char *path)
                                            cfrombstr(l->entry[i]),
                                            blength(l->entry[i]))) == NULL) {
 
-            if ((cnid = cnid_add(vol->v_cdb,             /* 6. */
-                                 &st,
-                                 did,
-                                 cfrombstr(l->entry[i]),
-                                 blength(l->entry[i]),
-                                 0)) == CNID_INVALID)
+            AFP_CNID_START("cnid_add");
+            cnid = cnid_add(vol->v_cdb,             /* 6. */
+                            &st,
+                            did,
+                            cfrombstr(l->entry[i]),
+                            blength(l->entry[i]),
+                            0);
+            AFP_CNID_DONE();
+            if (cnid == CNID_INVALID)
                 EC_FAIL;
 
             if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */
@@ -607,7 +616,11 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     /* Get it from the database */
     cnid = did;
     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): querying CNID database", ntohl(did));
-    if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) {
+
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen);
+    AFP_CNID_DONE();
+    if (upath == NULL) {
         afp_errno = AFPERR_NOOBJ;
         err = 1;
         goto exit;
@@ -643,7 +656,7 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): stating \"%s\"",
         ntohl(did), cfrombstr(fullpath));
 
-    if (lstat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+    if (ostat(cfrombstr(fullpath), &st, vol_syml_opt(vol)) != 0) { /* 5a */
         switch (errno) {
         case ENOENT:
             afp_errno = AFPERR_NOOBJ;
@@ -815,7 +828,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i
     cnid_t      id;
     struct adouble  ad;
     struct adouble *adp = NULL;
-    bstring fullpath;
+    bstring fullpath = NULL;
 
     AFP_ASSERT(vol);
     AFP_ASSERT(dir);
@@ -912,7 +925,7 @@ exit:
 void dir_free_invalid_q(void)
 {
     struct dir *dir;
-    while (dir = (struct dir *)dequeue(invalid_dircache_entries))
+    while ((dir = (struct dir *)dequeue(invalid_dircache_entries)))
         dir_free(dir);
 }
 
@@ -1159,6 +1172,14 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
             /* the name is illegal */
             LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name);
             afp_errno = AFPERR_PARAM;
+            if (vol->v_obj->options.flags & OPTION_VETOMSG) {
+                bstring message = bformat("Attempt to access vetoed file or directory \"%s\" in directory \"%s\"",
+                                          ret.u_name, bdata(dir->d_u_name));
+                if (setmessage(bdata(message)) == 0)
+                    /* Client may make multiple attempts, only send the message the first time */
+                    kill(getpid(), SIGUSR2);
+                bdestroy(message);
+            }
             return NULL;
         }
 
@@ -1181,7 +1202,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
              *   and thus call continue which should terminate the while loop because
              *   len = 0. Ok?
              */
-            if (of_stat(&ret) != 0) { /* 9 */
+            if (of_stat(vol, &ret) != 0) { /* 9 */
                 /*
                  * ret.u_name doesn't exist, might be afp_createfile|dir
                  * that means it should have been the last part
@@ -1299,7 +1320,7 @@ int movecwd(const struct vol *vol, struct dir *dir)
     LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")",
         ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
 
-    if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) {
+    if ((ret = ochdir(cfrombstr(dir->d_fullpath), vol_syml_opt(vol))) != 0 ) {
         LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): %s",
             cfrombstr(dir->d_fullpath), strerror(errno));
         if (ret == 1) {
@@ -1465,6 +1486,7 @@ int getdirparams(const AFPObj *obj,
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
+            ashort &= ~htons(vol->v_ignattr);
             memcpy( data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
             break;
@@ -1501,10 +1523,6 @@ int getdirparams(const AFPObj *obj,
                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
             } else { /* no appledouble */
                 memset( data, 0, 32 );
-                /* set default view -- this also gets done in ad_open() */
-                ashort = htons(FINDERINFO_CLOSEDVIEW);
-                memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
-
                 /* dot files are by default visible */
                 if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
                     ashort = htons(FINDERINFO_INVISIBLE);
@@ -1857,6 +1875,7 @@ int setdirparams(struct vol *vol, struct path *path, uint16_t d_bitmap, char *bu
                 ad_getattr(&ad, &bshort);
                 oshort = bshort;
                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+                    ashort &= ~htons(vol->v_ignattr);
                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
                 } else {
                     bshort &= ~ashort;
@@ -2174,7 +2193,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
         return err;
     }
 
-    if (of_stat(s_path) < 0) {
+    if (of_stat(vol, s_path) < 0) {
         return AFPERR_MISC;
     }
 
@@ -2195,12 +2214,11 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_setname(&ad, s_path->m_name);
     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
 
-    fce_register_new_dir(s_path);
+    fce_register(FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL, fce_dir);
 
     ad_flush(&ad);
     ad_close(&ad, ADFLAGS_HF);
 
-createdir_done:
     memcpy( rbuf, &dir->d_did, sizeof( uint32_t ));
     *rbuflen = sizeof( uint32_t );
     setvoltime(obj, vol );
@@ -2213,7 +2231,7 @@ createdir_done:
  * newparent curdir
  * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
  */
-int renamedir(const struct vol *vol,
+int renamedir(struct vol *vol,
               int dirfd,
               char *src,
               char *dst,
@@ -2239,11 +2257,11 @@ int renamedir(const struct vol *vol,
         case EXDEV:
             /* this needs to copy and delete. bleah. that means we have
              * to deal with entire directory hierarchies. */
-            if ((err = copydir(vol, dirfd, src, dst)) < 0) {
-                deletedir(-1, dst);
+            if ((err = copydir(vol, newparent, dirfd, src, dst)) < 0) {
+                deletedir(vol, -1, dst);
                 return err;
             }
-            if ((err = deletedir(dirfd, src)) < 0)
+            if ((err = deletedir(vol, dirfd, src)) < 0)
                 return err;
             break;
         default :
@@ -2270,7 +2288,6 @@ int deletecurdir(struct vol *vol)
     struct dirent *de;
     struct stat st;
     struct dir  *fdir, *pdir;
-    DIR *dp;
     struct adouble  ad;
     uint16_t       ashort;
     int err;
@@ -2287,39 +2304,17 @@ int deletecurdir(struct vol *vol)
 
         ad_getattr(&ad, &ashort);
         ad_close(&ad, ADFLAGS_HF);
-        if ((ashort & htons(ATTRBIT_NODELETE))) {
+        if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (ashort & htons(ATTRBIT_NODELETE))) {
             return  AFPERR_OLOCK;
         }
     }
     err = vol->vfs->vfs_deletecurdir(vol);
     if (err) {
-        LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
+        LOG(log_error, logtype_afpd, "deletecurdir: error deleting AppleDouble files in \"%s\"",
             cfrombstr(curdir->d_fullpath));
         return err;
     }
 
-    /* now get rid of dangling symlinks */
-    if ((dp = opendir("."))) {
-        while ((de = readdir(dp))) {
-            /* skip this and previous directory */
-            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                continue;
-
-            /* bail if it's not a symlink */
-            if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
-                LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
-                    curdir->d_fullpath);
-                closedir(dp);
-                return AFPERR_DIRNEMPT;
-            }
-
-            if ((err = netatalk_unlink(de->d_name))) {
-                closedir(dp);
-                return err;
-            }
-        }
-    }
-
     if (movecwd(vol, pdir) < 0) {
         err = afp_errno;
         goto delete_done;
@@ -2329,22 +2324,29 @@ int deletecurdir(struct vol *vol)
         cfrombstr(curdir->d_fullpath));
 
     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
-    if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
-        cnid_delete(vol->v_cdb, fdir->d_did);
-        dir_remove( vol, fdir );
-    } else {
+
+    switch (err) {
+    case AFP_OK:
+    case AFPERR_NOOBJ:
+        break;
+    case AFPERR_DIRNEMPT:
+        if (delete_vetoed_files(vol, bdata(fdir->d_u_name), false) != 0)
+            goto delete_done;
+        err = AFP_OK;
+        break;
+    default:
         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
             cfrombstr(curdir->d_fullpath));
+        goto delete_done;
     }
 
+    AFP_CNID_START("cnid_delete");
+    cnid_delete(vol->v_cdb, fdir->d_did);
+    AFP_CNID_DONE();
+
+    dir_remove( vol, fdir );
+
 delete_done:
-    if (dp) {
-        /* inode is used as key for cnid.
-         * Close the descriptor only after cnid_delete
-         * has been called.
-         */
-        closedir(dp);
-    }
     return err;
 }
 
@@ -2622,7 +2624,7 @@ int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, si
         return path_error(path, AFPERR_NOOBJ);
     }
 
-    if ( !path->st_valid && of_stat(path ) < 0 ) {
+    if ( !path->st_valid && of_stat(vol, path) < 0 ) {
         return( AFPERR_NOOBJ );
     }
     if ( path->st_errno ) {
index 7f3e8c56a1cd135c2405abb5fb3af61a700222dd..eb89c606afc41cce1994851265283bed05d27e9a 100644 (file)
@@ -115,7 +115,7 @@ extern int         getdirparams (const AFPObj *obj, const struct vol *, uint16_t
                                  struct dir *, char *, size_t *);
 
 extern int         setdirparams(struct vol *, struct path *, uint16_t, char *);
-extern int         renamedir(const struct vol *, int, char *, char *, struct dir *,
+extern int         renamedir(struct vol *, int, char *, char *, struct dir *,
                              struct dir *, char *);
 extern int         path_error(struct path *, int error);
 extern void        setdiroffcnt(struct dir *dir, struct stat *st,  uint32_t count);
index d469928c357b465a5781d2f5f51c9aad51fb8a2e..340f79aed6738676acd8341f7c1d7a46171324da 100644 (file)
@@ -284,7 +284,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
         sd.sd_last = sd.sd_buf;
         /* if dir was in the cache we don't have the inode */
-        if (( !o_path->st_valid && lstat( ".", &o_path->st ) < 0 ) ||
+        if (( !o_path->st_valid && ostat(".", &o_path->st, vol_syml_opt(vol)) < 0 ) ||
             (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
         {
             LOG(log_error, logtype_afpd, "enumerate: loop error: %s (%d)", strerror(errno), errno);
@@ -346,16 +346,10 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             sd.sd_last += len + 1;
             continue;
         }
-        memset(&s_path, 0, sizeof(s_path));
 
-        /* conversions on the fly */
-        const char *convname;
+        memset(&s_path, 0, sizeof(s_path));
         s_path.u_name = sd.sd_last;
-        if (ad_convert(sd.sd_last, &s_path.st, vol, &convname) == 0 && convname) {
-            s_path.u_name = (char *)convname;
-        }
-
-        if (of_stat( &s_path) < 0 ) {
+        if (of_stat(vol, &s_path) < 0 ) {
             /* so the next time it won't try to stat it again
              * another solution would be to invalidate the cache with 
              * sd.sd_did = 0 but if it's not ENOENT error it will start again
@@ -366,12 +360,21 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             continue;
         }
 
-        /* Fixup CNID db if ad_convert resulted in a rename (then convname != NULL) */
-        if (convname) {
-            s_path.id = cnid_lookup(vol->v_cdb, &s_path.st, curdir->d_did, sd.sd_last, strlen(sd.sd_last));
-            if (s_path.id != CNID_INVALID) {
-                if (cnid_update(vol->v_cdb, s_path.id, &s_path.st, curdir->d_did, convname, strlen(convname)) != 0)
-                    LOG(log_error, logtype_afpd, "enumerate: error updating CNID of \"%s\"", fullpathname(convname));
+        /* conversions on the fly */
+        const char *convname;
+        if (ad_convert(sd.sd_last, &s_path.st, vol, &convname) == 0) {
+            if (convname) {
+                s_path.u_name = (char *)convname;
+                AFP_CNID_START("cnid_lookup");
+                s_path.id = cnid_lookup(vol->v_cdb, &s_path.st, curdir->d_did, sd.sd_last, strlen(sd.sd_last));
+                AFP_CNID_DONE();
+                if (s_path.id != CNID_INVALID) {
+                    AFP_CNID_START("cnid_update");
+                    int cnid_up_ret = cnid_update(vol->v_cdb, s_path.id, &s_path.st, curdir->d_did, (char *)convname, strlen(convname));
+                    AFP_CNID_DONE();
+                    if (cnid_up_ret != 0)
+                        LOG(log_error, logtype_afpd, "enumerate: error updating CNID of \"%s\"", fullpathname(convname));
+                }
             }
         }
 
index 4cbeb6423ddd316cb28dc32cec5488ce98ed0f81..f6b1cf66178a585c5475aa9f781c8e21ea939612 100644 (file)
@@ -166,13 +166,6 @@ int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
             }
         } else {
             FinderInfo = ad_entry(adp, ADEID_FINDERI);
-
-            if ((adflags & ADFLAGS_DIR)) {
-                /* set default view */
-                uint16 = htons(FINDERINFO_CLOSEDVIEW);
-                memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
-            }
-
             /* Check if FinderInfo equals default and empty FinderInfo*/
             if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
                 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
index 64e8482d233ae4a20c5efe6a1b6ca92e1342c470..68e65905f97e46b65a899e7d66f6ae47271a24cf 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: extattrs.h,v 1.3 2009-10-15 10:43:13 didg Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index b40eafb988a7bf6681faf8fc8a60a2ebcafc40a3..4620378922d763010ac006a700670f5fd194fdc1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010 Mark Williams
+ * Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
  *
  * File change event API for netatalk
  *
 #include <stdlib.h>
 #include <errno.h>
 #include <time.h>
-
-
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <stdbool.h>
 
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 // ONLY USED IN THIS FILE
 #include "fce_api_internal.h"
 
-#define FCE_TRUE 1
-#define FCE_FALSE 0
-
 /* We store our connection data here */
 static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS];
 static int udp_sockets = 0;
-static int udp_initialized = FCE_FALSE;
+static bool udp_initialized = false;
 static unsigned long fce_ev_enabled =
     (1 << FCE_FILE_MODIFY) |
     (1 << FCE_FILE_DELETE) |
@@ -70,9 +67,8 @@ static unsigned long fce_ev_enabled =
     (1 << FCE_FILE_CREATE) |
     (1 << FCE_DIR_CREATE);
 
-static uint64_t tm_used;          /* used for passing to event handler */
 #define MAXIOBUF 1024
-static char iobuf[MAXIOBUF];
+static unsigned char iobuf[MAXIOBUF];
 static const char *skip_files[] = 
 {
        ".DS_Store",
@@ -80,6 +76,15 @@ static const char *skip_files[] =
 };
 static struct fce_close_event last_close_event;
 
+static char *fce_event_names[] = {
+    "",
+    "FCE_FILE_MODIFY",
+    "FCE_FILE_DELETE",
+    "FCE_DIR_DELETE",
+    "FCE_FILE_CREATE",
+    "FCE_DIR_CREATE"
+};
+
 /*
  *
  * Initialize network structs for any listeners
@@ -91,7 +96,7 @@ void fce_init_udp()
     int rv;
     struct addrinfo hints, *servinfo, *p;
 
-    if (udp_initialized == FCE_TRUE)
+    if (udp_initialized == true)
         return;
 
     memset(&hints, 0, sizeof hints);
@@ -106,7 +111,7 @@ void fce_init_udp()
             close(udp_entry->sock);
 
         if ((rv = getaddrinfo(udp_entry->addr, udp_entry->port, &hints, &servinfo)) != 0) {
-            LOG(log_error, logtype_afpd, "fce_init_udp: getaddrinfo(%s:%s): %s",
+            LOG(log_error, logtype_fce, "fce_init_udp: getaddrinfo(%s:%s): %s",
                 udp_entry->addr, udp_entry->port, gai_strerror(rv));
             continue;
         }
@@ -114,7 +119,7 @@ void fce_init_udp()
         /* loop through all the results and make a socket */
         for (p = servinfo; p != NULL; p = p->ai_next) {
             if ((udp_entry->sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
-                LOG(log_error, logtype_afpd, "fce_init_udp: socket(%s:%s): %s",
+                LOG(log_error, logtype_fce, "fce_init_udp: socket(%s:%s): %s",
                     udp_entry->addr, udp_entry->port, strerror(errno));
                 continue;
             }
@@ -122,7 +127,7 @@ void fce_init_udp()
         }
 
         if (p == NULL) {
-            LOG(log_error, logtype_afpd, "fce_init_udp: no socket for %s:%s",
+            LOG(log_error, logtype_fce, "fce_init_udp: no socket for %s:%s",
                 udp_entry->addr, udp_entry->port);
         }
         udp_entry->addrinfo = *p;
@@ -131,12 +136,12 @@ void fce_init_udp()
         freeaddrinfo(servinfo);
     }
 
-    udp_initialized = FCE_TRUE;
+    udp_initialized = true;
 }
 
 void fce_cleanup()
 {
-    if (udp_initialized == FCE_FALSE )
+    if (udp_initialized == false )
         return;
 
     for (int i = 0; i < udp_sockets; i++)
@@ -150,22 +155,21 @@ void fce_cleanup()
             udp_entry->sock = -1;
         }
     }
-    udp_initialized = FCE_FALSE;
+    udp_initialized = false;
 }
 
 /*
  * Construct a UDP packet for our listeners and return packet size
  * */
-static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id )
+static ssize_t build_fce_packet( struct fce_packet *packet, const char *path, int event, uint32_t event_id )
 {
     size_t pathlen = 0;
     ssize_t data_len = 0;
-    uint64_t *t;
 
     /* Set content of packet */
     memcpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) );
     packet->version = FCE_PACKET_VERSION;
-    packet->mode = mode;
+    packet->mode = event;
    
     packet->event_id = event_id; 
 
@@ -180,19 +184,7 @@ static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode
     /* This is the payload len. Means: the packet has len bytes more until packet is finished */
     data_len = FCE_PACKET_HEADER_SIZE + pathlen;
 
-    switch (mode) {
-    case FCE_TM_SIZE:
-        t = (uint64_t *)packet->data;
-        *t = hton64(tm_used);
-        memcpy(packet->data + sizeof(tm_used), path, pathlen);
-        
-        packet->datalen = pathlen + sizeof(tm_used);
-        data_len += sizeof(tm_used);
-        break;
-    default:
-        memcpy(packet->data, path, pathlen);
-        break;
-    }
+    memcpy(packet->data, path, pathlen);
 
     /* return the packet len */
     return data_len;
@@ -231,27 +223,26 @@ static void pack_fce_packet(struct fce_packet *packet, unsigned char *buf, int m
  * Send the fce information to all (connected) listeners
  * We dont give return code because all errors are handled internally (I hope..)
  * */
-static void send_fce_event( char *path, int mode )
+static void send_fce_event(const char *path, int event)
 {    
-    static int first_event = FCE_TRUE;
+    static bool first_event = true;
 
     struct fce_packet packet;
-    void *data = &packet;
     static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */
     time_t now = time(NULL);
 
-    LOG(log_debug, logtype_afpd, "send_fce_event: start");
+    LOG(log_debug, logtype_fce, "send_fce_event: start");
 
     /* initialized ? */
-    if (first_event == FCE_TRUE) {
-        first_event = FCE_FALSE;
+    if (first_event == true) {
+        first_event = false;
         fce_init_udp();
         /* Notify listeners the we start from the beginning */
         send_fce_event( "", FCE_CONN_START );
     }
 
     /* build our data packet */
-    ssize_t data_len = build_fce_packet( &packet, path, mode, ++event_id );
+    ssize_t data_len = build_fce_packet( &packet, path, event, ++event_id );
     pack_fce_packet(&packet, iobuf, MAXIOBUF);
 
     for (int i = 0; i < udp_sockets; i++)
@@ -273,7 +264,7 @@ static void send_fce_event( char *path, int mode )
             
             if (udp_entry->sock == -1) {
                 /* failed again, so go to rest again */
-                LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno  );
+                LOG(log_error, logtype_fce, "Cannot recreate socket for fce UDP connection: errno %d", errno  );
 
                 udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;
                 continue;
@@ -293,7 +284,7 @@ static void send_fce_event( char *path, int mode )
                    udp_entry->addrinfo.ai_addrlen);
 
             /* Rebuild our original data packet */
-            data_len = build_fce_packet( &packet, path, mode, event_id );
+            data_len = build_fce_packet(&packet, path, event, event_id);
             pack_fce_packet(&packet, iobuf, MAXIOBUF);
         }
 
@@ -307,7 +298,7 @@ static void send_fce_event( char *path, int mode )
         /* Problems ? */
         if (sent_data != data_len) {
             /* Argh, socket broke, we close and retry later */
-            LOG(log_error, logtype_afpd, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",
+            LOG(log_error, logtype_fce, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",
                 udp_entry->addr, udp_entry->port, sent_data, data_len, strerror(errno));
 
             close( udp_entry->sock );
@@ -323,7 +314,7 @@ static int add_udp_socket(const char *target_ip, const char *target_port )
         target_port = FCE_DEFAULT_PORT_STRING;
 
     if (udp_sockets >= FCE_MAX_UDP_SOCKS) {
-        LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );
+        LOG(log_error, logtype_fce, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );
         return AFPERR_PARAM;
     }
 
@@ -350,7 +341,7 @@ static void save_close_event(const char *path)
         send_fce_event(last_close_event.path, FCE_FILE_MODIFY);
     }
 
-    LOG(log_debug, logtype_afpd, "save_close_event: %s", path);
+    LOG(log_debug, logtype_fce, "save_close_event: %s", path);
 
     last_close_event.time = now;
     strncpy(last_close_event.path, path, MAXPATHLEN);
@@ -361,66 +352,54 @@ static void save_close_event(const char *path)
  * Dispatcher for all incoming file change events
  *
  * */
-static int register_fce(const char *u_name, int is_dir, int mode)
+int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type)
 {
-    static int first_event = FCE_TRUE;
+    static bool first_event = true;
+    const char *bname;
+
+    if (!(fce_ev_enabled & (1 << event)))
+        return AFP_OK;
+
+    AFP_ASSERT(event >= FCE_FIRST_EVENT && event <= FCE_LAST_EVENT);
+    AFP_ASSERT(path);
+
+    LOG(log_debug, logtype_fce, "register_fce(path: %s, type: %s, event: %s",
+        path, type == fce_dir ? "dir" : "file", fce_event_names[event]);
+
+    bname = basename_safe(path);
 
     if (udp_sockets == 0)
         /* No listeners configured */
         return AFP_OK;
 
-    if (u_name == NULL)
-        return AFPERR_PARAM;
 
        /* do some initialization on the fly the first time */
        if (first_event) {
                fce_initialize_history();
-        first_event = FCE_FALSE;
+        first_event = false;
        }
 
        /* handle files which should not cause events (.DS_Store atc. ) */
-       for (int i = 0; skip_files[i] != NULL; i++)
-       {
-               if (!strcmp( u_name, skip_files[i]))
+       for (int i = 0; skip_files[i] != NULL; i++) {
+               if (strcmp(bname, skip_files[i]) == 0)
                        return AFP_OK;
        }
 
-
-       char full_path_buffer[MAXPATHLEN + 1] = {""};
-       const char *cwd = getcwdpath();
-
-    if (mode == FCE_TM_SIZE) {
-        strlcpy(full_path_buffer, u_name, MAXPATHLEN);
-    } else if (!is_dir || mode == FCE_DIR_DELETE) {
-               if (strlen( cwd ) + strlen( u_name) + 1 >= MAXPATHLEN) {
-                       LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name );
-                       return AFPERR_PARAM;
-               }
-               sprintf( full_path_buffer, "%s/%s", cwd, u_name );
-       } else {
-               if (strlen( cwd ) >= MAXPATHLEN) {
-                       LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd);
-                       return AFPERR_PARAM;
-               }
-               strcpy( full_path_buffer, cwd);
-       }
-
        /* Can we ignore this event based on type or history? */
-       if (!(mode & FCE_TM_SIZE) && fce_handle_coalescation( full_path_buffer, is_dir, mode ))
-       {
-               LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer );
+       if (fce_handle_coalescation(event, path, type)) {
+               LOG(log_debug9, logtype_fce, "Coalesced fc event <%d> for <%s>", event, path);
                return AFP_OK;
        }
 
-       LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer );
-
-    if (mode & FCE_FILE_MODIFY) {
-        save_close_event(full_path_buffer);
-        return AFP_OK;
+    switch (event) {
+    case FCE_FILE_MODIFY:
+        save_close_event(path);
+        break;
+    default:
+        send_fce_event(path, event);
+        break;
     }
 
-    send_fce_event( full_path_buffer, mode );
-
     return AFP_OK;
 }
 
@@ -430,7 +409,7 @@ static void check_saved_close_events(int fmodwait)
 
     /* check if configured holdclose time has passed */
     if (last_close_event.time && ((last_close_event.time + fmodwait) < now)) {
-        LOG(log_debug, logtype_afpd, "check_saved_close_events: sending event: %s", last_close_event.path);
+        LOG(log_debug, logtype_fce, "check_saved_close_events: sending event: %s", last_close_event.path);
         /* yes, send event */
         send_fce_event(&last_close_event.path[0], FCE_FILE_MODIFY);
         last_close_event.path[0] = 0;
@@ -443,106 +422,13 @@ static void check_saved_close_events(int fmodwait)
 /*
  * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c)
  * */
-#ifndef FCE_TEST_MAIN
-
 void fce_pending_events(AFPObj *obj)
 {
-    vol_fce_tm_event();
+    if (!udp_sockets)
+        return;
     check_saved_close_events(obj->options.fce_fmodwait);
 }
 
-int fce_register_delete_file( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_DELETE)))
-        return ret;
-       
-    ret = register_fce( path->u_name, false, FCE_FILE_DELETE );
-
-    return ret;
-}
-int fce_register_delete_dir( char *name )
-{
-    int ret = AFP_OK;
-
-    if (name == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_DIR_DELETE)))
-        return ret;
-       
-    ret = register_fce( name, true, FCE_DIR_DELETE);
-
-    return ret;
-}
-
-int fce_register_new_dir( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_DIR_CREATE)))
-        return ret;
-
-    ret = register_fce( path->u_name, true, FCE_DIR_CREATE );
-
-    return ret;
-}
-
-
-int fce_register_new_file( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_CREATE)))
-        return ret;
-
-    ret = register_fce( path->u_name, false, FCE_FILE_CREATE );
-
-    return ret;
-}
-
-int fce_register_file_modification( struct ofork *ofork )
-{
-    int ret = AFP_OK;
-
-    if (ofork == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_MODIFY)))
-        return ret;
-
-    ret = register_fce(of_name(ofork), false, FCE_FILE_MODIFY );
-    
-    return ret;    
-}
-
-int fce_register_tm_size(const char *vol, size_t used)
-{
-    int ret = AFP_OK;
-
-    if (vol == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_TM_SIZE)))
-        return ret;
-
-    tm_used = used;             /* oh what a hack */
-    ret = register_fce(vol, false, FCE_TM_SIZE);
-
-    return ret;
-}
-#endif
-
 /*
  *
  * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times)
@@ -586,8 +472,6 @@ int fce_set_events(const char *events)
             fce_ev_enabled |= (1 << FCE_FILE_CREATE);
         } else if (strcmp(p, "dcre") == 0) {
             fce_ev_enabled |= (1 << FCE_DIR_CREATE);
-        } else if (strcmp(p, "tmsz") == 0) {
-            fce_ev_enabled |= (1 << FCE_TM_SIZE);
         }
     }
 
@@ -605,7 +489,7 @@ void shortsleep( unsigned int us )
 }
 int main( int argc, char*argv[] )
 {
-    int c,ret;
+    int c;
 
     char *port = FCE_DEFAULT_PORT_STRING;
     char *host = "localhost";
@@ -668,7 +552,7 @@ int main( int argc, char*argv[] )
         if (end_time && now >= end_time)
             break;
 
-        register_fce( path, 0, event_code );
+        fce_register(event_code, path, NULL, 0);
         ev_cnt++;
 
         
index 9ed185e53457868b7107b42ee4322caebdcea198..fb5e58e38971602b6de5317e41922e3db7d10461 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <stdbool.h>
 
+#include <atalk/fce_api.h>
+
 #define FCE_MAX_UDP_SOCKS 5     /* Allow a maximum of udp listeners for file change events */
 #define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */
 #define FCE_PACKET_VERSION  1
@@ -20,8 +22,7 @@
 #define FCE_COALESCE_DELETE (1 << 1)
 #define FCE_COALESCE_ALL    (FCE_COALESCE_CREATE | FCE_COALESCE_DELETE)
 
-struct udp_entry
-{
+struct udp_entry {
     int sock;
     char *addr;
     char *port;
@@ -30,12 +31,11 @@ struct udp_entry
     time_t next_try_on_error;      /* In case of error set next timestamp to retry */
 };
 
-struct fce_history
-{
-    unsigned char mode;
-       int is_dir;
-       char path[MAXPATHLEN + 1];
-       struct timeval tv;
+struct fce_history {
+    fce_ev_t       fce_h_event;
+       fce_obj_t      fce_h_type;
+       char           fce_h_path[MAXPATHLEN + 1];
+       struct timeval fce_h_tv;
 };
 
 struct fce_close_event {
@@ -45,7 +45,7 @@ struct fce_close_event {
 
 #define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN)
 
-bool fce_handle_coalescation( char *path, int is_dir, int mode );
+bool fce_handle_coalescation(int event, const char *path, fce_obj_t type);
 void fce_initialize_history();
 
 
index 07b59d73621555cf917eebbc4028ac347860cc37..69f6ea0dabfb9987c767eb6b8ff521bcdc3c1c9e 100644 (file)
@@ -54,9 +54,6 @@
 // ONLY USED IN THIS FILE
 #include "fce_api_internal.h"
 
-#define FCE_TRUE 1
-#define FCE_FALSE 0
-
 /* We store our connection data here */
 static uint32_t coalesce = 0;
 static struct fce_history fce_history_list[FCE_HISTORY_LEN];
@@ -92,7 +89,7 @@ void fce_initialize_history()
        }
 }
 
-bool fce_handle_coalescation( char *path, int is_dir, int mode )
+bool fce_handle_coalescation(int event, const char *path, fce_obj_t type)
 {
        /* These two are used to eval our next index in history */
        /* the history is unsorted, speed should not be a problem, length is 10 */
@@ -104,7 +101,7 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                return false;
 
        /* After a file creation *ALWAYS* a file modification is produced */
-       if ((mode == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
+       if ((event == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
         return true;
 
        /* get timestamp */
@@ -115,7 +112,7 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                struct fce_history *fh = &fce_history_list[i];
 
                /* Not inited ? */
-               if (fh->tv.tv_sec == 0) {
+               if (fh->fce_h_tv.tv_sec == 0) {
                        /* we can use it for new elements */
                        oldest_entry = 0;
                        oldest_entry_idx = i;
@@ -123,9 +120,9 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                }
 
                /* Too old ? */
-               if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS) {
+               if (get_ms_difftime(&fh->fce_h_tv, &tv ) > MAX_COALESCE_TIME_MS) {
                        /* Invalidate entry */
-                       fh->tv.tv_sec = 0;
+                       fh->fce_h_tv.tv_sec = 0;
                        oldest_entry = 0;
                        oldest_entry_idx = i;                   
                        continue;
@@ -133,33 +130,33 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
 
 
                /* If we find a parent dir wich was created we are done */
-               if ((coalesce & FCE_COALESCE_CREATE) && (fh->mode == FCE_DIR_CREATE)) {
+               if ((coalesce & FCE_COALESCE_CREATE) && (fh->fce_h_event == FCE_DIR_CREATE)) {
                        /* Parent dir ? */
-                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
                                return true;
                }
 
                /* If we find a parent dir we should be DELETED we are done */
                if ((coalesce & FCE_COALESCE_DELETE)
-            && fh->is_dir
-            && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {
+            && fh->fce_h_type
+            && (event == FCE_FILE_DELETE || event == FCE_DIR_DELETE)) {
                        /* Parent dir ? */
-                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
                                return true;
                }
 
                /* Detect oldest entry for next new entry */
-               if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {
-                       oldest_entry = fh->tv.tv_sec;
+               if (oldest_entry_idx == -1 || fh->fce_h_tv.tv_sec < oldest_entry) {
+                       oldest_entry = fh->fce_h_tv.tv_sec;
                        oldest_entry_idx = i;
                }
        }
 
        /* We have a new entry for the history, register it */
-       fce_history_list[oldest_entry_idx].tv = tv;
-       fce_history_list[oldest_entry_idx].mode = mode;
-       fce_history_list[oldest_entry_idx].is_dir = is_dir;
-       strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);
+       fce_history_list[oldest_entry_idx].fce_h_tv = tv;
+       fce_history_list[oldest_entry_idx].fce_h_event = event;
+       fce_history_list[oldest_entry_idx].fce_h_type = type;
+       strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN);
 
        /* we have to handle this event */
        return false;
index 8cd5f456947333a38372e90487aa78c7819eae70..9c78b1b2f3f4baaf1ade8e468ab65f584a42ece0 100644 (file)
@@ -25,6 +25,7 @@
 #include <atalk/globals.h>
 #include <atalk/fce_api.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/spotlight.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -77,6 +78,7 @@ static int default_type(void *finder)
 /* FIXME path : unix or mac name ? (for now it's unix name ) */
 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
 {
+    struct extmap       *em;
     void                *ad_finder = NULL;
     int                 chk_ext = 0;
 
@@ -85,9 +87,13 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
 
     if (ad_finder) {
         memcpy(data, ad_finder, ADEDLEN_FINDERI);
+        /* default type ? */
+        if (default_type(ad_finder)) 
+            chk_ext = 1;
     }
     else {
         memcpy(data, ufinderi, ADEDLEN_FINDERI);
+        chk_ext = 1;
         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
             uint16_t ashort;
             
@@ -96,7 +102,7 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
         }
     }
 
-    if (islink){
+    if (islink && !vol_syml_opt(vol)) {
         uint16_t linkflag;
         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
         linkflag |= htons(FINDERINFO_ISALIAS);
@@ -105,6 +111,12 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
         memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
     }
 
+    /** Only enter if no appledouble information and no finder information found. */
+    if (chk_ext && (em = getextmap( upath ))) {
+        memcpy(data, em->em_type, sizeof( em->em_type ));
+        memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
+    }
+
     return data;
 }
 
@@ -208,7 +220,10 @@ restart:
            catching moved files */
         adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
 
+        AFP_CNID_START("cnid_add");
            dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
+        AFP_CNID_DONE();
+
            /* Throw errors if cnid_add fails. */
            if (dbcnid == CNID_INVALID) {
             switch (errno) {
@@ -237,7 +252,7 @@ restart:
                     }
                     LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
                         vol->v_path);
-                    vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
+                    vol->v_cdb = cnid_open(vol, "tdb", flags);
                     if (vol->v_cdb) {
                         if (!(vol->v_flags & AFPVOL_TM)) {
                             vol->v_flags |= AFPVOL_RO;
@@ -324,6 +339,7 @@ int getmetadata(const AFPObj *obj,
                     || (bconchar(fullpath, '/') != BSTR_OK)
                     || (bcatcstr(fullpath, upath)) != BSTR_OK) {
                     LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+                    bdestroy(fullpath);
                     return AFPERR_MISC;
                 }
 
@@ -362,6 +378,7 @@ int getmetadata(const AFPObj *obj,
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
+            ashort &= ~htons(vol->v_ignattr);
 #if 0
             /* FIXME do we want a visual clue if the file is read only
              */
@@ -444,18 +461,23 @@ int getmetadata(const AFPObj *obj,
             data += sizeof( aint );
             break;
 
-        case FILPBIT_RFLEN :
-            if ( adp ) {
+        case FILPBIT_RFLEN: {
+            off_t rlen;
+            if (adp) {
                 if (adp->ad_rlen > 0xffffffff)
                     aint = 0xffffffff;
                 else
                     aint = htonl( adp->ad_rlen);
             } else {
-                aint = 0;
+                rlen = ad_reso_size(path->u_name, 0, NULL);
+                if (rlen > 0xffffffff)
+                    rlen = 0xffffffff;
+                aint = htonl(rlen);
             }
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
+        }
 
             /* Current client needs ProDOS info block for this file.
                Use simple heuristic and let the Mac "type" string tell
@@ -523,15 +545,18 @@ int getmetadata(const AFPObj *obj,
             data += sizeof( aint );
             break;
         case FILPBIT_EXTRFLEN:
-            aint = 0;
-            if (adp) 
+            if (adp) {
                 aint = htonl(adp->ad_rlen >> 32);
-            memcpy(data, &aint, sizeof( aint ));
-            data += sizeof( aint );
-            if (adp) 
+                memcpy(data, &aint, sizeof( aint ));
+                data += sizeof( aint );
                 aint = htonl(adp->ad_rlen);
-            memcpy(data, &aint, sizeof( aint ));
-            data += sizeof( aint );
+                memcpy(data, &aint, sizeof( aint ));
+                data += sizeof( aint );
+            } else {
+                int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
+                memcpy(data, &rlen, sizeof(rlen));
+                data += sizeof(rlen);
+            }
             break;
         case FILPBIT_UNIXPR :
             /* accessmode may change st_mode with ACLs */
@@ -594,7 +619,7 @@ int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct pat
     struct adouble     ad, *adp;
     int                 opened = 0;
     int rc;    
-    int flags;
+    int flags; /* uninitialized ok */
 
     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
 
@@ -630,7 +655,9 @@ int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct pat
         }
     }
     rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
-    ad_close(adp, ADFLAGS_HF | flags);
+
+    if (opened)
+        ad_close(adp, ADFLAGS_HF | flags);
 
     return( rc );
 }
@@ -675,7 +702,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     ad_init(&ad, vol);
     
     /* if upath is deleted we already in trouble anyway */
-    if ((of = of_findname(s_path))) {
+    if ((of = of_findname(vol, s_path))) {
         if (creatf)
             return AFPERR_BUSY;
         else
@@ -737,11 +764,10 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 createfile_iderr:
     ad_flush(&ad);
     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
-    fce_register_new_file(s_path);
+    fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
+    sl_index_file(path);
 
-createfile_done:
     curdir->d_offcnt++;
-
     setvoltime(obj, vol );
 
     return (retvalue);
@@ -828,7 +854,10 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
     uint16_t           bitmap = f_bitmap;
     uint32_t           cdate,bdate;
     u_char              finder_buf[32];
-    int symlinked = 0;
+    int symlinked = S_ISLNK(path->st.st_mode);
+    int fp;
+    ssize_t len;
+    char symbuf[MAXPATHLEN+1];
 
 #ifdef DEBUG
     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
@@ -870,29 +899,33 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
             break;
         case FILPBIT_FINFO :
             change_mdate = 1;
-            memcpy(finder_buf, buf, 32 );
-            if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
-                int fp;
-                ssize_t len;
-                int erc=1;
-                char buf[PATH_MAX+1];
-                if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
-                    if ((len = read(fp, buf, PATH_MAX+1))) {
-                        if (unlink(path->u_name) == 0) {
-                            buf[len] = 0;
-                            erc = symlink(buf, path->u_name);
-                            if (!erc)
-                                of_stat(path);
-                        }
-                    }
-                    close(fp);
+            if (memcmp(buf,"slnkrhap",8) == 0
+                && !(S_ISLNK(path->st.st_mode))
+                && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
+                /* request to turn this into a symlink */
+                if ((fp = open(path->u_name, O_RDONLY)) == -1) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                len = read(fp, symbuf, MAXPATHLEN);
+                close(fp);
+                if (!(len > 0)) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
                 }
-                if (erc != 0) {
-                    err=AFPERR_BITMAP;
+                if (unlink(path->u_name) != 0) {
+                    err = AFPERR_MISC;
                     goto setfilparam_done;
                 }
+                symbuf[len] = 0;
+                if (symlink(symbuf, path->u_name) != 0) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                of_stat(vol, path);
                 symlinked = 1;
             }
+            memcpy(finder_buf, buf, 32 );
             buf += 32;
             break;
         case FILPBIT_UNIXPR :
@@ -970,6 +1003,12 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
         isad = 0;
     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
         ad_setname(adp, path->m_name);
+        cnid_t id;
+        if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
+            LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
+            return AFPERR_MISC;
+        }
+        (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
     }
     
     bit = 0;
@@ -985,6 +1024,7 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
             ad_getattr(adp, &bshort);
             oshort = bshort;
             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+                ashort &= ~htons(vol->v_ignattr);
                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
             } else {
                 bshort &= ~ashort;
@@ -1002,6 +1042,17 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
             ad_setdate(adp, AD_DATE_BACKUP, bdate);
             break;
         case FILPBIT_FINFO :
+            if (default_type( ad_entry( adp, ADEID_FINDERI ))
+                    && ( 
+                     ((em = getextmap( path->m_name )) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+                     || ((em = getdefextmap()) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+            )) {
+                memcpy(finder_buf, ufinderi, 8 );
+            }
             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
             break;
         case FILPBIT_UNIXPR :
@@ -1064,7 +1115,7 @@ setfilparam_done:
  * adp         adouble struct of src file, if open, or & zeroed one
  *
  */
-int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
+int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
 {
     int                rc;
 
@@ -1089,7 +1140,7 @@ int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *n
                /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
                return AFPERR_OLOCK; /* little lie */
            }
-            if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
+            if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
                 /* on error copyfile delete dest */
                 return( rc );
             }
@@ -1323,7 +1374,7 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
         goto copy_exit;
     }
 
-    if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
+    if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
         retvalue = err;
         goto copy_exit;
     }
@@ -1341,8 +1392,9 @@ copy_exit:
  * because we are doing it elsewhere.
  * currently if newname is NULL then adp is NULL. 
  */
-int copyfile(const struct vol *s_vol,
-             const struct vol *d_vol, 
+int copyfile(struct vol *s_vol,
+             struct vol *d_vol, 
+             struct dir *d_dir, 
              int sfd,
              char *src,
              char *dst,
@@ -1364,13 +1416,17 @@ int copyfile(const struct vol *s_vol,
         adp = &ads;
     }
 
-    adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
+    adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
 
     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
         ret_err = errno;
         goto done;
     }
 
+    if (!AD_META_OPEN(adp))
+        /* no resource fork, don't create one for dst file */
+        adflags &= ~ADFLAGS_HF;
+
     if (!AD_RSRC_OPEN(adp))
         /* no resource fork, don't create one for dst file */
         adflags &= ~ADFLAGS_RF;
@@ -1403,12 +1459,25 @@ int copyfile(const struct vol *s_vol,
     if (err < 0)
        ret_err = errno;
 
-    if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
-        /* set the new name in the resource fork */
-        ad_copy_header(&add, adp);
-        ad_setname(&add, newname);
-        ad_flush( &add );
+    if (AD_META_OPEN(&add)) {
+        if (AD_META_OPEN(adp))
+            ad_copy_header(&add, adp);
+        ad_setname(&add, dst);
+        cnid_t id;
+        struct stat stdest;
+        if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
+            ret_err = errno;
+            goto error;
+        }
+        if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
+            ret_err = EINVAL;
+            goto error;
+        }
+        (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
+        ad_flush(&add);
     }
+
+error:
     ad_close( adp, adflags );
 
     if (ad_close( &add, adflags ) <0) {
@@ -1460,7 +1529,7 @@ done:
    WRITE lock on read only file.
 */
 
-static int check_attrib(struct adouble *adp)
+static int check_attrib(const struct vol *vol, struct adouble *adp)
 {
 uint16_t   bshort = 0;
 
@@ -1468,10 +1537,10 @@ uint16_t   bshort = 0;
     /*
      * Does kFPDeleteInhibitBit (bit 8) set?
      */
-       if ((bshort & htons(ATTRBIT_NODELETE))) {
+       if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (bshort & htons(ATTRBIT_NODELETE))) {
                return AFPERR_OLOCK;
        }
-    if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
+     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
        return AFPERR_BUSY;
        }
        return 0;
@@ -1496,7 +1565,7 @@ int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
          * ad_open would create a 0 byte resource fork
         */
         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
-            if ((err = check_attrib(&ad))) {
+            if ((err = check_attrib(vol, &ad))) {
                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
                return err;
             }
@@ -1525,7 +1594,7 @@ int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
         adp = &ad;
     }
 
-    if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
+    if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
         adflags |= ADFLAGS_RF;
         /* FIXME we have a pb here because we want to know if a file is open 
          * there's a 'priority inversion' if you can't open the ressource fork RW
@@ -1547,8 +1616,15 @@ int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
         err = AFPERR_BUSY;
     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
         cnid_t id;
-        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
-            cnid_delete(vol->v_cdb, id);
+        if (checkAttrib) {
+            AFP_CNID_START("cnid_get");
+            id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
+            AFP_CNID_DONE();
+            if (id) {
+                AFP_CNID_START("cnid_delete");
+                cnid_delete(vol->v_cdb, id);
+                AFP_CNID_DONE();
+            }
         }
     }
 
@@ -1586,7 +1662,7 @@ int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         return( AFPERR_PARAM);
     }
 
-    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+    if (vol->v_cdb == NULL || !(vol->v_cdb->cnid_db_flags & CNID_FLAG_PERSISTENT)) {
         return AFPERR_NOOP;
     }
 
@@ -1621,7 +1697,10 @@ int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
             return AFPERR_PARAM;
     }
     st = &s_path->st;
-    if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
+    AFP_CNID_START("cnid_lookup");
+    id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
+    AFP_CNID_DONE();
+    if (id) {
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFPERR_EXISTID;
@@ -1650,11 +1729,13 @@ static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
     cnid_t        did  = param->did;
     cnid_t       aint;
     
-    if ( lstat(de->d_name, &path.st) < 0 )
+    if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
         return 0;
     
     /* update or add to cnid */
+    AFP_CNID_START("cnid_add");
     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
+    AFP_CNID_DONE();
 
     return 0;
 }
@@ -1675,7 +1756,7 @@ reenumerate_id(struct vol *vol, char *name, struct dir *dir)
     }
     
     /* FIXME use of_statdir ? */
-    if (lstat(name, &st)) {
+    if (ostat(name, &st, vol_syml_opt(vol))) {
        return -1;
     }
 
@@ -1720,7 +1801,7 @@ int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
         return( AFPERR_PARAM);
     }
 
-    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+    if (vol->v_cdb == NULL || !(vol->v_cdb->cnid_db_flags & CNID_FLAG_PERSISTENT)) {
         return AFPERR_NOOP;
     }
 
@@ -1733,7 +1814,10 @@ int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
         return AFPERR_NOID;
     }
 retry:
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
+    AFP_CNID_DONE();
+    if (upath == NULL) {
         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
     }
 
@@ -1754,7 +1838,7 @@ retry:
 
     memset(&path, 0, sizeof(path));
     path.u_name = upath;
-    if ( of_stat(&path) < 0 ) {
+    if (of_stat(vol, &path) < 0 ) {
 #ifdef ESTALE
         /* with nfs and our working directory is deleted */
        if (errno == ESTALE) {
@@ -1825,7 +1909,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
         return( AFPERR_PARAM);
     }
 
-    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+    if (vol->v_cdb == NULL || !(vol->v_cdb->cnid_db_flags & CNID_FLAG_PERSISTENT)) {
         return AFPERR_NOOP;
     }
 
@@ -1836,7 +1920,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     ibuf += sizeof(id);
     fileid = id;
 
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
+    AFP_CNID_DONE();
+        if (upath == NULL) {
         return AFPERR_NOID;
     }
 
@@ -1849,7 +1936,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     err = AFP_OK;
-    if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
+    if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1869,7 +1956,9 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
         return AFPERR_BADTYPE;
 
 delete:
+    AFP_CNID_START("cnid_delete");
     if (cnid_delete(vol->v_cdb, fileid)) {
+        AFP_CNID_DONE();
         switch (errno) {
         case EROFS:
             return AFPERR_VLOCK;
@@ -1880,7 +1969,7 @@ delete:
             return AFPERR_PARAM;
         }
     }
-
+    AFP_CNID_DONE();
     return err;
 }
 
@@ -1912,7 +2001,7 @@ static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct p
         return NULL;
     }
     
-    if ((*of = of_findname(path))) {
+    if ((*of = of_findname(vol, path))) {
         /* reuse struct adouble so it won't break locks */
         adp = (*of)->of_ad;
     }
@@ -2008,7 +2097,9 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     
     /* look for the source cnid. if it doesn't exist, don't worry about
      * it. */
+    AFP_CNID_START("cnid_lookup");
     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
+    AFP_CNID_DONE();
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
         err = afp_errno; /* was AFPERR_PARAM */
@@ -2051,17 +2142,21 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
 
     /* look for destination id. */
     upath = path->u_name;
+    AFP_CNID_START("cnid_lookup");
     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
+    AFP_CNID_DONE();
 
     /* construct a temp name.
      * NOTE: the temp file will be in the dest file's directory. it
      * will also be inaccessible from AFP. */
     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
-    if (!mktemp(temp)) {
+    int fd;
+    if ((fd = mkstemp(temp)) == -1) {
         err = AFPERR_MISC;
         goto err_exchangefile;
     }
-    
+    close(fd);
+
     if (crossdev) {
         /* FIXME we need to close fork for copy, both s_of and d_of are null */
        ad_close(adsp, ADFLAGS_HF);
@@ -2069,32 +2164,39 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     /* now, quickly rename the file. we error if we can't. */
-    if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
         goto err_exchangefile;
     of_rename(vol, s_of, sdir, spath, curdir, temp);
 
     /* rename destination to source */
-    if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
         goto err_src_to_tmp;
     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
-    if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
         goto err_dest_to_src;
     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
 
-    /* id's need switching. src -> dest and dest -> src. 
+    /* 
+     * id's need switching. src -> dest and dest -> src. 
      * we need to re-stat() if it was a cross device copy.
-    */
-    if (sid)
+     */
+    if (sid) {
+        AFP_CNID_START("cnid_delete");        
         cnid_delete(vol->v_cdb, sid);
-    if (did)
+        AFP_CNID_DONE();
+    }
+    if (did) {
+        AFP_CNID_START("cnid_delete");
         cnid_delete(vol->v_cdb, did);
+        AFP_CNID_DONE();
+    }
 
-    if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
+    if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
        ||
-       (sid && ( (crossdev && lstat(p, &destst) < 0) ||
+        (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
     ) {
         switch (errno) {
@@ -2107,6 +2209,42 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         }
         goto err_temp_to_dest;
     }
+
+    if (AD_META_OPEN(adsp) || AD_META_OPEN(addp)) {
+        struct adouble adtmp;
+        bool opened_ads, opened_add;
+
+        ad_init(&adtmp, vol);
+        ad_init_offsets(&adtmp);
+
+        if (!AD_META_OPEN(adsp)) {
+            if (ad_open(adsp, p, ADFLAGS_HF) != 0)
+                return -1;
+            opened_ads = true;
+        }
+
+        if (!AD_META_OPEN(addp)) {
+            if (ad_open(addp, upath, ADFLAGS_HF) != 0)
+                return -1;
+            opened_add = true;
+        }
+
+        if (ad_copy_header(&adtmp, adsp) != 0)
+            goto err_temp_to_dest;
+        if (ad_copy_header(adsp, addp) != 0)
+            goto err_temp_to_dest;
+        if (ad_copy_header(addp, &adtmp) != 0)
+            goto err_temp_to_dest;
+        ad_flush(adsp);
+        ad_flush(addp);
+
+        if (opened_ads)
+            ad_close(adsp, ADFLAGS_HF);
+        if (opened_add)
+            ad_close(addp, ADFLAGS_HF);
+    }
+
+    /* FIXME: we should switch ressource fork too */
     
     /* here we need to reopen if crossdev */
     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
@@ -2154,17 +2292,17 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
      * properly. */
 err_temp_to_dest:
     /* rename dest to temp */
-    renamefile(vol, -1, upath, temp, temp, adsp);
+    renamefile(vol, curdir, -1, upath, temp, temp, adsp);
     of_rename(vol, s_of, curdir, upath, curdir, temp);
 
 err_dest_to_src:
     /* rename source back to dest */
-    renamefile(vol, -1, p, upath, path->m_name, addp);
+    renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
 
 err_src_to_tmp:
     /* rename temp back to source */
-    renamefile(vol, -1, temp, p, spath, adsp);
+    renamefile(vol, curdir, -1, temp, p, spath, adsp);
     of_rename(vol, s_of, curdir, temp, sdir, spath);
 
 err_exchangefile:
index aa9f3b4449a8ea386adf328e8b3f97d39cf759b6..1d0ff45ea16b4f3aeaf234b10d5f0c667179e1b6 100644 (file)
@@ -51,12 +51,6 @@ extern const u_char  ufinderi[];
 #define FILPBIT_EXTRFLEN 14
 #define FILPBIT_UNIXPR   15
 
-struct extmap {
-    char               *em_ext;
-    char               em_creator[ 4 ];
-    char               em_type[ 4 ];
-};
-
 #define kTextEncodingUTF8 0x08000103
 
 typedef enum {
@@ -111,8 +105,8 @@ extern struct extmap        *getdefextmap (void);
 extern int getfilparams (const AFPObj *obj, struct vol *, uint16_t, struct path *,
                          struct dir *, char *buf, size_t *, int);
 extern int setfilparams (const AFPObj *obj, struct vol *, struct path *, uint16_t, char *);
-extern int renamefile   (const struct vol *, int, char *, char *, char *, struct adouble *);
-extern int copyfile     (const struct vol *, const struct vol *, int, char *, char *, char *, struct adouble *);
+extern int renamefile   (struct vol *, struct dir *, int, char *, char *, char *, struct adouble *);
+extern int copyfile     (struct vol *, struct vol *, struct dir *, int, char *, char *, char *, struct adouble *);
 extern int deletefile   (const struct vol *, int, char *, int);
 
 extern int getmetadata  (const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path, 
index 56cbaccffff2d32d59697fb6acad396589284bca..f35edfc9ad6496e4cf3e00d949015b97b7964374 100644 (file)
@@ -26,6 +26,7 @@
 #include <atalk/globals.h>
 #include <atalk/fce_api.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/errchk.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -218,7 +219,7 @@ int check_name(const struct vol *vol, char *name)
     move and rename sdir:oldname to curdir:newname in volume vol
     special care is needed for lock   
 */
-static int moveandrename(const struct vol *vol,
+static int moveandrename(struct vol *vol,
                          struct dir *sdir,
                          int sdir_fd,
                          char *oldname,
@@ -248,7 +249,9 @@ static int moveandrename(const struct vol *vol,
     if (!isdir) {
         if ((oldunixname = strdup(mtoupath(vol, oldname, sdir->d_did, utf8_encoding(vol->v_obj)))) == NULL)
             return AFPERR_PARAM; /* can't convert */
+        AFP_CNID_START("cnid_get");
         id = cnid_get(vol->v_cdb, sdir->d_did, oldunixname, strlen(oldunixname));
+        AFP_CNID_DONE();
 
 #ifndef HAVE_ATFUNCS
         /* Need full path */
@@ -263,7 +266,7 @@ static int moveandrename(const struct vol *vol,
 #ifdef HAVE_ATFUNCS
         opened = of_findnameat(sdir_fd, &path);
 #else
-        opened = of_findname(&path);
+        opened = of_findname(vol, &path);
 #endif /* HAVE_ATFUNCS */
 
         if (opened) {
@@ -300,7 +303,7 @@ static int moveandrename(const struct vol *vol,
         ad_getattr(adp, &bshort);
         
         ad_close(adp, ADFLAGS_HF);
-        if ((bshort & htons(ATTRBIT_NORENAME))) {
+        if (!(vol->v_ignattr & ATTRBIT_NORENAME) && (bshort & htons(ATTRBIT_NORENAME))) {
             rc = AFPERR_OLOCK;
             goto exit;
         }
@@ -346,10 +349,10 @@ static int moveandrename(const struct vol *vol,
     if ( !isdir ) {
         path.st_valid = 1;
         path.st_errno = errno;
-        if (of_findname(&path)) {
+        if (of_findname(vol, &path)) {
             rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
         } else {
-            rc = renamefile(vol, sdir_fd, oldunixname, upath, newname, adp );
+            rc = renamefile(vol, curdir, sdir_fd, oldunixname, upath, newname, adp );
             if (rc == AFP_OK)
                 of_rename(vol, opened, sdir, oldname, curdir, newname);
         }
@@ -378,7 +381,9 @@ static int moveandrename(const struct vol *vol,
         }
 
         /* fix up the catalog entry */
+        AFP_CNID_START("cnid_update");
         cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
+        AFP_CNID_DONE();
     }
 
 exit:
@@ -464,6 +469,83 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     return( rc );
 }
 
+/* 
+ * Recursivley delete vetoed files and directories if the volume option is set
+ *
+ * @param vol   (r) volume handle
+ * @param upath (r) path of directory
+ *
+ * If the volume option delete veto files is set, this function recursively scans the
+ * directory "upath" for vetoed files and tries deletes these, the it will try to delete
+ * the directory. That may fail if the directory contains normal files that aren't vetoed.
+ *
+ * @returns 0 if the directory upath and all of its contents were deleted, otherwise -1.
+ *            If the volume option is not set it returns -1.
+ */
+int delete_vetoed_files(struct vol *vol, const char *upath, bool in_vetodir)
+{
+    EC_INIT;
+    DIR            *dp = NULL;
+    struct dirent  *de;
+    struct stat     sb;
+    int             pwd = -1;
+    bool            vetoed;
+
+    if (!(vol->v_flags & AFPVOL_DELVETO))
+        return -1;
+
+    EC_NEG1( pwd = open(".", O_RDONLY));
+    EC_ZERO( chdir(upath) );
+    EC_NULL( dp = opendir(".") );
+
+    while ((de = readdir(dp))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        if (stat(de->d_name, &sb) != 0) {
+            LOG(log_error, logtype_afpd, "delete_vetoed_files(\"%s/%s\"): %s",
+                upath, de->d_name, strerror(errno));
+                EC_EXIT_STATUS(AFPERR_DIRNEMPT);
+        }
+
+        if (in_vetodir || veto_file(vol->v_veto, de->d_name))
+            vetoed = true;
+        else
+            vetoed = false;
+
+        if (vetoed) {
+            LOG(log_debug, logtype_afpd, "delete_vetoed_files(\"%s/%s\"): deleting vetoed file",
+                upath, de->d_name);
+            switch (sb.st_mode & S_IFMT) {
+            case S_IFDIR:
+                /* recursion */
+                EC_ZERO( delete_vetoed_files(vol, de->d_name, vetoed));
+                break;
+            case S_IFREG:
+            case S_IFLNK:
+                EC_ZERO( netatalk_unlink(de->d_name) );
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    EC_ZERO_LOG( fchdir(pwd) );
+    pwd = -1;
+    EC_ZERO_LOG( rmdir(upath) );
+
+EC_CLEANUP:
+    if (dp)
+        closedir(dp);
+    if (pwd != -1) {
+        if (fchdir(pwd) != 0)
+            ret = -1;
+    }
+
+    EC_EXIT;
+}
+
 /* ------------------------------- */
 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
@@ -508,7 +590,9 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             if (rmdir(upath) != 0) {
                 switch (errno) {
                 case ENOTEMPTY:
-                    return AFPERR_DIRNEMPT;
+                    if (delete_vetoed_files(vol, upath, false) != 0)
+                        return AFPERR_DIRNEMPT;
+                    break;
                 case EACCES:
                     return AFPERR_ACCESS;
                 default:
@@ -521,11 +605,17 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
                 delcnid = deldir->d_did;
                 dir_remove(vol, deldir);
             }
-            if (delcnid == CNID_INVALID)
+            if (delcnid == CNID_INVALID) {
+                AFP_CNID_START("cnid_get");
                 delcnid = cnid_get(vol->v_cdb, curdir->d_did, upath, strlen(upath));
-            if (delcnid != CNID_INVALID)
+                AFP_CNID_DONE();
+            }
+            if (delcnid != CNID_INVALID) {
+                AFP_CNID_START("cnid_delete");
                 cnid_delete(vol->v_cdb, delcnid);
-            fce_register_delete_dir(upath);
+                AFP_CNID_DONE();
+            }
+            fce_register(FCE_DIR_DELETE, fullpathname(upath), NULL, fce_dir);
         } else {
             /* we have to cache this, the structs are lost in deletcurdir*/
             /* but we need the positive returncode to send our event */
@@ -533,10 +623,10 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             if ((dname = bstrcpy(curdir->d_u_name)) == NULL)
                 return AFPERR_MISC;
             if ((rc = deletecurdir(vol)) == AFP_OK)
-                fce_register_delete_dir(cfrombstr(dname));
+                fce_register(FCE_DIR_DELETE, fullpathname(cfrombstr(dname)), NULL, fce_dir);
             bdestroy(dname);
         }
-    } else if (of_findname(s_path)) {
+    } else if (of_findname(vol, s_path)) {
         rc = AFPERR_BUSY;
     } else {
         /* it's a file st_valid should always be true
@@ -547,7 +637,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             rc = AFPERR_NOOBJ;
         } else {
             if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK) {
-                               fce_register_delete_file( s_path );
+                               fce_register(FCE_FILE_DELETE, fullpathname(upath), NULL, fce_file);
                 if (vol->v_tm_used < s_path->st.st_size)
                     vol->v_tm_used = 0;
                 else 
@@ -582,8 +672,10 @@ char *absupath(const struct vol *vol, struct dir *dir, char *u)
         return NULL;
     if (bcatcstr(path, u) != BSTR_OK)
         return NULL;
-    if (path->slen > MAXPATHLEN)
+    if (path->slen > MAXPATHLEN) {
+        bdestroy(path);
         return NULL;
+    }
 
     LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
 
@@ -703,8 +795,8 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         if (!isdir && !vol_unix_priv(vol)) {
             int  admode = ad_mode("", 0777) | vol->v_fperm;
 
-            setfilmode(upath, admode, NULL, vol->v_umask);
-            vol->vfs->vfs_setfilmode(vol, upath, admode, NULL);
+            setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
+            vol->vfs->vfs_setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
         }
         setvoltime(obj, vol );
     }
index 02c9ac15b5382ed99c5ee4cf7a84166d4e85cfda..f72e5445b0f52b544dd6c2ddec7bfc40f0599981 100644 (file)
@@ -15,6 +15,7 @@ extern int            veto_file (const char *veto_str, const char *path);
 extern int             check_name (const struct vol *vol, char *name);
 
 extern int matchfile2dirperms (char *, struct vol *, int);
+extern int delete_vetoed_files(struct vol *vol, const char *upath, bool in_vetodir);
 
 /* FP functions */
 int afp_moveandrename (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 9c3ed7d2f1be64ce11c14b5814850195ca6c7742..4311655ec6597a19a55d9bff6cffbb061fbfa204 100644 (file)
@@ -22,6 +22,7 @@
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
+#include <atalk/bstrlib.h>
 #include <atalk/bstradd.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
@@ -65,10 +66,10 @@ static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap
     vol = ofork->of_vol;
     dir = dirlookup(vol, ofork->of_did);
 
-    if (NULL == (path.m_name = utompath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) {
+    if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) {
         return( AFPERR_MISC );
     }
-    path.u_name = of_name(ofork);
+    path.m_name = of_name(ofork);
     path.id = 0;
     st = &path.st;
     if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) |
@@ -152,27 +153,6 @@ static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int acc
     int denyreadset;
     int denywriteset;
 
-#ifdef HAVE_FSHARE_T
-    fshare_t shmd;
-
-    if (obj->options.flags & OPTION_SHARE_RESERV) {
-        shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0);
-        if (shmd.f_access == 0)
-            /* we must give an access mode, otherwise fcntl will complain */
-            shmd.f_access = F_RDACC;
-        shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0;
-        shmd.f_id = ofrefnum;
-
-        int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp);
-
-        if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) {
-            LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno));
-            errno = EACCES;
-            return -1;
-        }
-    }
-#endif
-
     if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) {
         return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum);
     }
@@ -232,6 +212,35 @@ static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int acc
         }
     }
 
+    /*
+     * Fix for Bug 560: put the Solaris share reservation after our locking stuff.
+     * Note that this still leaves room for a race condition where between placing our own
+     * locks above and putting the Solaris share move below another client puts a lock.
+     * We then end up with set locks from above and return with an error code, the proper
+     * fix requires a sane cleanup function for the error path in this function.
+     */
+
+#ifdef HAVE_FSHARE_T
+    fshare_t shmd;
+
+    if (obj->options.flags & OPTION_SHARE_RESERV) {
+        shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0);
+        if (shmd.f_access == 0)
+            /* we must give an access mode, otherwise fcntl will complain */
+            shmd.f_access = F_RDACC;
+        shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0;
+        shmd.f_id = ofrefnum;
+
+        int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp);
+
+        if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) {
+            LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno));
+            errno = EACCES;
+            return -1;
+        }
+    }
+#endif
+
     return 0;
 }
 
@@ -250,7 +259,6 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     struct stat     *st;
     uint16_t        bshort;
     struct path     *s_path;
-    struct stat xxx;
 
     ibuf++;
     fork = *ibuf++;
@@ -316,7 +324,7 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         }
     }
 
-    if ((opened = of_findname(s_path))) {
+    if ((opened = of_findname(vol, s_path))) {
         adsame = opened->of_ad;
     }
 
@@ -328,8 +336,6 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     } else {
         eid = ADEID_RFORK;
         adflags |= ADFLAGS_RF;
-        if (!(access & OPENACC_WR))
-            adflags |= ADFLAGS_NORF;
     }
 
     if (access & OPENACC_WR) {
@@ -388,6 +394,16 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
 
     if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
         ofork->of_flags |= AFPFORK_META;
+        if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) {
+            LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath);
+            cnid_t id;
+            if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
+                LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
+                goto openfork_err;
+            }
+            (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp);
+            ad_flush(ofork->of_ad);
+        }
     } else {
         switch (errno) {
         case EACCES:
@@ -492,6 +508,8 @@ openfork_err:
 int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
 {
     struct ofork    *ofork;
+    struct vol      *vol;
+    struct dir      *dir;
     off_t       size;
     uint16_t       ofrefnum, bitmap;
     int                 err;
@@ -514,6 +532,16 @@ int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, s
         return( AFPERR_PARAM );
     }
 
+    vol = ofork->of_vol;
+    if ((dir = dirlookup(vol, ofork->of_did)) == NULL) {
+        LOG(log_error, logtype_afpd, "%s: bad fork did",  of_name(ofork));
+        return AFPERR_MISC;
+    }
+    if (movecwd(vol, dir) != 0) {
+        LOG(log_error, logtype_afpd, "%s: bad fork directory", dir->d_fullpath);
+        return AFPERR_MISC;
+    }
+
     if (ofork->of_vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
@@ -575,7 +603,7 @@ int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, s
             goto afp_setfork_err;
         }
 
-        err = ad_rtruncate(ofork->of_ad, size);
+        err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size);
         if (st_size > size)
             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
         if (err < 0)
@@ -733,8 +761,6 @@ int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t
 static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
 {
     ssize_t cc;
-    int eof = 0;
-    char *p, *q;
 
     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
     if ( cc < 0 ) {
@@ -793,6 +819,8 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         goto afp_read_err;
     }
 
+    AFP_READ_START((long)reqcount);
+
     /* reqcount isn't always truthful. we need to deal with that. */
     size = ad_size(ofork->of_ad, eid);
 
@@ -800,7 +828,7 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")",
         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size);
 
-    if (offset > size) {
+    if (offset >= size) {
         err = AFPERR_EOF;
         goto afp_read_err;
     }
@@ -839,11 +867,13 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     }
 #endif
 
-    *rbuflen = MIN(reqcount, *rbuflen);
+    *rbuflen = MIN(reqcount, dsi->server_quantum);
 
-    err = read_file(ofork, eid, offset, rbuf, rbuflen);
-    if (err < 0)
+    cc = read_file(ofork, eid, offset, ibuf, rbuflen);
+    if (cc < 0) {
+        err = cc;
         goto afp_read_done;
+    }
 
     LOG(log_debug, logtype_afpd,
         "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
@@ -856,18 +886,22 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
      * we know that we're sending some data. if we fail, something
      * horrible happened.
      */
-    if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
+    if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0)
         goto afp_read_exit;
     *rbuflen = cc;
 
     while (*rbuflen > 0) {
-        cc = read_file(ofork, eid, offset, rbuf, rbuflen);
+        /*
+         * This loop isn't really entered anymore, we've already
+         * sent the whole requested block in dsi_readinit().
+         */
+        cc = read_file(ofork, eid, offset, ibuf, rbuflen);
         if (cc < 0)
             goto afp_read_exit;
 
         offset += *rbuflen;
         /* dsi_read() also returns buffer size of next allocation */
-        cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
+        cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */
         if (cc < 0)
             goto afp_read_exit;
         *rbuflen = cc;
@@ -885,6 +919,8 @@ afp_read_exit:
 afp_read_done:
     if (obj->options.flags & OPTION_AFP_READ_LOCK)
         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
+
+    AFP_READ_DONE();
     return err;
 
 afp_read_err:
@@ -1039,7 +1075,7 @@ int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, s
         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc");
 
     if (of_closefork(obj, ofork) < 0 ) {
-        LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
+        LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) );
         return( AFPERR_PARAM );
     }
 
@@ -1051,7 +1087,6 @@ static ssize_t write_file(struct ofork *ofork, int eid,
                           off_t offset, char *rbuf,
                           size_t rbuflen)
 {
-    char *p, *q;
     ssize_t cc;
 
     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
@@ -1090,7 +1125,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     uint16_t        ofrefnum;
     ssize_t         cc;
     DSI             *dsi = obj->dsi;
-    char            *rcvbuf = dsi->commands;
+    char            *rcvbuf = (char *)dsi->commands;
     size_t          rcvbuflen = dsi->server_quantum;
 
     /* figure out parameters */
@@ -1156,6 +1191,8 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    AFP_WRITE_START((long)reqcount);
+
     saveoff = offset;
     if (obj->options.flags & OPTION_AFP_READ_LOCK) {
         if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) {
@@ -1165,41 +1202,54 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     }
 
     /* find out what we have already */
-    cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen);
-
-    if (!cc || (cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
-        dsi_writeflush(dsi);
-        *rbuflen = 0;
-        if (obj->options.flags & OPTION_AFP_READ_LOCK)
-            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
-        return cc;
+    if ((cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen)) > 0) {
+        ssize_t written;
+        if ((written = write_file(ofork, eid, offset, rcvbuf, cc)) != cc) {
+            dsi_writeflush(dsi);
+            *rbuflen = 0;
+            if (obj->options.flags & OPTION_AFP_READ_LOCK)
+                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
+            if (written > 0)
+                /* It's used for the read size and as error code in write_file(), ugh */
+                written = AFPERR_MISC;
+            return written;
+        }
     }
 
     offset += cc;
 
-#if 0 /*def HAVE_SENDFILE_WRITE*/
-    if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize)) < 0) {
-        switch (errno) {
-        case EDQUOT:
-        case EFBIG:
-        case ENOSPC:
-            cc = AFPERR_DFULL;
-            break;
-        default:
-            LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
-            goto afp_write_loop;
+#ifdef WITH_RECVFILE
+    if (obj->options.flags & OPTION_RECVFILE) {
+        LOG(log_maxdebug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu32 ")",
+            ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, dsi->datasize);
+
+        if ((cc = ad_recvfile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize, obj->options.splice_size)) < dsi->datasize) {
+            switch (errno) {
+            case EDQUOT:
+            case EFBIG:
+            case ENOSPC:
+                cc = AFPERR_DFULL;
+                dsi_writeflush(dsi);
+                break;
+            case ENOSYS:
+                goto afp_write_loop;
+            default:
+                /* Low level error, can't do much to back up */
+                cc = AFPERR_MISC;
+                LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno));
+            }
+            *rbuflen = 0;
+            if (obj->options.flags & OPTION_AFP_READ_LOCK)
+                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
+            return cc;
         }
-        dsi_writeflush(dsi);
-        *rbuflen = 0;
-        if (obj->options.flags & OPTION_AFP_READ_LOCK)
-            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
-        return cc;
-    }
 
-    offset += cc;
-    goto afp_write_done;
-#endif /* 0, was HAVE_SENDFILE_WRITE */
+        offset += cc;
+        goto afp_write_done;
+    }
+#endif
 
+afp_write_loop:
     /* loop until everything gets written. currently
      * dsi_write handles the end case by itself. */
     while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) {
@@ -1218,6 +1268,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         offset += cc;
     }
 
+afp_write_done:
     if (obj->options.flags & OPTION_AFP_READ_LOCK)
         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
@@ -1230,6 +1281,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
 
     *rbuflen = set_off_t (offset, rbuf, is64);
+    AFP_WRITE_DONE();
     return( AFP_OK );
 
 afp_write_err:
index d7120e4778b163927bd17b31c2f5ffd4872aa3a9..5b63601f992fe139e2b67bf4d724199a6c873c52 100644 (file)
@@ -59,13 +59,13 @@ extern struct ofork *of_alloc    (struct vol *, struct dir *,
                                                       struct stat *);
 extern void         of_dealloc   (struct ofork *);
 extern struct ofork *of_find     (const uint16_t);
-extern struct ofork *of_findname (struct path *);
+extern struct ofork *of_findname (const struct vol *vol, struct path *);
 extern int          of_rename    (const struct vol *,
                                           struct ofork *,
                                           struct dir *, const char *,
                                           struct dir *, const char *);
 extern int          of_flush     (const struct vol *);
-extern int          of_stat      (struct path *);
+extern int          of_stat      (const struct vol *vol, struct path *);
 extern int          of_statdir   (struct vol *vol, struct path *);
 extern int          of_closefork (const AFPObj *obj, struct ofork *ofork);
 extern void         of_closevol  (const AFPObj *obj, const struct vol *vol);
diff --git a/etc/afpd/gettok.c b/etc/afpd/gettok.c
deleted file mode 100644 (file)
index fd48db6..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * $Id: gettok.c,v 1.6 2009-10-13 22:55:37 didg Exp $
- *
- * Copyright (c) 1990,1994 Regents of The University of Michigan.
- * All Rights Reserved.  See COPYRIGHT.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <sys/param.h>
-#include <string.h>
-#include <ctype.h>
-#include <pwd.h>
-
-#include <atalk/globals.h>
-
-static char    *l_curr;
-static char    *l_end;
-
-void initline( int len, char *line)
-{
-    l_curr = line;
-    l_end = line + len;
-}
-
-#define ST_QUOTE       0
-#define ST_WORD                1
-#define ST_BEGIN       2
-
-int
-parseline(int len, char *token)
-{
-    char       *p, *e;
-    int                state;
-
-    state = ST_BEGIN;
-    p = token;
-    e = token + len;
-
-    for (;;) {
-        if ( l_curr > l_end ) {                        /* end of line */
-            *token = '\0';
-            return( -1 );
-        }
-
-        switch ( *l_curr ) {
-        case '"' :
-            if ( state == ST_QUOTE ) {
-                state = ST_WORD;
-            } else {
-                state = ST_QUOTE;
-            }
-            break;
-
-        case '\0' :
-        case '\t' :
-        case '\n' :
-        case ' ' :
-            if ( state == ST_WORD ) {
-                *p = '\0';
-                return( p - token );
-            }
-            if ( state != ST_QUOTE ) {
-                break;
-            }
-            /* FALL THROUGH */
-
-        default :
-            if ( state == ST_BEGIN ) {
-                state = ST_WORD;
-            }
-            if ( p > e ) {                     /* end of token */
-                *token = '\0';
-                return( -1 );
-            }
-            *p++ = *l_curr;
-            break;
-        }
-
-        l_curr++;
-    }
-}
-
-#ifdef notdef
-void parseline(char *token, char *user)
-{
-    char               *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
-    struct passwd      *pwent;
-    int                        quoted = 0;
-
-    while ( isspace( *p )) {
-        p++;
-    }
-
-    /*
-     * If we've reached the end of the line, or a comment,
-     * don't return any more tokens.
-     */
-    if ( *p == '\0' || *p == '#' ) {
-        *token = '\0';
-        return;
-    }
-
-    if ( *p == '"' ) {
-        p++;
-        quoted = 1;
-    }
-    while ( *p != '\0' && ( quoted || !isspace( *p ))) {
-        if ( *p == '"' ) {
-            if ( quoted ) {
-                *t = '\0';
-                break;
-            }
-            quoted = 1;
-            p++;
-        } else {
-            *t++ = *p++;
-        }
-    }
-    pos = p;
-    *t = '\0';
-
-    /*
-     * We got to the end of the line without closing an open quote
-     */
-    if ( *p == '\0' && quoted ) {
-        *token = '\0';
-        return;
-    }
-
-    t = token;
-    if ( *t == '~' ) {
-        t++;
-        if ( *t == '\0' || *t == '/' ) {
-            u = user;
-            if ( *t == '/' ) {
-                t++;
-            }
-        } else {
-            u = t;
-            if (( q = strchr( t, '/' )) == NULL ) {
-                t = "";
-            } else {
-                *q = '\0';
-                t = q + 1;
-            }
-        }
-        if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
-            *token = '\0';
-            return;
-        }
-        strcpy( buf, pwent->pw_dir );
-        if ( *t != '\0' ) {
-            strcat( buf, "/" );
-            strcat( buf, t );
-        }
-        strcpy( token, buf );
-    }
-    return;
-}
-#endif /* notdef */
index b7471ccb19ec821b7b7a6559272b02ec6736c985..a7c624d48b4c8603ceb9aca2ed046001d89b9da7 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp $
  * $Name:  $
  */
 #define NDEBUG
@@ -26,7 +25,6 @@
 #include "hash.h"
 
 #ifdef KAZLIB_RCSID
-static const char rcsid[] = "$Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp $";
 #endif
 
 #define INIT_BITS   6
@@ -58,7 +56,6 @@ static const char rcsid[] = "$Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp
 static hnode_t *hnode_alloc(void *context);
 static void hnode_free(hnode_t *node, void *context);
 static hash_val_t hash_fun_default(const void *key);
-static hash_val_t hash_fun2(const void *key);
 static int hash_comp_default(const void *key1, const void *key2);
 
 int hash_val_t_bit;
@@ -850,6 +847,13 @@ static hash_val_t hash_fun_default(const void *key)
                       +(uint32_t)(((const uint8_t *)(d))[0]) )
 #endif
 
+static int hash_comp_default(const void *key1, const void *key2)
+{
+    return strcmp(key1, key2);
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
 static hash_val_t hash_fun2(const void *key)
 {
     int len, rem;
@@ -897,13 +901,6 @@ static hash_val_t hash_fun2(const void *key)
     return hash;
 }
 
-static int hash_comp_default(const void *key1, const void *key2)
-{
-    return strcmp(key1, key2);
-}
-
-#ifdef KAZLIB_TEST_MAIN
-
 #include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
@@ -985,8 +982,10 @@ int main(void)
         "s                      switch to non-functioning allocator\n"
         "q                      quit";
 
-    if (!h)
+    if (!h) {
         puts("hash_create failed");
+        return 1;
+    }
 
     for (;;) {
         if (prompt)
@@ -1015,6 +1014,7 @@ int main(void)
                 puts("out of memory");
                 free((void *) key);
                 free(val);
+                break;
             }
 
             if (!hash_alloc_insert(h, key, val)) {
index dcac14b6bd40a7f4ae8ada703b2244721d5d5261..886344eb13e3453f26f28b23f48856b2c650e8a4 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
  * $Name:  $
  */
 
index 1c5c4c425e4359e96e3ceea8539d574cbb52ef2c..9bea44a9d9408d9c6b1eff9acc62279f75d8952c 100644 (file)
@@ -38,6 +38,7 @@
 #include "fork.h"
 #include "uam_auth.h"
 #include "afp_zeroconf.h"
+#include "afpstats.h"
 
 #define AFP_LISTENERS 32
 #define FDSET_SAFETY  5
@@ -45,7 +46,7 @@
 unsigned char nologin = 0;
 
 static AFPObj obj;
-static server_child *server_children;
+static server_child_t *server_children;
 static sig_atomic_t reloadconfig = 0;
 static sig_atomic_t gotsigchld = 0;
 
@@ -54,9 +55,8 @@ static struct pollfd *fdset;
 static struct polldata *polldata;
 static int fdset_size;          /* current allocated size */
 static int fdset_used;          /* number of used elements */
-static int disasociated_ipc_fd; /* disasociated sessions uses this fd for IPC */
 
-static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child *server_children);
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children);
 
 static void afp_exit(int ret)
 {
@@ -81,16 +81,6 @@ static void fd_set_listening_sockets(const AFPObj *config)
                      LISTEN_FD,
                      dsi);
     }
-
-    if (config->options.flags & OPTION_KEEPSESSIONS)
-        fdset_add_fd(config->options.connections + AFP_LISTENERS + FDSET_SAFETY,
-                     &fdset,
-                     &polldata,
-                     &fdset_used,
-                     &fdset_size,
-                     disasociated_ipc_fd,
-                     DISASOCIATED_IPC_FD,
-                     NULL);
 }
  
 static void fd_reset_listening_sockets(const AFPObj *config)
@@ -100,9 +90,6 @@ static void fd_reset_listening_sockets(const AFPObj *config)
     for (dsi = config->dsi; dsi; dsi = dsi->next) {
         fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, dsi->serversock);
     }
-
-    if (config->options.flags & OPTION_KEEPSESSIONS)
-        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd);
 }
 
 /* ------------------ */
@@ -112,22 +99,9 @@ static void afp_goaway(int sig)
 
     case SIGTERM:
     case SIGQUIT:
-        switch (sig) {
-        case SIGTERM:
-            LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
-            break;
-        case SIGQUIT:
-            if (obj.options.flags & OPTION_KEEPSESSIONS) {
-                LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT, NOT disconnecting clients");
-            } else {
-                LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT");
-                sig = SIGTERM;
-            }
-            break;
-        }
+        LOG(log_note, logtype_afpd, "AFP Server shutting down");
         if (server_children)
-            server_child_kill(server_children, CHILD_DSIFORK, sig);
-
+            server_child_kill(server_children, SIGTERM);
         _exit(0);
         break;
 
@@ -137,7 +111,7 @@ static void afp_goaway(int sig)
         LOG(log_info, logtype_afpd, "disallowing logins");        
 
         if (server_children)
-            server_child_kill(server_children, CHILD_DSIFORK, sig);
+            server_child_kill(server_children, sig);
         break;
 
     case SIGHUP :
@@ -167,13 +141,6 @@ static void child_handler(void)
 #endif /* ! WAIT_ANY */
 
     while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
-        for (i = 0; i < server_children->nforks; i++) {
-            if ((fd = server_child_remove(server_children, i, pid)) != -1) {
-                fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);        
-                break;
-            }
-        }
-
         if (WIFEXITED(status)) {
             if (WEXITSTATUS(status))
                 LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
@@ -185,6 +152,12 @@ static void child_handler(void)
             else
                 LOG(log_info, logtype_afpd, "child[%d]: died", pid);
         }
+
+        fd = server_child_remove(server_children, pid);
+        if (fd == -1) {
+            continue;
+        }
+        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);
     }
 }
 
@@ -210,8 +183,6 @@ static int setlimits(void)
 
 int main(int ac, char **av)
 {
-    fd_set              rfds;
-    void                *ipc;
     struct sigaction   sv;
     sigset_t            sigs;
     int                 ret;
@@ -234,7 +205,7 @@ int main(int ac, char **av)
     /* install child handler for asp and dsi. we do this before afp_goaway
      * as afp_goaway references stuff from here. 
      * XXX: this should really be setup after the initial connections. */
-    if (!(server_children = server_child_alloc(obj.options.connections, CHILD_NFORKS))) {
+    if (!(server_children = server_child_alloc(obj.options.connections))) {
         LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
         afp_exit(EXITERR_SYS);
     }
@@ -253,6 +224,13 @@ int main(int ac, char **av)
         afp_exit(EXITERR_SYS);
     }
 #endif
+
+    sv.sa_handler = SIG_IGN;
+    sigemptyset( &sv.sa_mask );
+    if (sigaction(SIGPIPE, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+        afp_exit(EXITERR_SYS);
+    }
     
     sv.sa_handler = afp_goaway; /* handler for all sigs */
 
@@ -335,6 +313,11 @@ int main(int ac, char **av)
     sigaddset(&sigs, SIGCHLD);
 
     pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+#ifdef HAVE_DBUS_GLIB
+    /* Run dbus AFP statics thread */
+    if (obj.options.flags & OPTION_DBUS_AFPSTATS)
+        (void)afpstats_init(server_children);
+#endif
     if (configinit(&obj) != 0) {
         LOG(log_error, logtype_afpd, "main: no servers configured");
         afp_exit(EXITERR_CONF);
@@ -343,14 +326,8 @@ int main(int ac, char **av)
 
     /* Initialize */
     cnid_init();
-    
-    /* watch atp, dsi sockets and ipc parent/child file descriptor. */
-
-    if (obj.options.flags & OPTION_KEEPSESSIONS) {
-        LOG(log_note, logtype_afpd, "Activating continuous service");
-        disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
-    }
 
+    /* watch atp, dsi sockets and ipc parent/child file descriptor. */
     fd_set_listening_sockets(&obj);
 
     /* set limits */
@@ -382,12 +359,17 @@ int main(int ac, char **av)
 
         if (reloadconfig) {
             nologin++;
-            auth_unload();
+
             fd_reset_listening_sockets(&obj);
 
             LOG(log_info, logtype_afpd, "re-reading configuration file");
 
             configfree(&obj, NULL);
+            afp_config_free(&obj);
+
+            if (afp_config_parse(&obj, "afpd") != 0)
+                afp_exit(EXITERR_CONF);
+
             if (configinit(&obj) != 0) {
                 LOG(log_error, logtype_afpd, "config re-read: no servers configured");
                 afp_exit(EXITERR_CONF);
@@ -416,14 +398,14 @@ int main(int ac, char **av)
                 switch (polldata[i].fdtype) {
 
                 case LISTEN_FD:
-                    if (child = dsi_start(&obj, (DSI *)polldata[i].data, server_children)) {
+                    if ((child = dsi_start(&obj, (DSI *)polldata[i].data, server_children))) {
                         /* Add IPC fd to select fd set */
                         fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
                                      &fdset,
                                      &polldata,
                                      &fdset_used,
                                      &fdset_size,
-                                     child->ipc_fd,
+                                     child->afpch_ipc_fd,
                                      IPC_FD,
                                      child);
                     }
@@ -431,46 +413,13 @@ int main(int ac, char **av)
 
                 case IPC_FD:
                     child = (afp_child_t *)polldata[i].data;
-                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
-
-                    if (ipc_server_read(server_children, child->ipc_fd) != 0) {
-                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fd);
-                        close(child->ipc_fd);
-                        child->ipc_fd = -1;
-                        if ((obj.options.flags & OPTION_KEEPSESSIONS) && child->disasociated) {
-                            LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
-                            server_child_remove(server_children, CHILD_DSIFORK, child->pid);
-                        }
-                    }
-                    break;
-
-                case DISASOCIATED_IPC_FD:
-                    LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
-                    if ((recon_ipc_fd = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
-                        LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
-                        break;
-                    }
-                    if (readt(recon_ipc_fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
-                        LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
-                        close(recon_ipc_fd);
-                        break;
-                    }
-                    LOG(log_note, logtype_afpd, "main: IPC reconnect from pid [%u]", pid);
+                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->afpch_pid);
 
-                    if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, recon_ipc_fd)) == NULL) {
-                        LOG(log_error, logtype_afpd, "main: server_child_add");
-                        close(recon_ipc_fd);
-                        break;
+                    if (ipc_server_read(server_children, child->afpch_ipc_fd) != 0) {
+                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->afpch_ipc_fd);
+                        close(child->afpch_ipc_fd);
+                        child->afpch_ipc_fd = -1;
                     }
-                    child->disasociated = 1;
-                    fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
-                                 &fdset,
-                                 &polldata,
-                                 &fdset_used,
-                                 &fdset_size,
-                                 recon_ipc_fd,
-                                 IPC_FD,
-                                 child);
                     break;
 
                 default:
@@ -484,7 +433,7 @@ int main(int ac, char **av)
     return 0;
 }
 
-static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child *server_children)
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children)
 {
     afp_child_t *child = NULL;
 
index dce7e61681ac8e646ad4ed5ef810d39dacade824..18d6fcd91a227526a728f4b2bec9d6a83d7b4142 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: mangle.h,v 1.7 2009-10-13 22:55:37 didg Exp $
  *
  */
 
index 3c85f6a3a505c7fe9ae4c5ade65a5c44f997e708..5bd965bd2a55fd569942f550173b80a44e27c1a5 100644 (file)
 static char servermesg[MAXPATHLEN] = "";
 static char localized_message[MAXPATHLEN] = "";
 
-void setmessage(const char *message)
+/*!
+ * Copy AFP message to message buffer
+ * @param message (r) message to send
+ * @returns 0 if this message is being set the first time, return 1 if the preceeding
+ *          message was the same
+ */
+int setmessage(const char *message)
 {
+    if (strncmp(message, servermesg, MAXMESGSIZE) == 0)
+        return 1;
     strlcpy(servermesg, message, MAXMESGSIZE);
+    return 0;
 }
 
 void readmessage(AFPObj *obj)
@@ -177,6 +186,6 @@ int afp_getsrvrmesg(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
        *rbuflen += 1;
     }
     *rbuflen += outlen;
-    *message = 0;
+//    *message = 0;
     return AFP_OK;
 }
index b250c16633991d0a6a36a365f1cd48c585220bdb..50440ab42b6d2f38677dad27c86a88ae8299b218 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nfsquota.c,v 1.13 2009-10-13 22:55:37 didg Exp $
  *
  * parts of this are lifted from the bsd quota program and are
  * therefore under the following copyright:
index 3d690079ba84f7136575afa593e79ad2a3c6425a..9d389bb2cb9345960bdcfb7a6cc2630e3bc2e391 100644 (file)
 #include <atalk/bstradd.h>
 #include <atalk/globals.h>
 #include <atalk/fce_api.h>
+#include <atalk/ea.h>
 
 #include "volume.h"
 #include "directory.h"
 #include "fork.h"
+#include "desktop.h"
 
 /* we need to have a hashed list of oforks (by dev inode) */
 #define OFORK_HASHSIZE  64
@@ -228,14 +230,14 @@ struct ofork *of_find(const uint16_t ofrefnum )
 }
 
 /* -------------------------- */
-int of_stat(struct path *path)
+int of_stat(const struct vol *vol, struct path *path)
 {
     int ret;
 
     path->st_errno = 0;
     path->st_valid = 1;
 
-    if ((ret = lstat(path->u_name, &path->st)) < 0) {
+    if ((ret = ostat(path->u_name, &path->st, vol_syml_opt(vol))) < 0) {
         LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
             cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
        path->st_errno = errno;
@@ -274,7 +276,7 @@ int of_statdir(struct vol *vol, struct path *path)
 
     if (*path->m_name) {
         /* not curdir */
-        return of_stat (path);
+        return of_stat(vol, path);
     }
     path->st_errno = 0;
     path->st_valid = 1;
@@ -286,7 +288,7 @@ int of_statdir(struct vol *vol, struct path *path)
 
     LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
 
-    if (!(ret = lstat(pathname, &path->st)))
+    if (!(ret = ostat(pathname, &path->st, vol_syml_opt(vol))))
         return 0;
 
     path->st_errno = errno;
@@ -297,7 +299,7 @@ int of_statdir(struct vol *vol, struct path *path)
            return -1;
        path->st_errno = 0;
 
-       if ((ret = lstat(cfrombstr(path->d_dir->d_u_name), &path->st)) < 0) 
+       if ((ret = ostat(cfrombstr(path->d_dir->d_u_name), &path->st, vol_syml_opt(vol))) < 0) 
            path->st_errno = errno;
     }
 
@@ -305,13 +307,13 @@ int of_statdir(struct vol *vol, struct path *path)
 }
 
 /* -------------------------- */
-struct ofork *of_findname(struct path *path)
+struct ofork *of_findname(const struct vol *vol, struct path *path)
 {
     struct ofork *of;
     struct file_key key;
 
     if (!path->st_valid) {
-        of_stat(path);
+        of_stat(vol, path);
     }
 
     if (path->st_errno)
@@ -389,6 +391,8 @@ int of_closefork(const AFPObj *obj, struct ofork *ofork)
     struct timeval      tv;
     int         adflags = 0;
     int                 ret;
+    struct dir *dir;
+    bstring forkpath = NULL;
 
     adflags = 0;
     if (ofork->of_flags & AFPFORK_DATA)
@@ -405,9 +409,18 @@ int of_closefork(const AFPObj *obj, struct ofork *ofork)
         }
     }
 
+    dir = dirlookup(ofork->of_vol, ofork->of_did);
+    if (dir == NULL) {
+        LOG(log_debug, logtype_afpd, "dirlookup failed for %ju", (uintmax_t)ofork->of_did);
+    }
+
+    if (dir) {
+        forkpath = bformat("%s/%s", bdata(dir->d_fullpath), of_name(ofork));
+    }
+
     /* Somone has used write_fork, we assume file was changed, register it to file change event api */
-    if (ofork->of_flags & AFPFORK_MODIFIED) {
-        fce_register_file_modification(ofork);
+    if ((ofork->of_flags & AFPFORK_MODIFIED) && (forkpath)) {
+        fce_register(FCE_FILE_MODIFY, bdata(forkpath), NULL, fce_file);
     }
 
     ad_unlock(ofork->of_ad, ofork->of_refnum, ofork->of_flags & AFPFORK_ERROR ? 0 : 1);
@@ -424,12 +437,46 @@ int of_closefork(const AFPObj *obj, struct ofork *ofork)
 #endif
 
     ret = 0;
+
+    /*
+     * Check for 0 byte size resource forks, delete them.
+     * Here's the deal:
+     * (1) the size must be 0
+     * (2) the fork must refer to a resource fork
+     * (3) the refcount must be 1 which means this fork has the last
+     *     reference to the adouble struct and the subsequent
+     *     ad_close() will close the assoiciated fd.
+     * (4) nobody else has the resource fork open
+     *
+     * We only do this for ._ AppleDouble resource forks, not for
+     * xattr resource forks, because the test-suite then fails several
+     * tests on Solaris, the reason for that still needs to be
+     * determined.
+     */
+    if ((ofork->of_ad->ad_rlen == 0)
+        && (ofork->of_flags & AFPFORK_RSRC)
+        && (ofork->of_ad->ad_rfp->adf_refcount == 1)
+        && (ad_openforks(ofork->of_ad, ATTRBIT_DOPEN) == 0)) {
+
+#ifndef HAVE_EAFD
+        (void)unlink(ofork->of_ad->ad_ops->ad_path(
+                         mtoupath(ofork->of_vol,
+                                  of_name(ofork),
+                                  ofork->of_did,
+                                  utf8_encoding(obj)),
+                         0));
+#endif
+    }
+
     if ( ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD) < 0 ) {
         ret = -1;
     }
 
     of_dealloc(ofork);
 
+    if (forkpath)
+        bdestroy(forkpath);
+
     return ret;
 }
 
@@ -441,7 +488,7 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *
     struct ofork        *of;
     struct adouble      *adp;
 
-    if ((of = of_findname(path))) {
+    if ((of = of_findname(vol, path))) {
         adp = of->of_ad;
     } else {
         ad_init(ad, vol);
index 6903a21610ab421bbd3560569ae0e3cb30131d45..bab14e1345baf9a9f8b9447518ab1233b7f7650b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: quota.c,v 1.35 2010-04-03 07:11:35 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -26,6 +25,7 @@
 #include <atalk/afp.h>
 #include <atalk/compat.h>
 #include <atalk/unix.h>
+#include <atalk/util.h>
 
 #include "auth.h"
 #include "volume.h"
@@ -370,7 +370,7 @@ mountp( char *file, int *nfs)
     dev_t                      devno;
     static struct mnttab       mnt;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -381,14 +381,14 @@ mountp( char *file, int *nfs)
 
     while ( getmntent( mtab, &mnt ) == 0 ) {
         /* local fs */
-        if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+        if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
             fclose( mtab );
             return mnt.mnt_mountp;
         }
 
         /* check for nfs. we probably should use
          * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
-        if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt.mnt_special, ':')) {
             *nfs = 1;
             fclose( mtab );
@@ -458,7 +458,7 @@ special(char *file, int *nfs)
     struct mntent      *mnt;
     int                found=0;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0 ) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -469,14 +469,14 @@ special(char *file, int *nfs)
 
     while (( mnt = getmntent( mtab )) != NULL ) {
         /* check for local fs */
-        if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+        if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
            found = 1;
            break;
         }
 
         /* check for an nfs mount entry. the alternative is to use
         * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
-        if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt->mnt_fsname, ':')) {
             *nfs = 1;
            found = 1;
@@ -510,6 +510,7 @@ static int getfsquota(const AFPObj *obj, struct vol *vol, const int uid, struct
     struct quotctl      qc;
 #endif
 
+    memset(dq, 0, sizeof(struct dqblk));
     memset(&dqg, 0, sizeof(dqg));
        
 #ifdef __svr4__
@@ -558,7 +559,9 @@ static int getfsquota(const AFPObj *obj, struct vol *vol, const int uid, struct
 
 #else /* BSD4_4 */
     if (get_linux_quota (WANT_USER_QUOTA, vol->v_gvs, uid, dq) !=0) {
-        return( AFPERR_PARAM );
+#ifdef DEBUG_QUOTA
+        LOG(log_debug, logtype_afpd, "user quota did not work!" );
+#endif /* DEBUG_QUOTA */
     }
 
     if (get_linux_quota(WANT_GROUP_QUOTA, vol->v_gvs, getegid(),  &dqg) != 0) {
@@ -590,10 +593,15 @@ static int getfsquota(const AFPObj *obj, struct vol *vol, const int uid, struct
       ) /* if */
     {
         /* use group quota limits rather than user limits */
-        dq->dqb_curblocks = dqg.dqb_curblocks;
         dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
         dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
-        dq->dqb_btimelimit = dqg.dqb_btimelimit;
+        dq->dqb_curblocks = dqg.dqb_curblocks;
+        dq->dqb_ihardlimit = dqg.dqb_ihardlimit;
+        dq->dqb_isoftlimit = dqg.dqb_isoftlimit;
+        dq->dqb_curinodes = dqg.dqb_curinodes;
+        dq->dqb_btime = dqg.dqb_btime;
+        dq->dqb_itime = dqg.dqb_itime;
+        dq->bsize = dqg.bsize;
     } /* if */
 
 #endif /* TRU64 */
diff --git a/etc/afpd/spotlight-packet.bin b/etc/afpd/spotlight-packet.bin
new file mode 100644 (file)
index 0000000..7ddff55
Binary files /dev/null and b/etc/afpd/spotlight-packet.bin differ
diff --git a/etc/afpd/spotlight-packet2.bin b/etc/afpd/spotlight-packet2.bin
new file mode 100644 (file)
index 0000000..aa3bb07
Binary files /dev/null and b/etc/afpd/spotlight-packet2.bin differ
diff --git a/etc/afpd/spotlight.c b/etc/afpd/spotlight.c
new file mode 100644 (file)
index 0000000..5654624
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#define USE_LIST
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <time.h>
+#include <utime.h>
+
+#include <atalk/list.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/dalloc.h>
+#include <atalk/byteorder.h>
+#include <atalk/netatalk_conf.h>
+#include <atalk/volume.h>
+#include <atalk/spotlight.h>
+
+#include "directory.h"
+
+static TALLOC_CTX *sl_ctx;
+static void *sl_module;
+static struct sl_module_export *sl_module_export;
+
+/* Helper functions and stuff */
+static const char *neststrings[] = {
+    "",
+    "\t",
+    "\t\t",
+    "\t\t\t",
+    "\t\t\t\t",
+    "\t\t\t\t\t",
+    "\t\t\t\t\t\t",
+};
+
+static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
+{
+    const char *type;
+
+    LOG(log_debug, logtype_sl, "%s%s(#%d): {",
+        neststrings[nestinglevel], talloc_get_name(dd), talloc_array_length(dd->dd_talloc_array));
+
+    for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
+
+        type = talloc_get_name(dd->dd_talloc_array[n]);
+
+        if (STRCMP(type, ==, "DALLOC_CTX")
+                   || STRCMP(type, ==, "sl_array_t")
+                   || STRCMP(type, ==, "sl_filemeta_t")
+                   || STRCMP(type, ==, "sl_dict_t")) {
+            dd_dump(dd->dd_talloc_array[n], nestinglevel + 1);
+        } else if (STRCMP(type, ==, "uint64_t")) {
+            uint64_t i;
+            memcpy(&i, dd->dd_talloc_array[n], sizeof(uint64_t));
+            LOG(log_debug, logtype_sl, "%suint64_t: 0x%04x", neststrings[nestinglevel + 1], i);
+        } else if (STRCMP(type, ==, "char *")) {
+            LOG(log_debug, logtype_sl, "%sstring: %s", neststrings[nestinglevel + 1], (char *)dd->dd_talloc_array[n]);
+        } else if (STRCMP(type, ==, "sl_bool_t")) {
+            sl_bool_t bl;
+            memcpy(&bl, dd->dd_talloc_array[n], sizeof(sl_bool_t));
+            LOG(log_debug, logtype_sl, "%sbool: %s", neststrings[nestinglevel + 1], bl ? "true" : "false");
+        } else if (STRCMP(type, ==, "sl_nil_t")) {
+            LOG(log_debug, logtype_sl, "%snil", neststrings[nestinglevel + 1]);
+        } else if (STRCMP(type, ==, "sl_time_t")) {
+            sl_time_t t;
+            struct tm *tm;
+            char datestring[256];
+            memcpy(&t, dd->dd_talloc_array[n], sizeof(sl_time_t));
+            tm = localtime(&t.tv_sec);
+            strftime(datestring, sizeof(datestring), "%Y-%m-%d %H:%M:%S", tm);
+            LOG(log_debug, logtype_sl, "%ssl_time_t: %s.%06d", neststrings[nestinglevel + 1], datestring, t.tv_usec);
+        } else if (STRCMP(type, ==, "sl_cnids_t")) {
+            sl_cnids_t cnids;
+            memcpy(&cnids, dd->dd_talloc_array[n], sizeof(sl_cnids_t));
+            LOG(log_debug, logtype_sl, "%sCNIDs: unkn1: 0x%" PRIx16 ", unkn2: 0x%" PRIx32,
+                   neststrings[nestinglevel + 1], cnids.ca_unkn1, cnids.ca_context);
+            if (cnids.ca_cnids)
+                dd_dump(cnids.ca_cnids, nestinglevel + 2);
+        } else {
+            LOG(log_debug, logtype_sl, "%stype: %s", neststrings[nestinglevel + 1], type);
+        }
+    }
+    LOG(log_debug, logtype_sl, "%s}", neststrings[nestinglevel]);
+}
+
+#ifndef SPOT_TEST_MAIN
+/**************************************************************************************************
+ * Spotlight queries
+ **************************************************************************************************/
+
+static ATALK_LIST_HEAD(sl_queries);
+
+/*!
+ * Add a query to the list of active queries
+ */
+static int slq_add(slq_t *slq)
+{
+    list_add(&(slq->slq_list), &sl_queries);
+    return 0;
+}
+
+static int slq_remove(slq_t *slq)
+{
+    EC_INIT;
+    struct list_head *p;
+    slq_t *q = NULL;
+
+    list_for_each(p, &sl_queries) {
+        q = list_entry(p, slq_t, slq_list);
+        if ((q->slq_ctx1 == slq->slq_ctx1) && (q->slq_ctx2 == slq->slq_ctx2)) {            
+            list_del(p);
+            break;
+        }
+        q = NULL;
+    }
+
+    if (q == NULL) {
+        /* The SL query 'slq' was not found in the list, this is not supposed to happen! */
+        LOG(log_warning, logtype_sl, "slq_remove: slq not in active query list");
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static slq_t *slq_for_ctx(uint64_t ctx1, uint64_t ctx2)
+{
+    EC_INIT;
+    slq_t *q = NULL;
+    struct list_head *p;
+
+    list_for_each(p, &sl_queries) {
+        q = list_entry(p, slq_t, slq_list);
+
+        LOG(log_debug, logtype_sl, "slq_for_ctx(ctx1: 0x%" PRIx64 ", ctx2: 0x%" PRIx64
+            "): active: ctx1: 0x%" PRIx64 ", ctx2: 0x%" PRIx64,
+            ctx1, ctx2, q->slq_ctx1, q->slq_ctx2);
+
+        if ((q->slq_ctx1 == ctx1) && (q->slq_ctx2 == ctx2)) {            
+            break;
+        }
+        q = NULL;
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        q = NULL;
+    return q;
+}
+
+/* Error handling for queries */
+static void slq_error(slq_t *slq)
+{
+    if (!slq)
+        return;
+    sl_module_export->sl_mod_error(slq);
+    slq_remove(slq);
+    talloc_free(slq);
+}
+
+/**************************************************************************************************
+ * Spotlight RPC functions
+ **************************************************************************************************/
+
+static int sl_rpc_fetchPropertiesForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+
+    char *s;
+    sl_dict_t *dict;
+    sl_array_t *array;
+    sl_uuid_t uuid;
+
+    if (!v->v_uuid)
+        EC_FAIL_LOG("sl_rpc_fetchPropertiesForContext: missing UUID for volume: %s", v->v_localname);
+
+    dict = talloc_zero(reply, sl_dict_t);
+
+    /* key/val 1 */
+    s = dalloc_strdup(dict, "kMDSStoreMetaScopes");
+    dalloc_add(dict, s, char *);
+
+    array = talloc_zero(dict, sl_array_t);
+    s = dalloc_strdup(array, "kMDQueryScopeComputer");
+    dalloc_add(array, s, char *);
+    dalloc_add(dict, array, sl_array_t);
+
+    /* key/val 2 */
+    s = dalloc_strdup(dict, "kMDSStorePathScopes");
+    dalloc_add(dict, s, char *);
+
+    array = talloc_zero(dict, sl_array_t);
+    s = dalloc_strdup(array, v->v_path);
+    dalloc_add(array, s, char *);
+    dalloc_add(dict, array, sl_array_t);
+
+    /* key/val 3 */
+    s = dalloc_strdup(dict, "kMDSStoreUUID");
+    dalloc_add(dict, s, char *);
+
+    memcpy(uuid.sl_uuid, v->v_uuid, 16);
+    dalloc_add_copy(dict, &uuid, sl_uuid_t);
+
+    /* key/val 4 */
+    s = dalloc_strdup(dict, "kMDSStoreHasPersistentUUID");
+    dalloc_add(dict, s, char *);
+    sl_bool_t b = true;
+    dalloc_add_copy(dict, &b, sl_bool_t);
+
+    dalloc_add(reply, dict, sl_dict_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int cnid_comp_fn(const void *p1, const void *p2)
+{
+    const uint64_t *cnid1 = p1, *cnid2 = p2;
+    if (*cnid1 == *cnid2)
+        return 0;
+    if (*cnid1 < *cnid2)
+        return -1;
+    else
+        return 1;            
+}
+
+static int sl_createCNIDArray(slq_t *slq, const DALLOC_CTX *p)
+{
+    EC_INIT;
+    uint64_t *cnids = NULL;
+
+    EC_NULL( cnids = talloc_array(slq, uint64_t, talloc_array_length(p)) );
+    for (int i = 0; i < talloc_array_length(p); i++)
+        memcpy(&cnids[i], p->dd_talloc_array[i], sizeof(uint64_t));
+    qsort(cnids, talloc_array_length(p), sizeof(uint64_t), cnid_comp_fn);
+
+    slq->slq_cnids = cnids;
+    slq->slq_cnids_num = talloc_array_length(p);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (cnids)
+            talloc_free(cnids);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_openQuery(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, struct vol *v)
+{
+    EC_INIT;
+    char *sl_query;
+    uint64_t *uint64;
+    DALLOC_CTX *reqinfo;
+    sl_array_t *array;
+    sl_cnids_t *cnids;
+    slq_t *slq = NULL;
+
+    /* Allocate and initialize query object */
+    slq = talloc_zero(sl_ctx, slq_t);
+    slq->slq_state = SLQ_STATE_NEW;
+    slq->slq_obj = obj;
+    slq->slq_vol = v;
+    slq->slq_allow_expr = obj->options.flags & OPTION_SPOTLIGHT_EXPR ? true : false;
+    slq->slq_result_limit = obj->options.sparql_limit;
+
+    LOG(log_info, logtype_sl, "sl_rpc_openQuery: expr: %s, limit: %" PRIu64,
+        slq->slq_allow_expr ? "yes" : "no", slq->slq_result_limit);
+
+    /* convert spotlight query charset to host charset */
+    EC_NULL_LOG( sl_query = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryString") );
+    char slq_host[MAXPATHLEN + 1];
+    uint16_t convflags = v->v_mtou_flags;
+    size_t slq_maclen;
+    if (convert_charset(CH_UTF8_MAC, v->v_volcharset, v->v_maccharset, sl_query, strlen(sl_query), slq_host, MAXPATHLEN, &convflags) == -1) {
+        LOG(log_error, logtype_afpd, "sl_rpc_openQuery(\"%s\"): charset conversion failed", sl_query);
+        EC_FAIL;
+    }
+    slq->slq_qstring = talloc_strdup(slq, slq_host);
+    LOG(log_debug, logtype_sl, "sl_rpc_openQuery: %s", slq->slq_qstring);
+
+    slq->slq_time = time(NULL);
+    EC_NULL_LOG( uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    slq->slq_ctx1 = *uint64;
+    EC_NULL_LOG( uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    slq->slq_ctx2 = *uint64;
+    EC_NULL_LOG( reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDAttributeArray") );
+    slq->slq_reqinfo = talloc_steal(slq, reqinfo);
+    if ((cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryItemArray"))) {
+        EC_ZERO_LOG( sl_createCNIDArray(slq, cnids->ca_cnids) );
+    }
+        
+    LOG(log_maxdebug, logtype_sl, "sl_rpc_openQuery: requested attributes:");
+    dd_dump(slq->slq_reqinfo, 0);
+
+    (void)slq_add(slq);
+    
+    /* Run the query */
+    EC_ZERO( sl_module_export->sl_mod_start_search(slq) );
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = ret == 0 ? 0 : UINT64_MAX;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchQueryResultsForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t *uint64, ctx1, ctx2;
+
+    /* Context */
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    ctx1 = *uint64;
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    ctx2 = *uint64;
+
+    /* Get query for context */
+    EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) );
+    if (slq->slq_state != SLQ_STATE_RUNNING && slq->slq_state != SLQ_STATE_DONE) {
+        EC_FAIL_LOG("Spotlight: attempt to fetch results for query that isn't active");
+    }
+
+    /* Create and pass reply handle */
+    EC_NULL( slq->slq_reply = talloc_zero(reply, sl_array_t) );
+
+    /* Fetch Tracker results*/
+    EC_ZERO_LOG( sl_module_export->sl_mod_fetch_result(slq) );
+
+    dalloc_add(reply, slq->slq_reply, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+static int sl_rpc_storeAttributesForOIDArray(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    uint64_t uint64;
+    sl_array_t *array;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    char *path;
+    struct dir *dir;
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+    LOG(log_debug, logtype_sl, "sl_rpc_storeAttributesForOIDArray: CNID: %" PRIu32, id);
+
+    if (htonl(id) == DIRDID_ROOT) {
+        path = vol->v_path;
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+
+        did = htonl(id);
+        EC_NULL_LOG( path = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL_LOG( dir = dirlookup(vol, did) );
+        EC_NEG1_LOG( movecwd(vol, dir) );
+    }
+
+    /*
+     * We're possibly supposed to update attributes in two places: the
+     * database and the filesystem.  Due to the lack of documentation
+     * and not yet implemented database updates, we cherry pick attributes
+     * that seems to be candidates for updating filesystem metadata.
+     */
+
+    if ((sl_time = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "DALLOC_CTX", 1, "kMDItemFSContentChangeDate"))) {
+        struct utimbuf utimes;
+        utimes.actime = utimes.modtime = sl_time->tv_sec;
+        utime(path, &utimes);
+    }
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchAttributeNamesForOIDArray(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    uint64_t uint64;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    char *path;
+    struct dir *dir;
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+    LOG(log_debug, logtype_sl, "sl_rpc_fetchAttributeNamesForOIDArray: CNID: %" PRIu32, id);
+
+    if (htonl(id) == DIRDID_ROOT) {
+        path = vol->v_path;
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+
+        did = htonl(id);
+        EC_NULL_LOG( path = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL_LOG( dir = dirlookup(vol, did) );
+        EC_NEG1_LOG( movecwd(vol, dir) );
+    }
+
+    /* Result array */
+    sl_array_t *array = talloc_zero(reply, sl_array_t);
+    dalloc_add(reply, array, sl_array_t);
+
+    /* Return result value 0 */
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+
+    /* Return CNID array */
+    sl_cnids_t *replycnids = talloc_zero(reply, sl_cnids_t);
+    replycnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    replycnids->ca_unkn1 = 0xfec;
+    replycnids->ca_context = cnids->ca_context;
+    uint64 = (uint64_t)id;
+    dalloc_add_copy(replycnids->ca_cnids, &uint64, uint64_t);
+    dalloc_add(array, replycnids, sl_cnids_t);
+
+    /* Return filemeta array */
+
+    /*
+     * FIXME: this should return the real attributes from all known metadata sources
+     * (Tracker and filesystem)
+     */
+    sl_array_t *mdattrs = talloc_zero(reply, sl_array_t);
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSName"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemDisplayName"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSSize"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSOwnerUserID"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSOwnerGroupID"), "char *");
+    dalloc_add(mdattrs, dalloc_strdup(mdattrs, "kMDItemFSContentChangeDate"), "char *");
+
+    sl_filemeta_t *fmeta = talloc_zero(reply, sl_filemeta_t);
+    dalloc_add(fmeta, mdattrs, sl_array_t);
+    dalloc_add(array, fmeta, sl_filemeta_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_fetchAttributesForOIDArray(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *vol)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t uint64;
+    sl_cnids_t *cnids;
+    sl_time_t *sl_time;
+    cnid_t id;
+    struct dir *dir;
+    sl_array_t *reqinfo;
+
+
+
+    /* Allocate and initialize query object */
+    slq = talloc_zero(reply, slq_t);
+    slq->slq_state = SLQ_STATE_ATTRS;
+    slq->slq_obj = obj;
+    slq->slq_vol = vol;
+    EC_NULL( slq->slq_reply = talloc_zero(reply, sl_array_t) );
+    EC_NULL( reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1) );
+    slq->slq_reqinfo = talloc_steal(slq, reqinfo);
+    
+    EC_NULL_LOG( cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2) );
+    memcpy(&uint64, cnids->ca_cnids->dd_talloc_array[0], sizeof(uint64_t));
+    id = (cnid_t)uint64;
+
+    if (htonl(id) == DIRDID_ROOT) {
+        slq->slq_path = talloc_strdup(slq, vol->v_path);
+    } else if (id < CNID_START) {
+        EC_FAIL;
+    } else {
+        cnid_t did;
+        char buffer[12 + MAXPATHLEN + 1];
+        char *name;
+        did = htonl(id);
+        EC_NULL( name = cnid_resolve(vol->v_cdb, &did, buffer, sizeof(buffer)) );
+        EC_NULL( dir = dirlookup(vol, did) );
+        EC_NULL( slq->slq_path = talloc_asprintf(slq, "%s/%s", bdata(dir->d_fullpath), name) );
+    }
+
+    /* Return result value 0 */
+    uint64_t sl_res = 0;
+    dalloc_add_copy(slq->slq_reply, &sl_res, uint64_t);
+
+    /* Return CNID array */
+    sl_cnids_t *replycnids = talloc_zero(reply, sl_cnids_t);
+    replycnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    replycnids->ca_unkn1 = 0xfec;
+    replycnids->ca_context = cnids->ca_context;
+    uint64 = (uint64_t)id;
+    dalloc_add_copy(replycnids->ca_cnids, &uint64, uint64_t);
+    dalloc_add(slq->slq_reply, replycnids, sl_cnids_t);
+
+    /* Fetch attributes from module */
+    EC_ZERO_LOG( sl_module_export->sl_mod_fetch_attrs(slq) );
+
+    dalloc_add(reply, slq->slq_reply, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_rpc_closeQueryForContext(const AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
+{
+    EC_INIT;
+    slq_t *slq = NULL;
+    uint64_t *uint64, ctx1, ctx2;
+    sl_array_t *array;
+    
+    /* Context */
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1) );
+    ctx1 = *uint64;
+    EC_NULL_LOG (uint64 = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 2) );
+    ctx2 = *uint64;
+
+    /* Get query for context and free it */
+    EC_NULL_LOG( slq = slq_for_ctx(ctx1, ctx2) );
+    if (slq->slq_state != SLQ_STATE_DONE)
+        LOG(log_warning, logtype_sl, "Closing active query");
+    sl_module_export->sl_mod_end_search(slq);
+    slq_remove(slq);
+    talloc_free(slq);
+    slq = NULL;
+
+    array = talloc_zero(reply, sl_array_t);
+    uint64_t sl_res = 0;
+    dalloc_add_copy(array, &sl_res, uint64_t);
+    dalloc_add(reply, array, sl_array_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        slq_error(slq);
+    }
+    EC_EXIT;
+}
+
+/**************************************************************************************************
+ * Spotlight module functions
+ **************************************************************************************************/
+
+int sl_mod_load(AFPObj *obj)
+{
+    EC_INIT;
+
+    sl_ctx = talloc_new(NULL);
+
+    if ((sl_module = mod_open(obj->options.slmod_path)) == NULL) {
+        LOG(log_error, logtype_sl, "Failed to load module \'%s\': %s", obj->options.slmod_path, mod_error());
+        EC_FAIL;
+    }
+
+    if ((sl_module_export = mod_symbol(sl_module, "sl_mod")) == NULL) {
+        LOG(log_error, logtype_sl, "sl_mod_load(%s): mod_symbol error for symbol sl_mod", obj->options.slmod_path);
+        EC_FAIL;
+    }
+
+    sl_module_export->sl_mod_init(obj);
+   
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+/**
+ * Index a file
+ **/
+void sl_index_file(const char *path)
+{
+    if (sl_module_export && sl_module_export->sl_mod_index_file)
+        sl_module_export->sl_mod_index_file(path);
+}
+
+/**************************************************************************************************
+ * AFP functions
+ **************************************************************************************************/
+
+int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
+{
+    EC_INIT;
+    TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+    uint16_t vid;
+    int cmd;
+    int endianess = SL_ENC_LITTLE_ENDIAN;
+    struct vol      *vol;
+    DALLOC_CTX *query;
+    DALLOC_CTX *reply;
+    char *rpccmd;
+    int len;
+
+    *rbuflen = 0;
+
+    if (sl_module == NULL)
+        return AFPERR_NOOP;
+
+    ibuf += 2;
+    ibuflen -= 2;
+
+    vid = SVAL(ibuf, 0);
+    LOG(log_debug, logtype_sl, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
+
+    if ((vol = getvolbyvid(vid)) == NULL) {
+        LOG(log_error, logtype_sl, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
+        ret = AFPERR_ACCESS;
+        goto EC_CLEANUP;
+    }
+
+    /*    IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
+
+    cmd = RIVAL(ibuf, 6);
+    LOG(log_debug, logtype_sl, "afp_spotlight_rpc(cmd: %d)", cmd);
+
+    /*    IVAL(ibuf, 10: unknown, always 0x00000000 */
+
+    switch (cmd) {
+
+    case SPOTLIGHT_CMD_OPEN:
+    case SPOTLIGHT_CMD_OPEN2:
+        RSIVAL(rbuf, 0, ntohs(vid));
+        RSIVAL(rbuf, 4, 0);
+        len = strlen(vol->v_path) + 1;
+        strncpy(rbuf + 8, vol->v_path, len);
+        *rbuflen += 8 + len;
+        break;
+
+    case SPOTLIGHT_CMD_FLAGS:
+        RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? Helios uses 0x1eefface */
+        *rbuflen += 4;
+        break;
+
+    case SPOTLIGHT_CMD_RPC:
+        EC_NULL( query = talloc_zero(tmp_ctx, DALLOC_CTX) );
+        EC_NULL( reply = talloc_zero(tmp_ctx, DALLOC_CTX) );
+        EC_NEG1_LOG( sl_unpack(query, ibuf + 22) );
+
+        LOG(log_debug, logtype_sl, "afp_spotlight_rpc: Request dump:");
+        dd_dump(query, 0);
+
+        EC_NULL_LOG( rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "char *", 0) );
+
+        if (STRCMP(rpccmd, ==, "fetchPropertiesForContext:")) {
+            EC_ZERO_LOG( sl_rpc_fetchPropertiesForContext(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "openQueryWithParams:forContext:")) {
+            EC_ZERO_LOG( sl_rpc_openQuery(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchQueryResultsForContext:")) {
+            EC_ZERO_LOG( sl_rpc_fetchQueryResultsForContext(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "storeAttributes:forOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_storeAttributesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchAttributeNamesForOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_fetchAttributeNamesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "fetchAttributes:forOIDArray:context:")) {
+            EC_ZERO_LOG( sl_rpc_fetchAttributesForOIDArray(obj, query, reply, vol) );
+        } else if (STRCMP(rpccmd, ==, "closeQueryForContext:")) {
+            EC_ZERO_LOG( sl_rpc_closeQueryForContext(obj, query, reply, vol) );
+        } else {
+            LOG(log_error, logtype_sl, "afp_spotlight_rpc: unknown Spotlight RPC: %s", rpccmd);
+        }
+
+        LOG(log_debug, logtype_sl, "afp_spotlight_rpc: Reply dump:");
+        dd_dump(reply, 0);
+
+        memset(rbuf, 0, 4);
+        *rbuflen += 4;
+
+        EC_NEG1_LOG( len = sl_pack(reply, rbuf + 4) );
+        *rbuflen += len;
+        break;
+    }
+
+EC_CLEANUP:
+    talloc_free(tmp_ctx);
+    if (ret != AFP_OK) {
+        *rbuflen = 0;
+        return AFPERR_MISC;
+    }
+    EC_EXIT;
+}
+#endif
+
+/**************************************************************************************************
+ * Testing
+ **************************************************************************************************/
+
+#ifdef SPOT_TEST_MAIN
+
+int main(int argc, char **argv)
+{
+    EC_INIT;
+    TALLOC_CTX *mem_ctx = talloc_new(NULL);
+    DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
+    uint64_t i;
+
+    set_processname("spot");
+    setuplog("default:info,spotlight:debug", "/dev/tty");
+
+    LOG(log_info, logtype_sl, "Start");
+
+    i = 1;
+    dalloc_add_copy(dd, &i, uint64_t);
+    char *str = dalloc_strdup(dd, "hello world");
+    dalloc_add(dd, str, char *);
+    sl_bool_t b = true;
+    dalloc_add_copy(dd, &b, sl_bool_t);
+
+    /* add a nested array */
+    DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
+    i = 3;
+    dalloc_add_copy(nested, &i, uint64_t);
+    dalloc_add(dd, nested, DALLOC_CTX);
+
+    /* test an allocated CNID array */
+    uint64_t id = 16;
+    sl_cnids_t *cnids = talloc_zero(dd, sl_cnids_t);
+    cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    cnids->ca_unkn1 = 1;
+    dalloc_add_copy(cnids->ca_cnids, &id, uint64_t);
+    dalloc_add(dd, cnids, sl_cnids_t);
+
+    /* Now the Spotlight types */
+    sl_array_t *sl_array = talloc_zero(dd, sl_array_t);
+    i = 0x1234;
+    dalloc_add_copy(sl_array, &i, uint64_t);
+
+    sl_dict_t *sl_dict = talloc_zero(dd, sl_dict_t);
+    i = 0xffff;
+    dalloc_add_copy(sl_dict, &i, uint64_t);
+    dalloc_add(sl_array, sl_dict, sl_dict_t);
+
+    dalloc_add(dd, sl_array, sl_array_t);
+
+    dd_dump(dd, 0);
+
+    /* now parse a real spotlight packet */
+    if (argc > 1) {
+        char ibuf[8192];
+        char rbuf[8192];
+        int fd;
+        size_t len;
+        DALLOC_CTX *query;
+
+        EC_NULL( query = talloc_zero(mem_ctx, DALLOC_CTX) );
+
+        EC_NEG1_LOG( fd = open(argv[1], O_RDONLY) );
+        EC_NEG1_LOG( len = read(fd, ibuf, 8192) );
+        close(fd);
+        EC_NEG1_LOG( sl_unpack(query, ibuf + 24) );
+
+        /* Now dump the whole thing */
+        dd_dump(query, 0);
+    }
+
+#if 0
+    /* packing  */
+    int qlen;
+    char buf[MAX_SLQ_DAT];
+    EC_NEG1_LOG( qlen = sl_pack(query, buf) );
+
+    EC_NEG1_LOG( fd = open("test.bin", O_RDWR) );
+    lseek(fd, 24, SEEK_SET);
+    write(fd, buf, qlen);
+    close(fd);
+#endif
+
+EC_CLEANUP:
+    if (mem_ctx) {
+        talloc_free(mem_ctx);
+        mem_ctx = NULL;
+    }
+    EC_EXIT;
+}
+#endif
diff --git a/etc/afpd/spotlight_marshalling.c b/etc/afpd/spotlight_marshalling.c
new file mode 100644 (file)
index 0000000..69733f4
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/dalloc.h>
+#include <atalk/byteorder.h>
+#include <atalk/netatalk_conf.h>
+#include <atalk/volume.h>
+#include <atalk/dsi.h>
+#include <atalk/spotlight.h>
+
+#define MAX_SLQ_DAT (DSI_DATASIZ - 64)
+#define MAX_SLQ_TOC 8192
+
+/**************************************************************************************************
+ * RPC data marshalling and unmarshalling
+ **************************************************************************************************/
+
+/* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */
+#define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U)
+
+#define SQ_TYPE_NULL    0x0000
+#define SQ_TYPE_COMPLEX 0x0200
+#define SQ_TYPE_INT64   0x8400
+#define SQ_TYPE_BOOL    0x0100
+#define SQ_TYPE_FLOAT   0x8500
+#define SQ_TYPE_DATA    0x0700
+#define SQ_TYPE_CNIDS   0x8700
+#define SQ_TYPE_UUID    0x0e00
+#define SQ_TYPE_DATE    0x8600
+#define SQ_TYPE_TOC     0x8800
+
+#define SQ_CPX_TYPE_ARRAY           0x0a00
+#define SQ_CPX_TYPE_STRING          0x0c00
+#define SQ_CPX_TYPE_UTF16_STRING    0x1c00
+#define SQ_CPX_TYPE_DICT            0x0d00
+#define SQ_CPX_TYPE_CNIDS           0x1a00
+#define SQ_CPX_TYPE_FILEMETA        0x1b00
+
+#define SUBQ_SAFETY_LIM 20
+
+/* Forward declarations */
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx);
+static int sl_unpack_loop(DALLOC_CTX *query, const char *buf, int offset, uint count, const uint toc_offset, const uint encoding);
+
+/**************************************************************************************************
+ * Wrapper functions for the *VAL macros with bound checking
+ **************************************************************************************************/
+
+static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val)
+{
+    if (off + sizeof(val) >= maxoff) {
+        LOG(log_error, logtype_sl, "sivalc: off: %zd, maxoff: %zd", off, maxoff);
+        return -1;
+    }
+    SIVAL(buf, off, val);
+    return 0;
+}
+
+static int slvalc(char *buf, off_t off, off_t maxoff, uint64_t val)
+{
+    if (off + sizeof(val) >= maxoff) {
+        LOG(log_error, logtype_sl, "slvalc: off: %zd, maxoff: %zd", off, maxoff);
+        return -1;
+    }
+    SLVAL(buf, off, val);
+    return 0;
+}
+
+/*
+* Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
+* If there is no byte order mark, -1 is returned.
+*/
+static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
+    uint utf16_encoding;
+
+    /* Assumed encoding in absence of a bom is little endian */
+    utf16_encoding = SL_ENC_LITTLE_ENDIAN;
+
+    if (query_length >= 2) {
+        uint8_t le_bom[] = {0xff, 0xfe};
+        uint8_t be_bom[] = {0xfe, 0xff};
+        if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0)
+            utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
+        else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0)
+            utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
+    }
+
+    return utf16_encoding;
+}
+
+/**************************************************************************************************
+ * marshalling functions
+ **************************************************************************************************/
+
+#define SL_OFFSET_DELTA 16
+
+static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
+{
+    uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
+    return tag;
+}
+
+static int sl_pack_float(double d, char *buf, int offset)
+{
+    EC_INIT;
+
+    union {
+        double d;
+        uint64_t w;
+    } ieee_fp_union;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, ieee_fp_union.w) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_uint64(uint64_t u, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_INT64, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, u) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_BOOL, 1, bl ? 1 : 0)) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t);
+}
+
+static int sl_pack_nil(char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_NULL, 1, 1)) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t);
+}
+
+static int sl_pack_date(sl_time_t t, char *buf, int offset)
+{
+    EC_INIT;
+    uint64_t data = 0;
+
+    data = (t.tv_sec + SPOTLIGHT_TIME_DELTA) << 24;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATE, 2, 1)) );
+    EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, data) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + 2 * sizeof(uint64_t);
+}
+
+static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_UUID, 3, 1)) );
+    if (offset + 8 + 16 >= MAX_SLQ_DAT)
+        EC_FAIL;
+    memcpy(buf + offset + 8, uuid, 16);
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset + sizeof(uint64_t) + 16;
+}
+
+static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int off = 0, len;
+    int cnid_count = talloc_array_length(cnids->ca_cnids->dd_talloc_array);
+    uint64_t id;
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, 0)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    len = cnid_count + 1;
+    if (cnid_count > 0)
+        len ++;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, len, 8 /* unknown meaning, but always 8 */)) );
+    offset += 8;
+
+    if (cnid_count > 0) {
+        EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context)) );
+        offset += 8;
+
+        for (int i = 0; i < cnid_count; i++) {
+            memcpy(&id, cnids->ca_cnids->dd_talloc_array[i], sizeof(uint64_t));
+            EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, id) );
+            offset += 8;
+        }
+    }
+    
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_array(sl_array_t *array, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int count = talloc_array_length(array->dd_talloc_array);
+    int octets = (offset + SL_OFFSET_DELTA) / 8;
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_NEG1( offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+
+    EC_ZERO( slvalc(toc_buf,
+                    *toc_idx * 8,
+                    MAX_SLQ_TOC,
+                    sl_pack_tag(SQ_CPX_TYPE_DICT,
+                                (offset + SL_OFFSET_DELTA) / 8,
+                                talloc_array_length(dict->dd_talloc_array))) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_NEG1( offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx) );
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int fmlen;                  /* lenght of filemeta */
+    int saveoff = offset;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    offset += 16;
+
+    EC_NEG1( fmlen = sl_pack(fm, buf + offset) );
+
+    /* Check for empty filemeta array, if it's only 40 bytes, it's only the header but no content */
+    LOG(log_debug, logtype_sl, "fmlen: %d", fmlen);
+    if (fmlen > 40)
+        offset += fmlen;
+    else
+        fmlen = 0;
+
+    EC_ZERO( slvalc(buf, saveoff + 8, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8 /* unknown meaning, but always 8 */)) );
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_FILEMETA, (saveoff + SL_OFFSET_DELTA) / 8, fmlen / 8)) );
+    *toc_idx += 1;
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    int len, octets, used_in_last_octet;
+
+    len = strlen(s);
+    octets = (len / 8) + (len & 7 ? 1 : 0);
+    used_in_last_octet = 8 - (octets * 8 - len);
+
+    EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_STRING, (offset + SL_OFFSET_DELTA) / 8, used_in_last_octet)) );
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
+    *toc_idx += 1;
+    offset += 8;
+
+    EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet)) );
+    offset += 8;
+
+    if (offset + octets * 8 > MAX_SLQ_DAT)
+        EC_FAIL;
+    memset(buf + offset, 0, octets * 8);
+    strncpy(buf + offset, s, len);
+    offset += octets * 8;
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx)
+{
+    EC_INIT;
+    const char *type;
+
+    for (int n = 0; n < talloc_array_length(query->dd_talloc_array); n++) {
+
+        type = talloc_get_name(query->dd_talloc_array[n]);
+
+        if (STRCMP(type, ==, "sl_array_t")) {
+            EC_NEG1( offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_dict_t")) {
+            EC_NEG1( offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_filemeta_t")) {
+            EC_NEG1( offset = sl_pack_filemeta(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "uint64_t")) {
+            uint64_t i;
+            memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
+            EC_NEG1( offset = sl_pack_uint64(i, buf, offset) );
+        } else if (STRCMP(type, ==, "char *")) {
+            EC_NEG1( offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        } else if (STRCMP(type, ==, "sl_bool_t")) {
+            sl_bool_t bl;
+            memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
+            EC_NEG1( offset = sl_pack_bool(bl, buf, offset) );
+        } else if (STRCMP(type, ==, "double")) {
+            double d;
+            memcpy(&d, query->dd_talloc_array[n], sizeof(double));
+            EC_NEG1( offset = sl_pack_float(d, buf, offset) );
+        } else if (STRCMP(type, ==, "sl_nil_t")) {
+            EC_NEG1( offset = sl_pack_nil(buf, offset) );
+        } else if (STRCMP(type, ==, "sl_time_t")) {
+            sl_time_t t;
+            memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
+            EC_NEG1( offset = sl_pack_date(t, buf, offset) );
+        } else if (STRCMP(type, ==, "sl_uuid_t")) {
+            EC_NEG1( offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset) );
+        } else if (STRCMP(type, ==, "sl_cnids_t")) {
+            EC_NEG1( offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return -1;
+    return offset;
+}
+
+/**************************************************************************************************
+ * unmarshalling functions
+ **************************************************************************************************/
+
+static uint64_t sl_unpack_uint64(const char *buf, int offset, uint encoding)
+{
+    if (encoding == SL_ENC_LITTLE_ENDIAN)
+            return LVAL(buf, offset);
+        else
+            return RLVAL(buf, offset);
+}
+
+static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        dalloc_add_copy(query, &query_data64, uint64_t);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    sl_time_t t;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding) >> 24;
+        t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
+        t.tv_usec = 0;
+        dalloc_add_copy(query, &t, sl_time_t);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    sl_uuid_t uuid;
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        memcpy(uuid.sl_uuid, buf + offset, 16);
+        dalloc_add_copy(query, &uuid, sl_uuid_t);
+        offset += 16;
+    }
+
+    return count;
+}
+
+static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
+{
+    int count, i;
+    uint64_t query_data64;
+    double fval;
+    union {
+        double d;
+        uint32_t w[2];
+    } ieee_fp_union;
+
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 >> 32;
+    offset += 8;
+
+    i = 0;
+    while (i++ < count) {
+        if (encoding == SL_ENC_LITTLE_ENDIAN) {
+#ifdef WORDS_BIGENDIAN
+            ieee_fp_union.w[0] = IVAL(buf, offset + 4);
+            ieee_fp_union.w[1] = IVAL(buf, offset);
+#else
+            ieee_fp_union.w[0] = IVAL(buf, offset);
+            ieee_fp_union.w[1] = IVAL(buf, offset + 4);
+#endif
+        } else {
+#ifdef WORDS_BIGENDIAN
+            ieee_fp_union.w[0] = RIVAL(buf, offset);
+            ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
+#else
+            ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
+            ieee_fp_union.w[1] = RIVAL(buf, offset);
+#endif
+        }
+        dalloc_add_copy(query, &ieee_fp_union.d, double);
+        offset += 8;
+    }
+
+    return count;
+}
+
+static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
+{
+    EC_INIT;
+    int count;
+    uint64_t query_data64;
+    sl_cnids_t *cnids;
+
+    EC_NULL( cnids = talloc_zero(query, sl_cnids_t) );
+    EC_NULL( cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX) );
+
+    if (length <= 16)
+        /* that's permitted, it's an empty array */
+        goto EC_CLEANUP;
+    
+    query_data64 = sl_unpack_uint64(buf, offset, encoding);
+    count = query_data64 & 0xffff;
+
+    cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
+    cnids->ca_context = query_data64 >> 32;
+
+    offset += 8;
+
+    while (count --) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
+        offset += 8;
+    }
+
+    dalloc_add(query, cnids, sl_cnids_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static const char *spotlight_get_qtype_string(uint64_t query_type)
+{
+    switch (query_type) {
+    case SQ_TYPE_NULL:
+        return "null";
+    case SQ_TYPE_COMPLEX:
+        return "complex";
+    case SQ_TYPE_INT64:
+        return "int64";
+    case SQ_TYPE_BOOL:
+        return "bool";
+    case SQ_TYPE_FLOAT:
+        return "float";
+    case SQ_TYPE_DATA:
+        return "data";
+    case SQ_TYPE_CNIDS:
+        return "CNIDs";
+    default:
+        return "unknown";
+    }
+}
+
+static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
+{
+    switch (cpx_query_type) {
+    case SQ_CPX_TYPE_ARRAY:
+        return "array";
+    case SQ_CPX_TYPE_STRING:
+        return "string";
+    case SQ_CPX_TYPE_UTF16_STRING:
+        return "utf-16 string";
+    case SQ_CPX_TYPE_DICT:
+        return "dictionary";
+    case SQ_CPX_TYPE_CNIDS:
+        return "CNIDs";
+    case SQ_CPX_TYPE_FILEMETA:
+        return "FileMeta";
+    default:
+        return "unknown";
+    }
+}
+
+static int sl_unpack_cpx(DALLOC_CTX *query,
+                         const char *buf,
+                         const int offset,
+                         uint cpx_query_type,
+                         uint cpx_query_count,
+                         const uint toc_offset,
+                         const uint encoding)
+{
+    EC_INIT;
+
+    int roffset = offset;
+    uint64_t query_data64;
+    uint unicode_encoding;
+    uint8_t mark_exists;
+    char *p, *tmp;
+    int qlen, used_in_last_block, slen;
+    sl_array_t *sl_array;
+    sl_dict_t *sl_dict;
+    sl_filemeta_t *sl_fm;
+
+    switch (cpx_query_type) {
+    case SQ_CPX_TYPE_ARRAY:
+        sl_array = talloc_zero(query, sl_array_t);
+        EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) );
+        dalloc_add(query, sl_array, sl_array_t);
+        break;
+
+    case SQ_CPX_TYPE_DICT:
+        sl_dict = talloc_zero(query, sl_dict_t);
+        EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) );
+        dalloc_add(query, sl_dict, sl_dict_t);
+        break;
+
+    case SQ_CPX_TYPE_STRING:
+    case SQ_CPX_TYPE_UTF16_STRING:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        used_in_last_block = query_data64 >> 32;
+        slen = qlen - 16 + used_in_last_block;
+
+        if (cpx_query_type == SQ_CPX_TYPE_STRING) {
+            p = dalloc_strndup(query, buf + offset + 8, slen);
+        } else {
+            unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding);
+            mark_exists = (unicode_encoding & SL_ENC_UTF_16);
+            if (unicode_encoding & SL_ENC_BIG_ENDIAN)
+                EC_FAIL_LOG("Unsupported big endian UTF16 string");
+            slen -= mark_exists ? 2 : 0;
+            EC_NEG1( convert_string_allocate(CH_UCS2,
+                                             CH_UTF8,
+                                             buf + offset + (mark_exists ? 10 : 8),
+                                             slen,
+                                             &tmp) );
+            p = dalloc_strndup(query, tmp, strlen(tmp));
+            free(tmp);
+        }
+
+        dalloc_add(query, p, char *);
+        roffset += qlen;
+        break;
+
+    case SQ_CPX_TYPE_FILEMETA:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        if (qlen <= 8) {
+            EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen);
+        } else {
+            sl_fm = talloc_zero(query, sl_filemeta_t);
+            EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) );
+            dalloc_add(query, sl_fm, sl_filemeta_t);
+        }
+        roffset += qlen;
+        break;
+
+    case SQ_CPX_TYPE_CNIDS:
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        qlen = (query_data64 & 0xffff) * 8;
+        EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) );
+        roffset += qlen;
+        break;
+
+    default:
+        EC_FAIL;
+    }
+            
+EC_CLEANUP:
+    if (ret != 0)
+        roffset = -1;
+    return roffset;
+}
+
+static int sl_unpack_loop(DALLOC_CTX *query,
+                          const char *buf,
+                          int offset,
+                          uint count,
+                          const uint toc_offset,
+                          const uint encoding)
+{
+    EC_INIT;
+    int i, toc_index, query_length;
+    uint subcount;
+    uint64_t query_data64, query_type;
+    uint cpx_query_type, cpx_query_count;
+    sl_nil_t nil;
+    sl_bool_t b;
+
+    while (count > 0 && (offset < toc_offset)) {
+        query_data64 = sl_unpack_uint64(buf, offset, encoding);
+        query_length = (query_data64 & 0xffff) * 8;
+        query_type = (query_data64 & 0xffff0000) >> 16;
+        if (query_length == 0)
+            EC_FAIL;
+
+        switch (query_type) {
+        case SQ_TYPE_COMPLEX:
+            toc_index = (query_data64 >> 32) - 1;
+            query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
+            cpx_query_type = (query_data64 & 0xffff0000) >> 16;
+            cpx_query_count = query_data64 >> 32;
+
+            EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding));
+            count--;
+            break;
+        case SQ_TYPE_NULL:
+            subcount = query_data64 >> 32;
+            if (subcount > 64)
+                EC_FAIL;
+            nil = 0;
+            for (i = 0; i < subcount; i++)
+                dalloc_add_copy(query, &nil, sl_nil_t);
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_BOOL:
+            b = query_data64 >> 32;
+            dalloc_add_copy(query, &b, sl_bool_t);
+            offset += query_length;
+            count--;
+            break;
+        case SQ_TYPE_INT64:
+            EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_UUID:
+            EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_FLOAT:
+            EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        case SQ_TYPE_DATE:
+            EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
+            offset += query_length;
+            count -= subcount;
+            break;
+        default:
+            EC_FAIL;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0) {
+        offset = -1;
+    }
+    return offset;
+}
+
+/**************************************************************************************************
+ * Global functions for packing und unpacking
+ **************************************************************************************************/
+
+int sl_pack(DALLOC_CTX *query, char *buf)
+{
+    EC_INIT;
+    char toc_buf[MAX_SLQ_TOC];
+    int toc_index = 0;
+    int len = 0;
+
+    memcpy(buf, "432130dm", 8);
+    EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
+    EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) );
+    EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) );
+
+    EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) );
+    if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT)
+        EC_FAIL;
+    memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
+    len += 16 + (toc_index + 1 ) * 8;
+
+EC_CLEANUP:
+    if (ret != 0)
+        len = -1;
+    return len;
+}
+
+int sl_unpack(DALLOC_CTX *query, const char *buf)
+{
+    EC_INIT;
+    int encoding, i, toc_entries;
+    uint64_t toc_offset, tquerylen, toc_entry;
+
+    if (strncmp(buf, "md031234", 8) == 0)
+        encoding = SL_ENC_BIG_ENDIAN;
+    else
+        encoding = SL_ENC_LITTLE_ENDIAN;
+
+    buf += 8;
+
+    toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
+    if (toc_offset < 0 || (toc_offset > 65000)) {
+        EC_FAIL;
+    }
+
+    buf += 8;
+
+    toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
+
+    EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
+
+EC_CLEANUP:
+    EC_EXIT;
+}
index eb5b99b30b970a95f96547e6e87260be38282c9a..d95a7f3b8daf5fb24ecf8894bbb301da6da67154 100644 (file)
@@ -95,7 +95,7 @@ static void status_flags(char *data,
 static int status_server(char *data, const char *server, const struct afp_options *options)
 {
     char                *start = data;
-    char                *Obj, *Type, *Zone;
+    char                *Obj;
     char               buf[32];
     uint16_t           status;
     size_t             len;
@@ -311,39 +311,6 @@ static size_t status_netaddress(char *data, int *servoffset,
     return (data - begin);
 }
 
-static bool append_directoryname(char **pdata,
-                                 size_t offset,
-                                 size_t *size,
-                                 char* DirectoryNamesCount,
-                                 size_t len,
-                                 char *principal)
-{
-    if (sizeof(uint8_t) + len  > maxstatuslen - offset - *size) {
-        LOG(log_error, logtype_afpd,
-            "status:DirectoryNames: out of space for principal '%s' (len=%d)",
-            principal, len);
-        return false;
-    } else if (len > 255) {
-        LOG(log_error, logtype_afpd,
-            "status:DirectoryNames: principal '%s' (len=%d) too long (max=255)",
-            principal, len);
-        return false;
-    }
-
-    LOG(log_info, logtype_afpd,
-        "DirectoryNames[%d]=%s",
-        *DirectoryNamesCount, principal);
-
-    *DirectoryNamesCount += 1;
-    char *data = *pdata;
-    *data++ = len;
-    strncpy(data, principal, len);
-
-    *pdata += len + 1;
-    *size += sizeof(uint8_t) + len;
-
-    return true;
-}
 
 /**
  * DirectoryNamesCount offset: uint16_t
@@ -354,7 +321,7 @@ static bool append_directoryname(char **pdata,
 static size_t status_directorynames(char *data,
                                     int *diroffset,
                                     const DSI *dsi _U_,
-                                    const struct afp_options *options _U_)
+                                    const struct afp_options *options)
 {
     char *begin = data;
     uint16_t offset;
@@ -363,128 +330,14 @@ static size_t status_directorynames(char *data,
     offset = ntohs(offset);
     data += offset;
 
-    char *DirectoryNamesCount = data++;
-    char *DirectoryNames = data;
-    size_t size = sizeof(uint8_t);
-    *DirectoryNamesCount = 0;
-
-    if (!uam_gss_enabled())
-        goto offset_calc;
-
-#ifdef HAVE_KERBEROS
-    krb5_context context;
-    krb5_error_code ret;
-    const char *error_msg;
-
-    if (krb5_init_context(&context)) {
-        LOG(log_error, logtype_afpd,
-            "status:DirectoryNames failed to intialize a krb5_context");
-        goto offset_calc;
-    }
-
-    krb5_keytab keytab;
-    if ((ret = krb5_kt_default(context, &keytab)))
-        goto krb5_error;
-
-    // figure out which service principal to use
-    krb5_keytab_entry entry;
-    char *principal;
-    if (options->k5service && options->fqdn && options->k5realm) {
-        LOG(log_debug, logtype_afpd,
-            "status:DirectoryNames: using service principal specified in options");
-
-        krb5_principal service_principal;
-        if ((ret = krb5_build_principal(context,
-                                        &service_principal,
-                                        strlen(options->k5realm),
-                                        options->k5realm,
-                                        options->k5service,
-                                        options->fqdn,
-                                        NULL)))
-            goto krb5_error;
-
-        // try to get the given service principal from keytab
-        ret = krb5_kt_get_entry(context,
-                                keytab,
-                                service_principal,
-                                0, // kvno - wildcard
-                                0, // enctype - wildcard
-                                &entry);
-        if (ret == KRB5_KT_NOTFOUND) {
-            krb5_unparse_name(context, service_principal, &principal);
-            LOG(log_error, logtype_afpd,
-                "status:DirectoryNames: specified service principal '%s' not found in keytab",
-                principal);
-            // XXX: should this be krb5_xfree?
-#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
-            krb5_free_unparsed_name(context, principal);
-#else
-           krb5_xfree(principal);
-#endif
-            goto krb5_cleanup;
-        }
-        krb5_free_principal(context, service_principal);
-        if (ret)
-            goto krb5_error;
+    if (!uam_gss_enabled() || !options->k5principal) {
+        *data = 0;
+        data++;
     } else {
-        LOG(log_debug, logtype_afpd,
-            "status:DirectoryNames: using first entry from keytab as service principal");
-
-        krb5_kt_cursor cursor;
-        if ((ret = krb5_kt_start_seq_get(context, keytab, &cursor)))
-            goto krb5_error;
-
-        ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
-        krb5_kt_end_seq_get(context, keytab, &cursor);
-        if (ret)
-            goto krb5_error;
+        memcpy(data, options->k5principal, options->k5principal_buflen);
+        data += options->k5principal_buflen;
     }
 
-    krb5_unparse_name(context, entry.principal, &principal);
-    krb5_kt_free_entry(context, &entry);
-
-    append_directoryname(&data,
-                         offset,
-                         &size,
-                         DirectoryNamesCount,
-                         strlen(principal),
-                         principal);
-
-    free(principal);
-    goto krb5_cleanup;
-
-krb5_error:
-    if (ret) {
-        error_msg = krb5_get_error_message(context, ret);
-        LOG(log_note, logtype_afpd, "Can't get principal from default keytab: %s",
-            (char *)error_msg);
-#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
-        krb5_free_error_message(context, error_msg);
-#else
-       krb5_xfree(error_msg);
-#endif
-    }
-
-krb5_cleanup:
-    krb5_kt_close(context, keytab);
-    krb5_free_context(context);
-#else
-    if (!options->k5service || !options->fqdn || !options->k5realm)
-        goto offset_calc;
-
-    char principal[255];
-    size_t len = snprintf(principal, sizeof(principal), "%s/%s@%s",
-                          options->k5service, options->fqdn, options->k5realm);
-
-    append_directoryname(&data,
-                         offset,
-                         &size,
-                         DirectoryNamesCount,
-                         strlen(principal),
-                         principal);
-#endif // HAVE_KERBEROS
-
-offset_calc:
     /* Calculate and store offset for UTF8ServerName */
     *diroffset += sizeof(uint16_t);
     offset = htons(data - begin);
@@ -501,7 +354,7 @@ static size_t status_utf8servername(char *data, int *nameoffset,
     uint16_t namelen;
     size_t len;
     char *begin = data;
-    uint16_t offset, status;
+    uint16_t offset;
 
     memcpy(&offset, data + *nameoffset, sizeof(offset));
     offset = ntohs(offset);
@@ -645,7 +498,7 @@ void set_signature(struct afp_options *options) {
     char *servername_conf;
     int header = 0;
     char buf[1024], *p;
-    FILE *fp = NULL, *randomp;
+    FILE *fp = NULL;
     size_t len;
     char *server_tmp;
     
index 2f94ac8885d76dd18c5b3cf6d3d9d2040681fccb..1cc50d01bf038a4f017c530dcf5b7b001d2d5e9f 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: switch.h,v 1.4 2009-10-15 10:43:13 didg Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
index d99d381edd38cc4408a5076039ef47f53d4e7899..04e54cbbd2f60c1999337a917412bb4cb87b91d1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uam.c,v 1.35 2009-11-08 01:15:31 didg Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved.  See COPYRIGHT.
@@ -31,6 +30,7 @@
 #include <atalk/util.h>
 #include <atalk/globals.h>
 #include <atalk/volume.h>
+#include <atalk/bstrlib.h>
 
 #include "afp_config.h"
 #include "auth.h"
@@ -46,7 +46,7 @@
 /* --- server uam functions -- */
 
 /* uam_load. uams must have a uam_setup function. */
-struct uam_mod *uam_load(const char *path, const char *name)
+struct uam_mod *uam_load(AFPObj *obj, const char *path, const char *name)
 {
     char buf[MAXPATHLEN + 1], *p;
     struct uam_mod *mod;
@@ -82,7 +82,7 @@ struct uam_mod *uam_load(const char *path, const char *name)
     /* version check would go here */
 
     if (!mod->uam_fcn->uam_setup ||
-            ((*mod->uam_fcn->uam_setup)(name) < 0)) {
+        ((*mod->uam_fcn->uam_setup)(obj, name) < 0)) {
         LOG(log_error, logtype_afpd, "uam_load(%s): uam_setup failed", name);
         goto uam_load_err;
     }
@@ -209,25 +209,24 @@ struct passwd *uam_getname(void *private, char *name, const int len)
         return pwent;
         
     /* if we have a NT domain name try with it */
-    if (obj->options.ntdomain && obj->options.ntseparator) {
+    if (obj->options.addomain || (obj->options.ntdomain && obj->options.ntseparator)) {
         /* FIXME What about charset ? */
-        size_t ulen = strlen(obj->options.ntdomain) + strlen(obj->options.ntseparator) + strlen(name);
-        if ((p = malloc(ulen +1))) {
-            strcpy(p, obj->options.ntdomain);
-            strcat(p, obj->options.ntseparator);
-            strcat(p, name);
-            pwent = getpwnam(p);
-            free(p);
-            if (pwent) {
-                int len = strlen(pwent->pw_name);              
-                if (len < MAXUSERLEN) {
-                    strncpy(name,pwent->pw_name, MAXUSERLEN);  
-                }else{
-                    LOG(log_error, logtype_uams, "MAJOR:The name %s is longer than %d",pwent->pw_name,MAXUSERLEN);
-                }
-
-                return pwent;
+        bstring princ;
+        if (obj->options.addomain)
+            princ = bformat("%s@%s", name, obj->options.addomain);
+        else
+            princ = bformat("%s%s%s", obj->options.ntdomain, obj->options.ntseparator, name);
+        pwent = getpwnam(bdata(princ));
+        bdestroy(princ);
+
+        if (pwent) {
+            int len = strlen(pwent->pw_name);              
+            if (len < MAXUSERLEN) {
+                strncpy(name,pwent->pw_name, MAXUSERLEN);  
+            } else {
+                LOG(log_error, logtype_uams, "The name '%s' is longer than %d", pwent->pw_name, MAXUSERLEN);
             }
+            return pwent;
         }
     }
 #ifndef NO_REAL_USER_NAME
index a553ffabff4aaac03c3f787c4e42b77d27c69a39..4eb3bf0464b7251dd89774c7d3109989a62c4603 100644 (file)
@@ -51,11 +51,11 @@ struct uam_obj {
     (a)->uam_next->uam_prev = (a)->uam_prev; \
 } while (0)
 
-extern struct uam_mod *uam_load (const char *, const char *);
+extern struct uam_mod *uam_load (AFPObj *, const char *, const char *);
 extern void uam_unload (struct uam_mod *);
 
 /* auth.c */
-int auth_load (const char *, const char *);
+int auth_load (AFPObj *, const char *, const char *);
 int auth_register (const int, struct uam_obj *);
 #define auth_unregister(a) uam_detach(a)
 struct uam_obj *auth_uamfind (const int, const char *, const int);
index e26e6d1f1b50f702da0fb3ea0d9786937b275e99..a3e4d59704592e9626734bdde323a992953241b1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uid.c,v 1.14 2005-04-28 20:49:45 bfernhomberg Exp $
  * code: jeff@univrel.pr.uconn.edu
  *
  * These functions are abstracted here, so that all calls for resolving
index 647cbd8d164a76182953c420c3650ae773f2a2a2..87745147fc3c0a4bea82ba5652acf4bfbdca107c 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uid.h,v 1.6 2002-08-30 19:32:41 didg Exp $
  * code: jeff@univrel.pr.uconn.edu
  */
 
index 16562f3b757b1d0912d09e221a2d52b5144aee33..a42a6ab9093fde4f923e43510474d52ad58334b4 100644 (file)
@@ -158,7 +158,7 @@ void accessmode(const AFPObj *obj, const struct vol *vol, char *path, struct mac
 
     ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
     if (!st) {
-        if (lstat(path, &sb) != 0)
+        if (ostat(path, &sb, vol_syml_opt(vol)) != 0)
             return;
         st = &sb;
     }
@@ -207,7 +207,7 @@ mode_t mtoumode(struct maccess *ma)
 int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
 {
     if (!path->st_valid) {
-        of_stat(path);
+        of_stat(vol, path);
     }
 
     if (path->st_errno) {
@@ -216,7 +216,7 @@ int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
         
     mode |= vol->v_fperm;
 
-    if (setfilmode( path->u_name, mode, &path->st, vol->v_umask) < 0)
+    if (setfilmode(vol, path->u_name, mode, &path->st) < 0)
         return -1;
     /* we need to set write perm if read set for resource fork */
     return vol->vfs->vfs_setfilmode(vol, path->u_name, mode, &path->st);
@@ -224,23 +224,27 @@ int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
 
 
 /* --------------------- */
-int setdirunixmode(const struct vol *vol, const char *name, mode_t mode)
+int setdirunixmode(const struct vol *vol, char *name, mode_t mode)
 {
     LOG(log_debug, logtype_afpd, "setdirunixmode('%s', mode:%04o) {v_dperm:%04o}",
         fullpathname(name), mode, vol->v_dperm);
 
-    mode |= vol->v_dperm;
+    mode |= (vol->v_dperm | DIRBITS) & ~vol->v_umask;
 
     if (dir_rx_set(mode)) {
        /* extending right? dir first then .AppleDouble in rf_setdirmode */
-       if (chmod_acl(name, (DIRBITS | mode) & ~vol->v_umask) < 0 )
+        if (ochmod(name, mode, NULL,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
                return -1;
     }
     if (vol->vfs->vfs_setdirunixmode(vol, name, mode, NULL) < 0) {
         return  -1 ;
     }
     if (!dir_rx_set(mode)) {
-       if (chmod_acl(name, (DIRBITS | mode) & ~vol->v_umask) < 0 )
+        if (ochmod(name, mode, NULL,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
             return -1;
     }
     return 0;
@@ -249,7 +253,7 @@ int setdirunixmode(const struct vol *vol, const char *name, mode_t mode)
 /* ----------------------------- */
 int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct path* path)
 {
-    if (lchown(path->u_name, uid, gid) < 0 && errno != EPERM) {
+    if (ochown( path->u_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
         LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
             uid, gid, path->u_name, strerror(errno));
         return -1;
@@ -271,7 +275,7 @@ int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct
  * co-opting some bits. */
 int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const gid_t gid)
 {
-    if (lchown(name, uid, gid ) < 0 && errno != EPERM ) {
+    if (ochown(name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
             uid, gid, fullpathname(name), strerror(errno) );
     }
@@ -281,4 +285,3 @@ int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const
 
     return( 0 );
 }
-
index 73a515b2dcf357496ffec0ae57aa67ca11f13422..3b3c281b528c57dd4c035243796340823c35ec8f 100644 (file)
@@ -213,7 +213,7 @@ extern int uquota_getvolspace (const AFPObj *obj, struct vol *, VolSpace *, VolS
 
 extern struct afp_options default_options;
 
-extern int setdirunixmode   (const struct vol *, const char *, mode_t);
+extern int setdirunixmode   (const struct vol *, char *, mode_t);
 extern int setdirmode       (const struct vol *, const char *, mode_t);
 extern int setdirowner      (const struct vol *, const char *, const uid_t, const gid_t);
 extern int setfilunixmode   (const struct vol *, struct path*, const mode_t);
index da57a30a822491746f603ba6112516b3ac325dac..ae6c35236d93a7b264ab3962c4f68cff3055d73c 100644 (file)
@@ -39,6 +39,7 @@
 #include <atalk/iniparser.h>
 #include <atalk/unix.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/server_ipc.h>
 
 #ifdef CNID_DB
 #include <atalk/cnid.h>
@@ -152,7 +153,6 @@ static int get_tm_used(struct vol * restrict vol)
     DIR *dir = NULL;
     const struct dirent *entry;
     const char *p;
-    struct stat st;
     long int links;
     time_t now = time(NULL);
 
@@ -177,13 +177,18 @@ static int get_tm_used(struct vol * restrict vol)
 
             EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") );
             
-            if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1)
+            if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1) {
+                bdestroy(infoplist);
                 continue;
+            }
 
             EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") );
 
-            if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1)
+            if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1) {
+                bdestroy(infoplist);
+                bdestroy(bandsdir);
                 continue;
+            }
 
             used += (links - 1) * bandsize;
             LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes",
@@ -212,7 +217,6 @@ static int getvolspace(const AFPObj *obj, struct vol *vol,
 {
     int         spaceflag, rc;
     uint32_t   maxsize;
-    VolSpace    used;
 #ifndef NO_QUOTA_SUPPORT
     VolSpace    qfree, qtotal;
 #endif
@@ -264,22 +268,6 @@ getvolspace_done:
     return( AFP_OK );
 }
 
-#define FCE_TM_DELTA 10  /* send notification every 10 seconds */
-void vol_fce_tm_event(void)
-{
-    static time_t last;
-    time_t now = time(NULL);
-    struct vol  *vol = getvolumes();
-
-    if ((last + FCE_TM_DELTA) < now) {
-        last = now;
-        for ( ; vol; vol = vol->v_next ) {
-            if (vol->v_flags & AFPVOL_TM)
-                (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_appended);
-        }
-    }
-}
-
 /* -----------------------
  * set volume creation date
  * avoid duplicate, well at least it tries
@@ -381,7 +369,7 @@ static int getvolparams(const AFPObj *obj, uint16_t bitmap, struct vol *vol, str
             }
             /* prior 2.1 only VOLPBIT_ATTR_RO is defined */
             if (obj->afp_version > 20) {
-                if (vol->v_cdb != NULL && (vol->v_cdb->flags & CNID_FLAG_PERSISTENT))
+                if (vol->v_cdb != NULL && (vol->v_cdb->cnid_db_flags & CNID_FLAG_PERSISTENT))
                     ashort |= VOLPBIT_ATTR_FILEID;
                 ashort |= VOLPBIT_ATTR_CATSEARCH;
 
@@ -542,11 +530,12 @@ int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf
     char        *namebuf;
     int         vcnt;
     size_t      len;
+    uint32_t    aint;
 
-    load_volumes(obj, closevol);
+    load_volumes(obj, lv_none);
 
     data = rbuf + 5;
-    for ( vcnt = 0, volume = getvolumes(); volume; volume = volume->v_next ) {
+    for ( vcnt = 0, volume = getvolumes(); volume && vcnt < 255; volume = volume->v_next ) {
         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
             struct maccess ma;
 
@@ -573,6 +562,14 @@ int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf
         if (len == (size_t)-1)
             continue;
 
+        /*
+         * There seems to be an undocumented limit on how big our reply can get
+         * before the client chokes and closes the connection.
+         * Testing with 10.8.4 found the limit at ~4600 bytes. Go figure. 
+         */
+        if (((data + len + 3) - rbuf) > 4600)
+            break;
+
         /* set password bit if there's a volume password */
         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
 
@@ -587,12 +584,13 @@ int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf
     *rbuflen = data - rbuf;
     data = rbuf;
     if ( gettimeofday( &tv, NULL ) < 0 ) {
-        LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
+        LOG(log_error, logtype_afpd, "afp_getsrvrparms: gettimeofday: %s", strerror(errno) );
         *rbuflen = 0;
         return AFPERR_PARAM;
     }
-    tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
-    memcpy(data, &tv.tv_sec, sizeof( uint32_t));
+
+    aint = AD_DATE_FROM_UNIX(tv.tv_sec);
+    memcpy(data, &aint, sizeof( uint32_t));
     data += sizeof( uint32_t);
     *data = vcnt;
     return( AFP_OK );
@@ -641,14 +639,7 @@ static int volume_openDB(const AFPObj *obj, struct vol *volume)
         flags |= CNID_FLAG_NODEV;
     }
 
-    LOG(log_debug, logtype_afpd, "CNID server: %s:%s", volume->v_cnidserver, volume->v_cnidport);
-
-    volume->v_cdb = cnid_open(volume->v_path,
-                              volume->v_umask,
-                              volume->v_cnidscheme,
-                              flags,
-                              volume->v_cnidserver,
-                              volume->v_cnidport);
+    volume->v_cdb = cnid_open(volume, volume->v_cnidscheme, flags);
 
     if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
         /* The first attempt failed and it wasn't yet an attempt to open in-memory */
@@ -657,7 +648,7 @@ static int volume_openDB(const AFPObj *obj, struct vol *volume)
         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
             volume->v_path);
         flags |= CNID_FLAG_MEMORY;
-        volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
+        volume->v_cdb = cnid_open(volume, "tdb", flags);
 #ifdef SERVERTEXT
         /* kill ourself with SIGUSR2 aka msg pending */
         if (volume->v_cdb) {
@@ -672,6 +663,31 @@ static int volume_openDB(const AFPObj *obj, struct vol *volume)
     return (!volume->v_cdb)?-1:0;
 }
 
+/*
+ * Send list of open volumes to afpd master via IPC
+ */
+static void server_ipc_volumes(AFPObj *obj)
+{
+    struct vol *volume, *vols;
+    volume = vols = getvolumes();
+    bstring openvolnames = bfromcstr("");
+    bool firstvol = true;
+
+    while (volume) {
+        if (volume->v_flags & AFPVOL_OPEN) {
+            if (!firstvol)
+                bcatcstr(openvolnames, ", ");
+            else
+                firstvol = false;
+            bcatcstr(openvolnames, volume->v_localname);
+        }
+        volume = volume->v_next;
+    }
+
+    ipc_child_write(obj->ipc_fd, IPC_VOLUMES, blength(openvolnames), bdata(openvolnames));
+    bdestroy(openvolnames);
+}
+
 /* -------------------------
  * we are the user here
  */
@@ -679,16 +695,14 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
 {
     struct stat st;
     char    *volname;
-    char        *p;
 
     struct vol  *volume;
     struct dir  *dir;
     int     len, ret;
     size_t  namelen;
     uint16_t   bitmap;
-    char        path[ MAXPATHLEN + 1];
     char        *vol_uname;
-    char        *vol_mname;
+    char        *vol_mname = NULL;
     char        *volname_tmp;
 
     ibuf += 2;
@@ -721,7 +735,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     if ((len + 1) & 1) /* pad to an even boundary */
         ibuf++;
 
-    load_volumes(obj, closevol);
+    load_volumes(obj, lv_none);
 
     for ( volume = getvolumes(); volume; volume = volume->v_next ) {
         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
@@ -765,33 +779,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         return AFPERR_PARAM;
     }
 
-    if ( NULL == getcwd(path, MAXPATHLEN)) {
-        /* shouldn't be fatal but it will fail later */
-        LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
-        return AFPERR_MISC;
-    }
-
-    /* Normalize volume path */
-#ifdef REALPATH_TAKES_NULL
-    if ((volume->v_path = realpath(path, NULL)) == NULL)
-        return AFPERR_MISC;
-#else
-    if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
-        return AFPERR_MISC;
-    if (realpath(path, volume->v_path) == NULL) {
-        free(volume->v_path);
-        return AFPERR_MISC;
-    }
-    /* Safe some memory */
-    char *tmp;
-    if ((tmp = strdup(volume->v_path)) == NULL) {
-        free(volume->v_path);
-        return AFPERR_MISC;
-    }
-    free(volume->v_path);
-    volume->v_path = tmp;
-#endif
-
     if (volume_codepage(obj, volume) < 0) {
         ret = AFPERR_MISC;
         goto openvol_err;
@@ -820,9 +807,9 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         goto openvol_err;
     }
 
-    if ((vol_uname = strrchr(path, '/')) == NULL)
-        vol_uname = path;
-    else if (*(vol_uname + 1) != '\0')
+    if ((vol_uname = strrchr(volume->v_path, '/')) == NULL)
+        vol_uname = volume->v_path;
+    else if (vol_uname[1] != '\0')
         vol_uname++;
 
     if ((dir = dir_new(vol_mname,
@@ -838,7 +825,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         ret = AFPERR_MISC;
         goto openvol_err;
     }
-    free(vol_mname);
     volume->v_root = dir;
     curdir = dir;
 
@@ -860,7 +846,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
          * fixing the trash at DID 17.
          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
          */
-        if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+        if ((volume->v_cdb->cnid_db_flags & CNID_FLAG_PERSISTENT)) {
 
             /* FIXME find db time stamp */
             if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) {
@@ -873,9 +859,11 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         }
 
         const char *msg;
-        if ((msg = iniparser_getstring(obj->iniconfig, volume->v_configname, "login message",  NULL)) != NULL)
+        if ((msg = atalk_iniparser_getstring(obj->iniconfig, volume->v_configname, "login message",  NULL)) != NULL)
             setmessage(msg);
 
+        free(vol_mname);
+        server_ipc_volumes(obj);
         return( AFP_OK );
     }
 
@@ -890,6 +878,7 @@ openvol_err:
         cnid_close(volume->v_cdb);
         volume->v_cdb = NULL;
     }
+    free(vol_mname);
     *rbuflen = 0;
     return ret;
 }
@@ -947,6 +936,7 @@ int afp_closevol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
     (void)chdir("/");
     curdir = NULL;
     closevol(obj, vol);
+    server_ipc_volumes(obj);
 
     return( AFP_OK );
 }
index 3bca88214bfee50687bc7dc052d5e70a9bb8c948..a8bd2b8bad453a6b51de78a8f90dad5ee8026257 100644 (file)
@@ -19,7 +19,6 @@ extern int              ustatfs_getvolspace (const struct vol *,
                                              uint32_t *);
 extern void             setvoltime (AFPObj *, struct vol *);
 extern int              pollvoltime (AFPObj *);
-extern void             vol_fce_tm_event(void);
 
 /* FP functions */
 int afp_openvol      (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 3ebcbebb465d858df205e0acdbad2f68a2173e8d..a9133d9c7f45d3de60f046b362c19ab6939dc947 100644 (file)
@@ -11,10 +11,10 @@ cnid_dbd_SOURCES = dbif.c pack.c comm.c db_param.c main.c \
                    dbd_add.c dbd_get.c dbd_resolve.c dbd_lookup.c \
                    dbd_update.c dbd_delete.c dbd_getstamp.c \
                    dbd_rebuild_add.c dbd_dbcheck.c dbd_search.c
-cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
+cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@ @MYSQL_LIBS@
 
 cnid_metad_SOURCES = cnid_metad.c usockfd.c db_param.c
-cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la @ACL_LIBS@
+cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la @ACL_LIBS@ @MYSQL_LIBS@
 
 dbd_SOURCES = cmd_dbd.c \
        cmd_dbd_scanvol.c \
@@ -26,7 +26,7 @@ dbd_SOURCES = cmd_dbd.c \
        dbd_rebuild_add.c \
        dbd_resolve.c \
        dbd_update.c
-dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
+dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@ @MYSQL_LIBS@
 
 noinst_HEADERS = dbif.h pack.h db_param.h dbd.h usockfd.h comm.h cmd_dbd.h
 
index 8f8b734216fb2a9413d1ffde668d2ff5fb40b5f1..9e8e008ec0ce86da94d186f57309da9e002ae752 100644 (file)
 #include <signal.h>
 #include <string.h>
 #include <errno.h>
+#include <pwd.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/util.h>
+#include <atalk/errchk.h>
 
 #include "cmd_dbd.h"
-#include "dbd.h"
-#include "dbif.h"
-#include "db_param.h"
-#include "pack.h"
 
-#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
+enum dbd_cmd {dbd_scan, dbd_rebuild};
 
-int nocniddb = 0;               /* Dont open CNID database, only scan filesystem */
+/* Global variables */
 volatile sig_atomic_t alarmed;  /* flags for signals */
-int db_locked;                  /* have we got the fcntl lock on lockfile ? */
-
-static DBD *dbd;
-static int verbose;             /* Logging flag */
-static int exclusive;           /* Exclusive volume access */
-static struct db_param db_param = {
-    NULL,                       /* Volume dirpath */
-    1,                          /* bdb logfile autoremove */
-    64 * 1024,                  /* bdb cachesize (64 MB) */
-    DEFAULT_MAXLOCKS,           /* maxlocks */
-    DEFAULT_MAXLOCKOBJS,        /* maxlockobjs */
-    0,                          /* flush_interval */
-    0,                          /* flush_frequency */
-    0,                          /* usock_file */
-    -1,                         /* fd_table_size */
-    -1,                         /* idle_timeout */
-    -1                          /* max_vols */
-};
-static char dbpath[MAXPATHLEN+1];   /* Path to the dbd database */
 
-/* 
-   Provide some logging
- */
-void dbd_log(enum logtype lt, char *fmt, ...)
-{
-    int len;
-    static char logbuffer[1024];
-    va_list args;
+/* Local variables */
+static dbd_flags_t flags;
 
-    if ( (lt == LOGSTD) || (verbose == 1)) {
-        va_start(args, fmt);
-        len = vsnprintf(logbuffer, 1023, fmt, args);
-        va_end(args);
-        logbuffer[1023] = 0;
-
-        printf("%s\n", logbuffer);
-    }
-}
+/***************************************************************************
+ * Local functions
+ ***************************************************************************/
 
-/* 
  SIGNAL handling:
  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+/*
* SIGNAL handling:
* catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
  */
-
 static void sig_handler(int signo)
 {
     alarmed = 1;
@@ -128,99 +93,83 @@ static void set_signal(void)
 
 static void usage (void)
 {
-    printf("dbd (Netatalk %s)\n"
-           "Usage: dbd [-CeFtvx] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u <path to netatalk volume>\n"
-           "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n"
-           "dbd must be run with appropiate permissions i.e. as root.\n\n"
-           "Main commands are:\n"
-           "   -d Dump CNID database\n"
-           "      Option: -i dump indexes too\n\n"
-           "   -s Scan volume:\n"
-           "      Options: -c Don't check .AppleDouble stuff, only ckeck orphaned.\n"
-           "               -n Don't open CNID database, skip CNID checks.\n\n"
-           "   -r Rebuild volume:\n"
-           "      Options: -c Don't create .AppleDouble stuff, only cleanup orphaned.\n"
-           "               -f wipe database and rebuild from IDs stored in AppleDouble\n"
-           "                  metadata file or EA. Implies -e.\n\n"
-           "   -u Upgrade:\n"
-           "      Opens the database which triggers any necessary upgrades,\n"
-           "      then closes and exits.\n\n"
-           "General options:\n"
-           "   -C convert from adouble:v2 to adouble:ea (use with -r)\n"
+    printf("Usage: dbd [-cfFstuvV] <path to netatalk volume>\n\n"
+           "dbd scans all file and directories of AFP volumes, updating the\n"
+           "CNID database of the volume. dbd must be run with appropiate\n"
+           "permissions i.e. as root.\n\n"
+           "Options:\n"
+           "   -s scan volume: treat the volume as read only and don't\n"
+           "      perform any filesystem modifications\n"
+           "   -c convert from adouble:v2 to adouble:ea\n"
            "   -F location of the afp.conf config file\n"
-           "   -e only work on inactive volumes and lock them (exclusive)\n"
-           "   -x rebuild indexes (just for completeness, mostly useless!)\n"
+           "   -f delete and recreate CNID database\n"
            "   -t show statistics while running\n"
-           "   -v verbose\n\n"
-           , VERSION
+           "   -u username for use with AFP volumes using user variable $u\n"
+           "   -v verbose\n"
+           "   -V show version info\n\n"
         );
 }
 
-int main(int argc, char **argv)
+/***************************************************************************
+ * Global functions
+ ***************************************************************************/
+
+void dbd_log(enum logtype lt, char *fmt, ...)
 {
-    int c, lockfd, ret = -1;
-    int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0;
-    dbd_flags_t flags = 0;
-    char *volpath;
-    int cdir;
-    AFPObj obj = { 0 };
-    struct vol *vol;
+    int len;
+    static char logbuffer[1024];
+    va_list args;
 
-    if (geteuid() != 0) {
-        usage();
-        exit(EXIT_FAILURE);
+    if ( (lt == LOGSTD) || (flags & DBD_FLAGS_VERBOSE)) {
+        va_start(args, fmt);
+        len = vsnprintf(logbuffer, 1023, fmt, args);
+        va_end(args);
+        logbuffer[1023] = 0;
+
+        printf("%s\n", logbuffer);
     }
-    /* Inhereting perms in ad_mkdir etc requires this */
-    ad_setfuid(0);
+}
 
-    while ((c = getopt(argc, argv, ":cCdefFinrstuvx")) != -1) {
+int main(int argc, char **argv)
+{
+    EC_INIT;
+    int dbd_cmd = dbd_rebuild;
+    int cdir = -1;
+    AFPObj obj = { 0 };
+    struct vol *vol = NULL;
+    const char *volpath = NULL;
+    char *username = NULL;
+    int c;
+    while ((c = getopt(argc, argv, ":cfF:rstu:vV")) != -1) {
         switch(c) {
         case 'c':
-            flags |= DBD_FLAGS_CLEANUP;
-            break;
-        case 'C':
             flags |= DBD_FLAGS_V2TOEA;
             break;
-        case 'd':
-            dump = 1;
+        case 'f':
+            flags |= DBD_FLAGS_FORCE;
+            break;
+        case 'F':
+            obj.cmdlineconfigfile = strdup(optarg);
             break;
-        case 'i':
-            dumpindexes = 1;
+        case 'r':
+            /* the default */
             break;
         case 's':
-            scan = 1;
+            dbd_cmd = dbd_scan;
             flags |= DBD_FLAGS_SCAN;
             break;
-        case 'n':
-            nocniddb = 1; /* FIXME: this could/should be a flag too for consistency */
-            break;
-        case 'r':
-            rebuild = 1;
-            break;
         case 't':
             flags |= DBD_FLAGS_STATS;
             break;
         case 'u':
-            prep_upgrade = 1;
+            username = strdup(optarg);
             break;
         case 'v':
-            verbose = 1;
-            break;
-        case 'e':
-            exclusive = 1;
-            flags |= DBD_FLAGS_EXCL;
-            break;
-        case 'x':
-            rebuildindexes = 1;
-            break;
-        case 'f':
-            force = 1;
-            exclusive = 1;
-            flags |= DBD_FLAGS_FORCE | DBD_FLAGS_EXCL;
-            break;
-        case 'F':
-            obj.cmdlineconfigfile = strdup(optarg);
+            flags |= DBD_FLAGS_VERBOSE;
             break;
+        case 'V':
+            printf("dbd %s\n", VERSION);
+            exit(0);
         case ':':
         case '?':
             usage();
@@ -229,16 +178,18 @@ int main(int argc, char **argv)
         }
     }
 
-    if ((dump + scan + rebuild + prep_upgrade) != 1) {
+    if ( (optind + 1) != argc ) {
         usage();
         exit(EXIT_FAILURE);
     }
+    volpath = argv[optind];
 
-    if ( (optind + 1) != argc ) {
+    if (geteuid() != 0) {
         usage();
         exit(EXIT_FAILURE);
     }
-    volpath = argv[optind];
+    /* Inhereting perms in ad_mkdir etc requires this */
+    ad_setfuid(0);
 
     setvbuf(stdout, (char *) NULL, _IONBF, 0);
 
@@ -257,13 +208,29 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);
     }
 
+
+    /* Initialize CNID subsystem */
+    cnid_init();
+
     /* Setup logging. Should be portable among *NIXes */
-    if (!verbose)
-        setuplog("default:info", "/dev/tty");
+    if (flags & DBD_FLAGS_VERBOSE)
+        setuplog("default:note, cnid:debug", "/dev/tty");
     else
-        setuplog("default:debug", "/dev/tty");
+        setuplog("default:note", "/dev/tty");
+
+    /* Set username */
+    if (username) {
+        strncpy(obj.username, username, MAXUSERLEN);
+        struct passwd *pwd;
+        pwd = getpwnam(obj.username);
+        if (!pwd) {
+            dbd_log( LOGSTD, "unknown user");
+            exit(EXIT_FAILURE);
+        }
+        obj.uid = pwd->pw_uid;
+    }
 
-    if (load_volumes(&obj, NULL) != 0) {
+    if (load_volumes(&obj, lv_all) != 0) {
         dbd_log( LOGSTD, "Couldn't load volumes");
         exit(EXIT_FAILURE);
     }
@@ -278,7 +245,17 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);
     }
 
-    pack_setvol(vol);
+    /* open volume */
+    if (STRCMP(vol->v_cnidscheme, != , "dbd") && STRCMP(vol->v_cnidscheme, != , "mysql")) {
+        dbd_log(LOGSTD, "\"%s\" isn't a \"dbd\" CNID volume", vol->v_path);
+        exit(EXIT_FAILURE);
+    }
+    vol->v_cdb = cnid_open(vol, vol->v_cnidscheme,
+                           vol->v_flags & AFPVOL_NODEV ? CNID_FLAG_NODEV : 0);
+    if (vol->v_cdb == NULL) {
+        dbd_log(LOGSTD, "Cant initialize CNID database connection for %s", vol->v_path);
+        exit(EXIT_FAILURE);
+    }
 
     if (vol->v_adouble == AD_VERSION_EA)
         dbd_log( LOGDEBUG, "adouble:ea volume");
@@ -301,145 +278,28 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);        
     }
 
-    /* Enuser dbpath is there, create if necessary */
-    struct stat st;
-    if (stat(vol->v_dbpath, &st) != 0) {
-        if (errno != ENOENT) {
-            dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", vol->v_dbpath, strerror(errno));
-            exit(EXIT_FAILURE);        
+    if (flags & DBD_FLAGS_FORCE) {
+        if (cnid_wipe(vol->v_cdb) != 0) {
+            dbd_log( LOGSTD, "Failed to wipe CNID db");
+            EC_FAIL;
         }
-        if ((mkdir(vol->v_dbpath, 0755)) != 0) {
-            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", vol->v_dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }        
-    }
-
-    /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
-    if ( (strlen(vol->v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
-        dbd_log( LOGSTD, "Volume pathname too long");
-        exit(EXIT_FAILURE);        
-    }
-    strncpy(dbpath, vol->v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
-    strcat(dbpath, "/.AppleDB");
-
-    /* Check or create dbpath */
-    int dbdirfd = open(dbpath, O_RDONLY);
-    if (dbdirfd == -1 && errno == ENOENT) {
-        if (errno == ENOENT) {
-            if ((mkdir(dbpath, 0755)) != 0) {
-                dbd_log( LOGSTD, "Can't create .AppleDB for \"%s\": %s", dbpath, strerror(errno));
-                exit(EXIT_FAILURE);
-            }
-        } else {
-            dbd_log( LOGSTD, "Somethings wrong with .AppleDB for \"%s\", giving up: %s", dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-    } else {
-        close(dbdirfd);
-    }
-
-    /* Get db lock */
-    if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
-        goto exit_noenv;
-    if (db_locked != LOCK_EXCL) {
-        dbd_log(LOGDEBUG, "Database is in use, acquiring shared lock");
-        /* Couldn't get exclusive lock, try shared lock if -e wasn't requested */
-        if (exclusive) {
-            dbd_log(LOGSTD, "Database is in use and exlusive was requested");
-            goto exit_noenv;
-        }
-        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD)
-            goto exit_noenv;
-    }
-
-    /* Check if -f is requested and wipe db if yes */
-    if ((flags & DBD_FLAGS_FORCE) && rebuild) {
-        char cmd[8 + MAXPATHLEN];
-        if ((db_locked = get_lock(LOCK_FREE, NULL)) != 0)
-            goto exit_noenv;
-
-        snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
-        dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
-        system(cmd);
-        if ((mkdir(dbpath, 0755)) != 0) {
-            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        dbd_log( LOGDEBUG, "Removed old database.");
-        if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
-            goto exit_noenv;
-    }
-
-    /* 
-       Lets start with the BerkeleyDB stuff
-    */
-    if ( ! nocniddb) {
-        if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL)
-            goto exit_noenv;
-        
-        if (dbif_env_open(dbd,
-                          &db_param,
-                          (db_locked == LOCK_EXCL) ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
-            dbd_log( LOGSTD, "error opening database!");
-            goto exit_noenv;
-        }
-
-        if (db_locked == LOCK_EXCL)
-            dbd_log( LOGDEBUG, "Finished recovery.");
-
-        if (dbif_open(dbd, NULL, rebuildindexes) < 0) {
-            dbif_close(dbd);
-            goto exit_failure;
-        }
-
-        /* Prepare upgrade ? We're done */
-        if (prep_upgrade) {
-            (void)dbif_txn_close(dbd, 1);
-            goto cleanup;
-        }
-    }
-
-    /* Downgrade db lock if not running exclusive */
-    if (!exclusive && (db_locked == LOCK_EXCL)) {
-        if (get_lock(LOCK_UNLOCK, NULL) != 0)
-            goto exit_failure;
-        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD)
-            goto exit_failure;
     }
 
     /* Now execute given command scan|rebuild|dump */
-    if (dump && ! nocniddb) {
-        if (dbif_dump(dbd, dumpindexes) < 0) {
-            dbd_log( LOGSTD, "Error dumping database");
-        }
-    } else if ((rebuild && ! nocniddb) || scan) {
-        if (cmd_dbd_scanvol(dbd, vol, flags) < 0) {
+    switch (dbd_cmd) {
+    case dbd_scan:
+    case dbd_rebuild:
+        if (cmd_dbd_scanvol(vol, flags) < 0) {
             dbd_log( LOGSTD, "Error repairing database.");
         }
+        break;
     }
 
-cleanup:
-    /* Cleanup */
-    dbd_log(LOGDEBUG, "Closing db");
-    if (! nocniddb) {
-        if (dbif_close(dbd) < 0) {
-            dbd_log( LOGSTD, "Error closing database");
-            goto exit_failure;
-        }
-    }
-
-exit_success:
-    ret = 0;
-
-exit_failure:
-    if (dbif_env_remove(dbpath) < 0) {
-        dbd_log( LOGSTD, "Error removing BerkeleyDB database environment");
-        ret++;
-    }
-    get_lock(0, NULL);
+EC_CLEANUP:
+    if (vol)
+        cnid_close(vol->v_cdb);
 
-exit_noenv:    
-    if ((fchdir(cdir)) < 0)
+    if (cdir != -1 && (fchdir(cdir) < 0))
         dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
 
     if (ret == 0)
index efec636b482e464f40a1bda7ebc469dd420acb70..9fcb3f4edbef4178feb1004a7864bab7cfd520c4 100644 (file)
@@ -12,29 +12,18 @@ typedef unsigned int dbd_flags_t;
 
 #define DBD_FLAGS_SCAN     (1 << 0)
 #define DBD_FLAGS_FORCE    (1 << 1)
-#define DBD_FLAGS_EXCL     (1 << 2)
-#define DBD_FLAGS_CLEANUP  (1 << 3) /* Dont create AD stuff, but cleanup orphaned */
-#define DBD_FLAGS_STATS    (1 << 4)
-#define DBD_FLAGS_V2TOEA   (1 << 5) /* Convert adouble:v2 to adouble:ea */
+#define DBD_FLAGS_STATS    (1 << 2)
+#define DBD_FLAGS_V2TOEA   (1 << 3) /* Convert adouble:v2 to adouble:ea */
+#define DBD_FLAGS_VERBOSE  (1 << 4)
 
 #define ADv2_DIRNAME ".AppleDouble"
 
 #define DIR_DOT_OR_DOTDOT(a) \
         ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
 
-#define STRCMP(a,b,c) \
-        (strcmp(a,c) b 0)
-
-extern int nocniddb; /* Dont open CNID database, only scan filesystem */
-extern int db_locked; /* have we got the fcntl lock on lockfd ? */
 extern volatile sig_atomic_t alarmed;
 
 extern void dbd_log(enum logtype lt, char *fmt, ...);
-extern int cmd_dbd_scanvol(DBD *dbd, struct vol *vol, dbd_flags_t flags);
+extern int cmd_dbd_scanvol(struct vol *vol, dbd_flags_t flags);
 
-/*
-  Functions for querying the database which couldn't be reused from the existing
-  funcs pool of dbd_* for one reason or another
-*/
-extern int cmd_dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
 #endif /* CMD_DBD_H */
index dcbd4c74779161acb4be4f3b3feda12bc6bec771..8ce6fbf5d624989308b09269eea25d17da7b8279 100644 (file)
 #include <atalk/adouble.h>
 #include <atalk/unicode.h>
 #include <atalk/netatalk_conf.h>
-#include <atalk/cnid_dbd_private.h>
 #include <atalk/volume.h>
 #include <atalk/ea.h>
 #include <atalk/util.h>
 #include <atalk/acl.h>
 #include <atalk/compat.h>
+#include <atalk/cnid.h>
+#include <atalk/errchk.h>
 
 #include "cmd_dbd.h"
 #include "dbif.h"
 #define ADFILE_OK (adfile_ok == 0)
 
 
-static struct vol     *myvol;
 static char           cwdbuf[MAXPATHLEN+1];
-static DBD            *dbd;
-static DBD            *dbd_rebuild;
+static struct vol     *vol;
 static dbd_flags_t    dbd_flags;
 static char           stamp[CNID_DEV_LEN];
 static char           *netatalk_dirs[] = {
@@ -83,74 +82,28 @@ static char *utompath(char *upath)
     u = upath;
     outlen = strlen(upath);
 
-    if ((myvol->v_casefold & AFPVOL_UTOMUPPER))
+    if ((vol->v_casefold & AFPVOL_UTOMUPPER))
         flags |= CONV_TOUPPER;
-    else if ((myvol->v_casefold & AFPVOL_UTOMLOWER))
+    else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
         flags |= CONV_TOLOWER;
 
-    if ((myvol->v_flags & AFPVOL_EILSEQ)) {
+    if ((vol->v_flags & AFPVOL_EILSEQ)) {
         flags |= CONV__EILSEQ;
     }
 
     /* convert charsets */
-    if ((size_t)-1 == ( outlen = convert_charset(myvol->v_volcharset,
+    if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset,
                                                  CH_UTF8_MAC,
-                                                 myvol->v_maccharset,
+                                                 vol->v_maccharset,
                                                  u, outlen, mpath, MAXPATHLEN, &flags)) ) {
         dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
-                 myvol->v_volcodepage, myvol->v_maccodepage, u);
+                 vol->v_volcodepage, vol->v_maccodepage, u);
         return NULL;
     }
 
     return(m);
 }
 
-/*
-  Taken form afpd/desktop.c
-*/
-static char *mtoupath(char *mpath)
-{
-    static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
-    char    *m, *u;
-    size_t       inplen;
-    size_t       outlen;
-    u_int16_t    flags = 0;
-
-    if (!mpath)
-        return NULL;
-
-    if ( *mpath == '\0' ) {
-        return( "." );
-    }
-
-    /* set conversion flags */
-    if ((myvol->v_casefold & AFPVOL_MTOUUPPER))
-        flags |= CONV_TOUPPER;
-    else if ((myvol->v_casefold & AFPVOL_MTOULOWER))
-        flags |= CONV_TOLOWER;
-
-    if ((myvol->v_flags & AFPVOL_EILSEQ)) {
-        flags |= CONV__EILSEQ;
-    }
-
-    m = mpath;
-    u = upath;
-
-    inplen = strlen(m);
-    outlen = MAXPATHLEN;
-
-    if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
-                                                myvol->v_volcharset,
-                                                myvol->v_maccharset,
-                                                m, inplen, u, outlen, &flags)) ) {
-        dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
-                 myvol->v_volcodepage, mpath);
-        return NULL;
-    }
-
-    return( upath );
-}
-
 /*
   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
   Returns pointer to name or NULL.
@@ -186,46 +139,15 @@ static const char *check_special_dirs(const char *name)
  */
 static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname)
 {
-    int ret;
     cnid_t id;
 
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = 0;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = sp->st_dev;
-    rqst.ino = sp->st_ino;
-    rqst.type = S_ISDIR(sp->st_mode) ? 1 : 0;
-    rqst.name = (char *)oldname;
-    rqst.namelen = strlen(oldname);
-
     /* Query the database */
-    ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
-    if (dbif_txn_close(dbd, ret) != 0)
-        return -1;
-    if (rply.result != CNID_DBD_RES_OK)
+    if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID)
+        /* not in db, no need to update */
         return 0;
-    id = rply.cnid;
-
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = id;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = sp->st_dev;
-    rqst.ino = sp->st_ino;
-    rqst.type = S_ISDIR(sp->st_mode) ? 1 : 0;
-    rqst.name = (char *)newname;
-    rqst.namelen = strlen(newname);
 
     /* Update the database */
-    ret = dbd_update(dbd, &rqst, &rply);
-    if (dbif_txn_close(dbd, ret) != 0)
-        return -1;
-    if (rply.result != CNID_DBD_RES_OK)
+    if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0)
         return -1;
 
     return 0;
@@ -243,10 +165,10 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
 
     *newname = NULL;
 
-    if (myvol->v_adouble == AD_VERSION_EA) {
+    if (vol->v_adouble == AD_VERSION_EA) {
         if (!(dbd_flags & DBD_FLAGS_V2TOEA))
             return 0;
-        if (ad_convert(fname, st, myvol, newname) != 0) {
+        if (ad_convert(fname, st, vol, newname) != 0) {
             switch (errno) {
             case ENOENT:
                 break;
@@ -258,13 +180,10 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
         return 0;
     }
     
-    if (dbd_flags & DBD_FLAGS_CLEANUP)
-        return 0;
-
     if (S_ISDIR(st->st_mode))
         adflags |= ADFLAGS_DIR;
 
-    adname = myvol->ad_path(fname, adflags);
+    adname = vol->ad_path(fname, adflags);
 
     if ((ret = access( adname, F_OK)) != 0) {
         if (errno != ENOENT) {
@@ -280,7 +199,7 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
             return -1;
 
         /* Create ad file */
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
 
         if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
@@ -300,7 +219,7 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
         chmod(adname, st->st_mode);
 #endif
     } else {
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
         if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) {
             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
             return -1;
@@ -369,7 +288,7 @@ static int check_eafiles(const char *fname)
     struct stat st;
     char *eaname;
 
-    if ((ret = ea_open(myvol, fname, EA_RDWR, &ea)) != 0) {
+    if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) {
         if (errno == ENOENT)
             return 0;
         dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
@@ -422,10 +341,7 @@ static int check_addir(int volroot)
     struct adouble ad;
     char *mname = NULL;
 
-    if (dbd_flags & DBD_FLAGS_CLEANUP)
-        return 0;
-
-    if (myvol->v_adouble == AD_VERSION_EA)
+    if (vol->v_adouble == AD_VERSION_EA)
         return 0;
 
     /* Check for ad-dir */
@@ -438,10 +354,10 @@ static int check_addir(int volroot)
     }
 
     /* Check for ".Parent" */
-    if ( (adpar_ok = access(myvol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
+    if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
         if (errno != ENOENT) {
             dbd_log(LOGSTD, "Access error on '%s/%s': %s",
-                    cwdbuf, myvol->ad_path(".", ADFLAGS_DIR), strerror(errno));
+                    cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno));
             return -1;
         }
         dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
@@ -460,7 +376,7 @@ static int check_addir(int volroot)
         }
 
         /* Create ad dir and set name */
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
 
         if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
@@ -481,7 +397,7 @@ static int check_addir(int volroot)
             return -1;
         }
         chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
-        chown(myvol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
+        chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
     }
 
     return 0;
@@ -500,7 +416,7 @@ static int check_eafile_in_adouble(const char *name)
     char *namep, *namedup = NULL;
 
     /* Check if this is an AFPVOL_EA_AD vol */
-    if (myvol->v_vfs_ea == AFPVOL_EA_AD) {
+    if (vol->v_vfs_ea == AFPVOL_EA_AD) {
         /* Does the filename contain "::EA" ? */
         namedup = strdup(name);
         if ((namep = strstr(namedup, "::EA")) == NULL) {
@@ -559,7 +475,7 @@ static int read_addir(void)
     struct stat st;
 
     if ((chdir(ADv2_DIRNAME)) != 0) {
-        if (myvol->v_adouble == AD_VERSION_EA) {
+        if (vol->v_adouble == AD_VERSION_EA) {
             return 0;
         }
         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
@@ -643,49 +559,27 @@ static int read_addir(void)
 */
 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok)
 {
-    int ret, adflags = ADFLAGS_HF;
+    int adflags = ADFLAGS_HF;
     cnid_t db_cnid, ad_cnid;
     struct adouble ad;
 
     adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0);
 
-    /* Force checkout every X items */
-    static int cnidcount = 0;
-    cnidcount++;
-    if (cnidcount > 10000) {
-        cnidcount = 0;
-        if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
-            dbd_log(LOGSTD, "Error checkpointing!");
-            return CNID_INVALID;
-        }
-    }
-
     /* Get CNID from ad-file */
-    ad_cnid = 0;
+    ad_cnid = CNID_INVALID;
     if (ADFILE_OK) {
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
         if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
             
-            if (dbd_flags & DBD_FLAGS_CLEANUP)
-                return CNID_INVALID;
-
-            if (myvol->v_adouble != AD_VERSION_EA) {
+            if (vol->v_adouble != AD_VERSION_EA) {
                 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
                 return CNID_INVALID;
             }
             dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name);
             adfile_ok = 1;
         } else {
-
-            if (dbd_flags & DBD_FLAGS_FORCE) {
-                ad_cnid = ad_forcegetid(&ad);
-                /* This ensures the changed stamp is written */
-                ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
-                ad_flush(&ad);
-            } else
-                ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
-
-            if (ad_cnid == 0)
+            ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
+            if (ad_cnid == CNID_INVALID)
                 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
             else
                 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
@@ -694,128 +588,44 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
     }
 
     /* Get CNID from database */
-
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = ad_cnid;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = st->st_dev;
-    rqst.ino = st->st_ino;
-    rqst.type = S_ISDIR(st->st_mode)?1:0;
-    rqst.name = (char *)name;
-    rqst.namelen = strlen(name);
-
-    /* Query the database */
-    ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
-    if (dbif_txn_close(dbd, ret) != 0)
+    if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID)
         return CNID_INVALID;
-    if (rply.result == CNID_DBD_RES_OK) {
-        db_cnid = rply.cnid;
-    } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
-        if ( ! (dbd_flags & DBD_FLAGS_FORCE))
-            dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
-        db_cnid = 0;
-    } else {
-        dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
-        db_cnid = 0;
-    }
 
-    /* Compare results from both CNID searches */
-    if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
-        /* Everything is fine */
-        return db_cnid;
-    } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
+    /* Compare CNID from db and adouble file */
+    if (ad_cnid != db_cnid && adfile_ok == 0) {
         /* Mismatch, overwrite ad file with value from db */
-        dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            dbd_log(LOGSTD, "Updating AppleDouble file for '%s/%s' with CNID: %u from database",
-                            cwdbuf, name, ntohl(db_cnid));
-            ad_init(&ad, myvol);
-            if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
-                dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                        cwdbuf, name, strerror(errno));
-                return CNID_INVALID;
-            }
-            ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-            ad_flush(&ad);
-            ad_close(&ad, ADFLAGS_HF);
-        }
-        return db_cnid;
-    } else if (ad_cnid && (db_cnid == 0)) {
-        /* in ad-file but not in db */
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            /* Ensure the cnid from the ad-file is not already occupied by another file */
-            dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
-                    ntohl(ad_cnid));
-
-            rqst.cnid = ad_cnid;
-            ret = dbd_resolve(dbd, &rqst, &rply);
-            if (rply.result == CNID_DBD_RES_OK) {
-                /* Occupied! Choose another, update ad-file */
-                ret = dbd_add(dbd, &rqst, &rply, 1);
-                if (dbif_txn_close(dbd, ret) != 0)
-                    return CNID_INVALID;
-                db_cnid = rply.cnid;
-                dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
-
-                if (ADFILE_OK && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
-                    dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
-                            cwdbuf, name, ntohl(db_cnid));
-                    ad_init(&ad, myvol);
-                    if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
-                        dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                                cwdbuf, name, strerror(errno));
-                        return CNID_INVALID;
-                    }
-                    ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-                    ad_flush(&ad);
-                    ad_close(&ad, ADFLAGS_HF);
-                }
-                return db_cnid;
-            }
-
-            dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
-                    cwdbuf, name, ntohl(ad_cnid));
-            rqst.cnid = ad_cnid;
-            ret = dbd_rebuild_add(dbd, &rqst, &rply);
-            if (dbif_txn_close(dbd, ret) != 0)
-                return CNID_INVALID;
-        }
-        return ad_cnid;
-    } else if ((db_cnid == 0) && (ad_cnid == 0)) {
-        /* No CNID at all, we clearly have to allocate a fresh one... */
-        /* Note: the next test will use this new CNID too! */
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            /* add to db */
-            ret = dbd_add(dbd, &rqst, &rply, 1);
-            if (dbif_txn_close(dbd, ret) != 0)
-                return CNID_INVALID;
-            db_cnid = rply.cnid;
-            dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
+        dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u",
+                cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
+        ad_init(&ad, vol);
+        if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
+            dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+                    cwdbuf, name, strerror(errno));
+            return CNID_INVALID;
         }
+        ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+        ad_flush(&ad);
+        ad_close(&ad, ADFLAGS_HF);
     }
 
-    if ((ad_cnid == 0) && db_cnid) {
-        /* in db but zeroID in ad-file, write it to ad-file */
-        if (ADFILE_OK && ! (dbd_flags & DBD_FLAGS_SCAN)) {            
-            dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
-                    cwdbuf, name, ntohl(db_cnid));
-            ad_init(&ad, myvol);
-            if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
-                dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                        cwdbuf, name, strerror(errno));
-                return CNID_INVALID;
-            }
-            ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-            ad_flush(&ad);
-            ad_close(&ad, ADFLAGS_HF);
-        }
-        return db_cnid;
-    }
+    return db_cnid;
+}
 
-    return CNID_INVALID;
+static int check_orphaned(const char *name)
+{
+    int rc;
+    struct stat sb;
+
+    if (strlen(name) < 3)
+        return;
+
+    rc = lstat(&name[2], &sb);
+
+    if (rc != 0 && errno == ENOENT) {
+        dbd_log(LOGSTD, "Removing orphaned AppleDouble \"%s/%s\"", cwdbuf, name);
+        unlink(name);
+        return 1;
+    }
+    return 0;
 }
 
 /*
@@ -826,7 +636,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
 */
 static int dbd_readdir(int volroot, cnid_t did)
 {
-    int cwd, ret = 0, adfile_ok, addir_ok, encoding_ok;
+    int cwd, ret = 0, adfile_ok, addir_ok;
     cnid_t cnid = 0;
     const char *name;
     DIR *dp;
@@ -879,7 +689,7 @@ static int dbd_readdir(int volroot, cnid_t did)
         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
             continue;
 
-        if (!myvol->vfs->vfs_validupath(myvol, ep->d_name)) {
+        if (!vol->vfs->vfs_validupath(vol, ep->d_name)) {
             dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name);
             continue;
         }
@@ -893,10 +703,8 @@ static int dbd_readdir(int volroot, cnid_t did)
         switch (st.st_mode & S_IFMT) {
         case S_IFREG:
         case S_IFDIR:
-            break;
         case S_IFLNK:
-            dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
-            continue;
+            break;
         default:
             dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
@@ -927,53 +735,41 @@ static int dbd_readdir(int volroot, cnid_t did)
            Tests
         **************************************************************************/
 
+        /* Check for invalid names and orphaned ._ files */
+        if (S_ISREG(st.st_mode) && (strncmp(ep->d_name, "._", strlen("._")) == 0)) {
+            if (check_orphaned(ep->d_name))
+                continue;
+            if (vol->vfs->vfs_validupath(vol, ep->d_name)) {
+                dbd_log(LOGSTD, "Bad AppleDouble \"%s/%s\"", cwdbuf, ep->d_name);
+                continue;
+            }
+        }
+
         /* Check for appledouble file, create if missing, but only if we have addir */
         const char *name = NULL;
         adfile_ok = -1;
         if (ADDIR_OK)
             adfile_ok = check_adfile(ep->d_name, &st, &name);
 
-        if (name == NULL) {
-            name = ep->d_name;
-        } else {
-            update_cnid(did, &st, ep->d_name, name);
-        }
+        if (!S_ISLNK(st.st_mode)) {
+            if (name == NULL) {
+                name = ep->d_name;
+            } else {
+                update_cnid(did, &st, ep->d_name, name);
+            }
 
-        if ( ! nocniddb) {
             /* Check CNIDs */
             cnid = check_cnid(name, did, &st, adfile_ok);
 
-            /* Now add this object to our rebuild dbd */
-            if (cnid && dbd_rebuild) {
-                static uint count = 0;
-                rqst.cnid = rply.cnid;
-                ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
-                if (dbif_txn_close(dbd_rebuild, ret) != 0)
-                    return -1;
-                if (rply.result != CNID_DBD_RES_OK) {
-                    dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
-                             cnid, cwdbuf, name);
-                    return -1;
-                }
-                count++;
-                if (count == 10000) {
-                    if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
-                        dbd_log(LOGSTD, "Error checkpointing!");
-                        return -1;
-                    }
-                    count = 0;
-                }
-            }
+            /* Check EA files */
+            if (vol->v_vfs_ea == AFPVOL_EA_AD)
+                check_eafiles(name);
         }
 
-        /* Check EA files */
-        if (myvol->v_vfs_ea == AFPVOL_EA_AD)
-            check_eafiles(name);
-
         /**************************************************************************
           Recursion
         **************************************************************************/
-        if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
+        if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */
             strcat(cwdbuf, "/");
             strcat(cwdbuf, name);
             dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
@@ -1000,7 +796,7 @@ static int dbd_readdir(int volroot, cnid_t did)
     /*
       Use results of previous checks
     */
-    if ((myvol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
+    if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
         if (rmdir(ADv2_DIRNAME) != 0) {
             switch (errno) {
             case ENOENT:
@@ -1015,246 +811,57 @@ static int dbd_readdir(int volroot, cnid_t did)
     return ret;
 }
 
-static int scanvol(struct vol *vol, dbd_flags_t flags)
+/*
+  Main func called from cmd_dbd.c
+*/
+int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags)
 {
+    EC_INIT;
     struct stat st;
 
-    /* Make this stuff accessible from all funcs easily */
-    myvol = vol;
-    dbd_flags = flags;
-
     /* Run with umask 0 */
     umask(0);
 
-    strcpy(cwdbuf, myvol->v_path);
-    chdir(myvol->v_path);
+    /* Make vol accessible for all funcs */
+    vol = vol_in;
+    dbd_flags = flags;
+
+    /* We only support unicode volumes ! */
+    if (vol->v_volcharset != CH_UTF8) {
+        dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
+        EC_FAIL;
+    }
+
+    /*
+     * Get CNID database stamp, cnid_getstamp() passes the buffer,
+     * then cnid_resolve() actually gets the value from the db
+     */
+    cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp));
 
-    if ((myvol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
+    if (setjmp(jmp) != 0) {
+        EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */
+    }
+
+    strcpy(cwdbuf, vol->v_path);
+    chdir(vol->v_path);
+
+    if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
         if (lstat(".", &st) != 0)
-            return -1;
+            EC_FAIL;
         if (ad_convert(".", &st, vol, NULL) != 0) {
             switch (errno) {
             case ENOENT:
                 break;
             default:
-                dbd_log(LOGSTD, "Conversion error for \"%s\": %s", myvol->v_path, strerror(errno));
+                dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno));
                 break;
             }
         }
     }
 
     /* Start recursion */
-    if (dbd_readdir(1, htonl(2)) < 0)  /* 2 = volumeroot CNID */
-        return -1;
-
-    return 0;
-}
-
-/*
-  Remove all CNIDs from dbd that are not in dbd_rebuild
-*/
-static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
-{
-    int ret = 0, deleted = 0;
-    cnid_t dbd_cnid = 0, rebuild_cnid = 0;
-    struct cnid_dbd_rqst rqst;
-    struct cnid_dbd_rply rply;
-
-    /* jump over rootinfo key */
-    if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
-        return;
-    if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
-        return;
-
-    /* Get first id from dbd_rebuild */
-    if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
-        return;
-
-    /* Start main loop through dbd: get CNID from dbd */
-    while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
-        /* Check if we got a termination signal */
-        if (alarmed)
-            longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
-
-        if (deleted > 1000) {
-            deleted = 0;
-            if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
-                dbd_log(LOGSTD, "Error checkpointing!");
-                goto cleanup;
-            }
-        }
-
-        /* This should be the normal case: CNID is in both dbs */
-        if (dbd_cnid == rebuild_cnid) {
-            /* Get next CNID from rebuild db */
-            if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
-                /* Some error */
-                goto cleanup;
-            } else if (ret == 0) {
-                /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
-                while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
-                    dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
-                    if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-                        rqst.cnid = htonl(dbd_cnid);
-                        if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
-                            dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
-                            (void)dbif_txn_abort(dbd);
-                            goto cleanup;
-                        }
-                        
-                        if (dbif_txn_close(dbd, ret) != 0)
-                            return;
-                        deleted++;
-                    }
-                    /* Check if we got a termination signal */
-                    if (alarmed)
-                        longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
-                }
-                return;
-            } else
-                /* Normal case (ret=1): continue while loop */
-                continue;
-        }
-
-        if (dbd_cnid < rebuild_cnid) {
-            /* CNID is orphaned -> delete */
-            dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
-            if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-                rqst.cnid = htonl(dbd_cnid);
-                if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
-                    dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
-                    (void)dbif_txn_abort(dbd);
-                    goto cleanup;
-                }
-                if (dbif_txn_close(dbd, ret) != 0)
-                    return;
-                deleted++;
-            }
-            continue;
-        }
+    EC_NEG1( dbd_readdir(1, htonl(2)) );  /* 2 = volumeroot CNID */
 
-        if (dbd_cnid > rebuild_cnid) {
-            dbif_idwalk(dbd, NULL, 1); /* Close cursor */
-            dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
-            (void)dbif_txn_close(dbd, 2);
-            (void)dbif_txn_close(dbd_rebuild, 2);                
-            dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
-            dbif_dump(dbd_rebuild, 0);
-            dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
-            goto cleanup;
-        }
-    } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
-
-cleanup:
-    dbif_idwalk(dbd, NULL, 1); /* Close cursor */
-    dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
-    return;
-}
-
-static const char *get_tmpdb_path(void)
-{
-    pid_t pid = getpid();
-    static char path[MAXPATHLEN];
-    snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
-    if (mkdir(path, 0755) != 0)
-        return NULL;
-    return path;
-}
-
-/*
-  Main func called from cmd_dbd.c
-*/
-int cmd_dbd_scanvol(DBD *dbd_ref, struct vol *vol, dbd_flags_t flags)
-{
-    int ret = 0;
-    struct db_param db_param = { 0 };
-    const char *tmpdb_path = NULL;
-
-    /* Set cachesize for in-memory rebuild db */
-    db_param.cachesize = 64 * 1024;         /* 64 MB */
-    db_param.maxlocks = DEFAULT_MAXLOCKS;
-    db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
-    db_param.logfile_autoremove = 1;
-
-    /* Make it accessible for all funcs */
-    dbd = dbd_ref;
-
-    /* We only support unicode volumes ! */
-    if (vol->v_volcharset != CH_UTF8) {
-        dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
-        return -1;
-    }
-
-    /* Get volume stamp */
-    dbd_getstamp(dbd, &rqst, &rply);
-    if (rply.result != CNID_DBD_RES_OK) {
-        ret = -1;
-        goto exit;
-    }
-    memcpy(stamp, rply.name, CNID_DEV_LEN);
-
-    /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
-    if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) {
-        tmpdb_path = get_tmpdb_path();
-        if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
-            ret = -1;
-            goto exit;
-        }
-
-        if (dbif_env_open(dbd_rebuild,
-                          &db_param,
-                          DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
-            dbd_log(LOGSTD, "error opening tmp database!");
-            goto exit;
-        }
-
-        if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
-            ret = -1;
-            goto exit;
-        }
-
-        if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
-            ret = -1;
-            goto exit;
-        }
-    }
-
-    if (setjmp(jmp) != 0) {
-        ret = 0;                /* Got signal, jump from dbd_readdir */
-        goto exit;
-    }
-
-    /* scanvol */
-    if ((scanvol(vol, flags)) != 0) {
-        ret = -1;
-        goto exit;
-    }
-
-exit:
-    if (! nocniddb) {
-        if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0)
-            ret = -1;
-        if (dbd_rebuild)
-            if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0)
-                ret = -1;
-        if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
-            /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
-               other clients in between our pass 1 and 2 */
-            delete_orphaned_cnids(dbd, dbd_rebuild, flags);
-    }
-
-    if (dbd_rebuild) {
-        dbd_log(LOGDEBUG, "Closing tmp db");
-        dbif_close(dbd_rebuild);
-
-        if (tmpdb_path) {
-            char cmd[8 + MAXPATHLEN];
-            snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
-            dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
-            system(cmd);
-            snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);
-            system(cmd);
-        }        
-    }
-    return ret;
+EC_CLEANUP:
+    EC_EXIT;
 }
index dcf7d5faaa78b89fb68f866a979d3f5c55d7b4af..b05d72db61cf4d1d3cd76711f129ba50079d9358 100644 (file)
 
 #include <atalk/util.h>
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/paths.h>
 #include <atalk/compat.h>
 #include <atalk/errchk.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/volume.h>
 
@@ -154,8 +155,17 @@ static struct server *test_usockfn(const char *path)
     return NULL;
 }
 
-/* -------------------- */
-static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
+/**
+ * Pass connection request to existing cnid_dbd process or start a new one
+ *
+ * @param[in] obj      handle
+ * @param[in] dbdpn    Path to cnid_dbd binary
+ * @param[in] volpath  Path of AFP volume
+ * @param[in] username  Optional username, may be NULL
+ *
+ * @return 0 on success, -1 on error
+ **/
+ int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath, const char *username)
 {
     pid_t pid;
     struct server *up;
@@ -183,8 +193,8 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
     time(&t);
     if (!up) {
         /* find an empty slot (i < maxvol) or the first free slot (i == maxvol)*/
-        for (i = 0; i <= maxvol; i++) {
-            if (srv[i].v_path == NULL && i < MAXVOLS) {
+        for (i = 0; i <= maxvol && i < MAXVOLS; i++) {
+            if (srv[i].v_path == NULL) {
                 up = &srv[i];
                 if ((up->v_path = strdup(volpath)) == NULL)
                     return -1;
@@ -265,9 +275,21 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
             LOG(log_warning, logtype_cnid,
                 "Multiple attempts to start CNID db daemon for \"%s\" failed, wiping the slate clean...",
                 up->v_path);
-            ret = execlp(dbdpn, dbdpn, "-F", obj->options.configfile, "-p", volpath, "-t", buf1, "-l", buf2, "-d", NULL);
+            ret = execlp(dbdpn, dbdpn,
+                         "-F", obj->options.configfile,
+                         "-p", volpath,
+                         "-t", buf1,
+                         "-l", buf2,
+                         "-u", username,
+                         NULL);
         } else {
-            ret = execlp(dbdpn, dbdpn, "-F", obj->options.configfile, "-p", volpath, "-t", buf1, "-l", buf2, NULL);
+            ret = execlp(dbdpn, dbdpn,
+                         "-F", obj->options.configfile,
+                         "-p", volpath,
+                         "-t", buf1,
+                         "-l", buf2,
+                         "-u", username,
+                         NULL);
         }
         /* Yikes! We're still here, so exec failed... */
         LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
@@ -286,7 +308,6 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
 static int set_dbdir(const char *dbdir, const char *vpath)
 {
     EC_INIT;
-    int status;
     struct stat st;
     bstring oldpath, newpath;
     char *cmd_argv[4];
@@ -301,7 +322,7 @@ static int set_dbdir(const char *dbdir, const char *vpath)
         EC_FAIL;
     }
 
-    if (lstat(bdata(oldpath), &st) == 0 && lstat(bdata(newpath), &st) != 0 && errno == ENOENT) {
+    if (lstat(cfrombstr(oldpath), &st) == 0 && lstat(cfrombstr(newpath), &st) != 0 && errno == ENOENT) {
         /* There's an .AppleDB in the volume root, we move it */
         cmd_argv[0] = "mv";
         cmd_argv[1] = bdata(oldpath);
@@ -315,7 +336,7 @@ static int set_dbdir(const char *dbdir, const char *vpath)
 
     }
 
-    if (lstat(bdata(newpath), &st) < 0 && mkdir(bdata(newpath), 0755 ) < 0) {
+    if (lstat(cfrombstr(newpath), &st) < 0 && mkdir(cfrombstr(newpath), 0755 ) < 0) {
         LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", bdata(newpath));
         EC_FAIL;
     }
@@ -326,44 +347,6 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
-/* ------------------ */
-static uid_t user_to_uid (char *username)
-{
-    struct passwd *this_passwd;
-
-    /* check for anything */
-    if ( !username || strlen ( username ) < 1 ) return 0;
-
-    /* grab the /etc/passwd record relating to username */
-    this_passwd = getpwnam ( username );
-
-    /* return false if there is no structure returned */
-    if (this_passwd == NULL) return 0;
-
-    /* return proper uid */
-    return this_passwd->pw_uid;
-
-}
-
-/* ------------------ */
-static gid_t group_to_gid ( char *group)
-{
-    struct group *this_group;
-
-    /* check for anything */
-    if ( !group || strlen ( group ) < 1 ) return 0;
-
-    /* grab the /etc/groups record relating to group */
-    this_group = getgrnam ( group );
-
-    /* return false if there is no structure returned */
-    if (this_group == NULL) return 0;
-
-    /* return proper gid */
-    return this_group->gr_gid;
-
-}
-
 /* ------------------ */
 static void catch_child(int sig _U_) 
 {
@@ -457,11 +440,23 @@ static int setlimits(void)
     return 0;
 }
 
+static uid_t uid_from_name(const char *name)
+{
+    struct passwd *pwd;
+
+    pwd = getpwnam(name);
+    if (pwd == NULL)
+        return 0;
+    return pwd->pw_uid;
+}
+
 /* ------------------ */
 int main(int argc, char *argv[])
 {
-    char  volpath[MAXPATHLEN + 1];
-    int   len, actual_len;
+    char  *volname = NULL;
+    char  *volpath = NULL;
+    char  *username = NULL;
+    int   len[DBD_NUM_OPEN_ARGS], actual_len;
     pid_t pid;
     int   status;
     char  *dbdpn = _PATH_CNID_DBD;
@@ -471,7 +466,6 @@ int main(int argc, char *argv[])
     int    cc;
     uid_t  uid = 0;
     gid_t  gid = 0;
-    int    err = 0;
     int    debug = 0;
     int    ret;
     sigset_t set;
@@ -502,13 +496,10 @@ int main(int argc, char *argv[])
     if (afp_config_parse(&obj, "cnid_metad") != 0)
         daemon_exit(1);
 
-    if (load_volumes(&obj, NULL) != 0)
-        daemon_exit(1);
-
     (void)setlimits();
 
-    host = iniparser_getstrdup(obj.iniconfig, INISEC_GLOBAL, "cnid listen", "localhost:4700");
-    if (port = strrchr(host, ':'))
+    host = atalk_iniparser_getstrdup(obj.iniconfig, INISEC_GLOBAL, "cnid listen", "localhost:4700");
+    if ((port = strrchr(host, ':')))
         *port++ = 0;
     else
         port = DEFAULTPORT;
@@ -568,7 +559,7 @@ int main(int argc, char *argv[])
         if (rqstfd <= 0)
             continue;
 
-        ret = readt(rqstfd, &len, sizeof(int), 1, 4);
+        ret = readt(rqstfd, &len[0], sizeof(int) * DBD_NUM_OPEN_ARGS, 1, 4);
 
         if (!ret) {
             /* already close */
@@ -578,33 +569,60 @@ int main(int argc, char *argv[])
             LOG(log_severe, logtype_cnid, "error read: %s", strerror(errno));
             goto loop_end;
         }
-        else if (ret != sizeof(int)) {
+        else if (ret != DBD_NUM_OPEN_ARGS * sizeof(int)) {
             LOG(log_error, logtype_cnid, "short read: got %d", ret);
             goto loop_end;
         }
+
         /*
          *  checks for buffer overruns. The client libatalk side does it too
          *  before handing the dir path over but who trusts clients?
          */
-        if (!len || len +DBHOMELEN +2 > MAXPATHLEN) {
-            LOG(log_error, logtype_cnid, "wrong len parameter: %d", len);
+        if (!len[0] || !len[1]) {
+            LOG(log_error, logtype_cnid, "wrong len parameter: len[0]: %d, len[1]: %d", len[0], len[1]);
             goto loop_end;
         }
 
-        actual_len = readt(rqstfd, volpath, len, 1, 5);
-        if (actual_len < 0) {
-            LOG(log_severe, logtype_cnid, "Read(2) error : %s", strerror(errno));
+        volname = malloc(len[0]);
+        volpath = malloc(len[1]);
+        if (len[2]) {
+            username = malloc(len[2]);
+        }
+        if (!volname || !volpath || (len[2] && !username)) {
+            LOG(log_severe, logtype_cnid, "malloc: %s", strerror(errno));
             goto loop_end;
         }
-        if (actual_len != len) {
-            LOG(log_error, logtype_cnid, "error/short read (dir): %s", strerror(errno));
+
+        actual_len = readt(rqstfd, volname, len[0], 1, 5);
+        if (actual_len != len[0]) {
+            LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno));
             goto loop_end;
         }
-        volpath[len] = '\0';
 
-        LOG(log_debug, logtype_cnid, "main: request for volume: %s", volpath);
+        actual_len = readt(rqstfd, volpath, len[1], 1, 5);
+        if (actual_len != len[1]) {
+            LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno));
+            goto loop_end;
+        }
 
-        if (load_volumes(&obj, NULL) != 0) {
+        if (len[2]) {
+            actual_len = readt(rqstfd, username, len[2], 1, 5);
+            if (actual_len != len[2]) {
+                LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno));
+                goto loop_end;
+            }
+            strlcpy(obj.username, username, MAXUSERLEN);
+            obj.uid = uid_from_name(username);
+            if (!obj.uid)
+                goto loop_end;
+        } else {
+            obj.username[0] = 0;
+        }
+
+        LOG(log_debug, logtype_cnid, "user: %s, volume %s, path %s",
+            username ? username : "-", volname, volpath);
+
+        if (load_volumes(&obj, lv_all) != 0) {
             LOG(log_severe, logtype_cnid, "main: error reloading config");
             goto loop_end;
         }
@@ -616,12 +634,17 @@ int main(int argc, char *argv[])
 
         LOG(log_maxdebug, logtype_cnid, "main: dbpath: %s", vol->v_dbpath);
 
-        if (set_dbdir(vol->v_dbpath, volpath) < 0) {
+        if (set_dbdir(vol->v_dbpath, vol->v_path) < 0) {
             goto loop_end;
         }
 
-        maybe_start_dbd(&obj, dbdpn, vol->v_path);
+        maybe_start_dbd(&obj, dbdpn, vol->v_path, username);
+
     loop_end:
         close(rqstfd);
+        unload_volumes(&obj);
+        SAFE_FREE(volname);
+        SAFE_FREE(volpath);
+        SAFE_FREE(username);
     }
 }
index 7cd8f5598c7cff49930ac147f799e7dea1d210bb..28fab0e47b7a79d9b6315fc1e17e7063c0b09083 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <atalk/logger.h>
 #include <atalk/util.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/compat.h>
 
 #include "db_param.h"
index 46d91fc18fab1c8e97c815e91393ea73c418426e..2261777048a35887af881fc14698d72820337cff 100644 (file)
@@ -9,7 +9,7 @@
 /* number of seconds to try reading in readt */
 #define CNID_DBD_TIMEOUT 1
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 
 extern int      comm_init  (struct db_param *, int, int);
index c89f8e5cec00890c2d9b4a3e8410241cc7b684a7..3c8c102fc17cd39a16f9d09c89068b476833d9bd 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: db_param.c,v 1.9 2009-11-23 19:04:14 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (c) Frank Lahm 2009
index bfa60954037c19bbfca7d998ab34d9725f50792a..ab532d80438d68000913f545aa5de554e9eb38c1 100644 (file)
@@ -9,13 +9,13 @@
 
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 extern int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
 extern int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply);
 
-extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int nolookup);
-extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int roflag);
+extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
+extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_get(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_resolve(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_update(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
index 36c0856867b5e4c703926f34c4ac6de405ba3d94..3af72fa81aaab6f7515665bbf427e3cae03854e1 100644 (file)
@@ -15,7 +15,7 @@
 #include <sys/time.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/cnid.h>
 #include <db.h>
 
@@ -74,7 +74,7 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
 {
     static cnid_t id;
     static char buf[ROOTINFO_DATALEN];
-    DBT rootinfo_key, rootinfo_data;
+    DBT rootinfo_key, rootinfo_data, key, data;
     int rc;
     cnid_t hint;
 
@@ -83,47 +83,63 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
     rootinfo_key.data = ROOTINFO_KEY;
     rootinfo_key.size = ROOTINFO_KEYLEN;
 
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+
     if (id == 0) {
-        if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) < 0) {
+        if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) != 1) {
             rply->result = CNID_DBD_RES_ERR_DB;
             return -1;
         }
-        if (rc == 0) {
-            /* no rootinfo key yet */
-            memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+        memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
+        memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
+        id = ntohl(hint);
+        if (id < CNID_START - 1)
             id = CNID_START - 1;
-        } else {
-            memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
-            memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
-            id = ntohl(hint);
-            if (id < CNID_START - 1)
-                id = CNID_START - 1;
-        }
     }
 
-    /* If we've hit the max CNID allowed, we return an error. CNID
-     * needs to be recycled before proceding. */
-    if (++id == CNID_INVALID) {
-        rply->result = CNID_DBD_RES_ERR_MAX;
-        return -1;
+    cnid_t trycnid, tmp;
+
+    while (true) {
+        if (rply->cnid != CNID_INVALID) {
+            trycnid = ntohl(rply->cnid);
+            rply->cnid = CNID_INVALID;
+        } else {
+            if (++id == CNID_INVALID)
+                id = CNID_START;
+            trycnid = id;
+        }
+        tmp = htonl(trycnid);
+        key.data = &tmp;
+        key.size = sizeof(cnid_t);
+        rc = dbif_get(dbd, DBIF_CNID, &key, &data, 0);
+        if (rc == 0) {
+            break;
+        } else if (rc == -1) {
+            rply->result = CNID_DBD_RES_ERR_DB;
+            return -1;
+        }
     }
 
-    rootinfo_data.data = buf;
-    rootinfo_data.size = ROOTINFO_DATALEN;
-    hint = htonl(id);
-    memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
+    if (trycnid == id) {
+        rootinfo_data.data = buf;
+        rootinfo_data.size = ROOTINFO_DATALEN;
+        hint = htonl(id);
+        memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
 
-    if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
-        rply->result = CNID_DBD_RES_ERR_DB;
-        return -1;
+        if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
+            rply->result = CNID_DBD_RES_ERR_DB;
+            return -1;
+        }
     }
-    rply->cnid = hint;
+
+    rply->cnid = htonl(trycnid);
     return 0;
 }
 
 /* ------------------------ */
 /* We need a nolookup version for `dbd` */
-int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int nolookup)
+int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     rply->namelen = 0;
 
@@ -131,24 +147,25 @@ int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, in
         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
     /* See if we have an entry already and return it if yes */
-    if (! nolookup) {
-        if (dbd_lookup(dbd, rqst, rply, 0) < 0) {
-            LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): error in dbd_lookup",
-                ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-            return -1;
-        }
+    if (dbd_lookup(dbd, rqst, rply) < 0) {
+        LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): error in dbd_lookup",
+            ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+        return -1;
+    }
 
-        if (rply->result == CNID_DBD_RES_OK) {
-            /* Found it. rply->cnid is the correct CNID now. */
-            LOG(log_debug, logtype_cnid, "dbd_add: dbd_lookup success --> CNID: %u", ntohl(rply->cnid));
-            return 1;
-        }
+    if (rply->result == CNID_DBD_RES_OK) {
+        /* Found it. rply->cnid is the correct CNID now. */
+        LOG(log_debug, logtype_cnid, "dbd_add: dbd_lookup success --> CNID: %u", ntohl(rply->cnid));
+        return 1;
     }
 
     LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): {adding to database ...}",
         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-
+    if (rqst->cnid) {
+        /* rqst->cnid is the cnid "hint"/backup from the adouble file */
+        rply->cnid = rqst->cnid;
+    }
     if (get_cnid(dbd, rply) < 0) {
         if (rply->result == CNID_DBD_RES_ERR_MAX) {
             LOG(log_error, logtype_cnid, "dbd_add: FATAL: CNID database has reached its limit.");
index e621704f9339305aeac21c9e1e35555c65bdcb30..27f9021eeffc45e8075183d96680c5332d145820 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_dbcheck.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -16,7 +15,7 @@
 #include <arpa/inet.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "pack.h"
 #include "dbif.h"
index a4a3ca63e29a8c58aad3b15a1ace3e66a2793701..59109420c5f26342b39aa7495b05aaf20b556cde 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_delete.c,v 1.5 2009-07-12 09:21:34 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -14,7 +13,7 @@
 #include <arpa/inet.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "dbif.h"
 #include "dbd.h"
index 0ef71b19670c8c734ec34d3f825604559a41e682..07d82da2b12698930aad8fc942ecb99189e68d02 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_get.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -15,7 +14,7 @@
 #include <errno.h>
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 
 #include "dbif.h"
index 197a6db12087eb2f35e052ba5b0c29f30e34d398..27270c76c1b66d2dcb010e77e95218d1b2443651 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: dbd_getstamp.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -15,7 +14,7 @@
 #include <errno.h>
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "dbif.h"
 #include "dbd.h"
index 6169738b5f66e3230bb55fbb246eb38b08259d0f..9ff4e411d356115829d7f3b6959168884d367047 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_lookup.c,v 1.18 2010-01-19 14:57:11 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (C) Frank Lahm 2009
@@ -119,7 +118,8 @@ to be safe we must assign new CNIDs to both files.
 #include <arpa/inet.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
+#include <atalk/cnid.h>
 
 #include "pack.h"
 #include "dbif.h"
@@ -130,7 +130,7 @@ to be safe we must assign new CNIDs to both files.
  *  up the database if there's a problem.
  */
 
-int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
+int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     unsigned char *buf;
     DBT key, devdata, diddata;
@@ -211,16 +211,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 
                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-            if (! roflag) {
-                rqst->cnid = id_devino;
-                rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
-                if (rc < 0) {
-                    LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
-                        rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-                    return -1;
-                }
+            rqst->cnid = id_devino;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+                return -1;
             }
         }
 
@@ -230,16 +228,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 
                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-            if (! roflag) {
-                rqst->cnid = id_didname;
-                rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
-                if (rc < 0) {
-                    LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
-                        rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-                    return -1;
-                }
+            rqst->cnid = id_didname;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+                return -1;
             }
         }
 
@@ -262,15 +258,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             ntohl(rqst->did), rqst->name, ntohl(id_didname),
             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
 
-        if (! roflag) {
-            rqst->cnid = id_devino;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
+        rqst->cnid = id_devino;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
+
+        rqst->cnid = id_didname;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
 
-            rqst->cnid = id_didname;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
-        }
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
@@ -282,12 +277,11 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
             update = 1;
         } else {
-            if ( ! roflag) {
-                rqst->cnid = id_devino;
-                if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                    return -1;
-            }
+            rqst->cnid = id_devino;
+            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+                return -1;
             rply->result = CNID_DBD_RES_NOTFOUND;
+            rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
             return 1;
         }
     }
@@ -295,17 +289,16 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
     if ( ! devino) {
         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-        if ( ! roflag) {
-            rqst->cnid = id_didname;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
-        }
+        rqst->cnid = id_didname;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
         rply->result = CNID_DBD_RES_NOTFOUND;
+        rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
         return 1;
     }
 
     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
-    if (!update || roflag) {
+    if (!update) {
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
index 562838b384a28dacc46873f8742a8296a6a2eba1..3aee1c9472da11a22cab8374d5312158b12bddf4 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_rebuild_add.c,v 1.4 2009-12-23 10:18:48 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2005
  * All Rights Reserved.  See COPYING.
@@ -14,7 +13,7 @@
 #include <arpa/inet.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "pack.h"
 #include "dbif.h"
index bb3561caba7a1018605be36649d85b13abb8ab29..20ff8f7be1eb68694fa4efe60d1ef2bac590036b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_resolve.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -14,7 +13,7 @@
 #include <errno.h>
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "dbif.h"
 #include "dbd.h"
index 66979424f868f8507c5ba0368d6c330067cb43a3..685eef67d594cbb2ef191f85c1fc2af9c454d404 100644 (file)
@@ -12,7 +12,7 @@
 #include <arpa/inet.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 #include "dbif.h"
 #include "dbd.h"
index e8abf5d23dd3df7ee946b7ea8e362b2c62b4485e..7a95bfe720cb0fed4eb30d3095c1b12d59ba74db 100644 (file)
@@ -13,7 +13,7 @@
 #include <atalk/logger.h>
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 
 #include "pack.h"
index b21f259b860f40e12c71167d27c4695a9bc1bda9..3dae61cdad2f6ba5561f14708d4c37f39dc5f27c 100644 (file)
@@ -22,6 +22,7 @@
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/errchk.h>
+#include <atalk/cnid.h>
 
 #include "db_param.h"
 #include "dbif.h"
@@ -88,13 +89,11 @@ EC_CLEANUP:
 static int dbif_init_rootinfo(DBD *dbd, int version)
 {
     DBT key, data;
-    uint32_t v;
+    uint32_t uint32;
     char buf[ROOTINFO_DATALEN];
 
     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
 
-    v = version;
-    v = htonl(v);
 
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
@@ -104,7 +103,13 @@ static int dbif_init_rootinfo(DBD *dbd, int version)
     data.size = ROOTINFO_DATALEN;
 
     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
-    memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
+
+    uint32 = htonl(CNID_START - 1);
+    memcpy(buf + CNID_TYPE_OFS, &uint32, sizeof(uint32));
+
+    uint32 = htonl(version);
+    memcpy(buf + CNID_DID_OFS, &uint32, sizeof(uint32));
+
     if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
         return -1;
 
@@ -326,97 +331,6 @@ exit:
     return ret;
 }
 
-/*!
- * Get lock on db lock file
- *
- * @args cmd       (r) lock command:
- *                     LOCK_FREE:   close lockfd
- *                     LOCK_UNLOCK: unlock lockm keep lockfd open
- *                     LOCK_EXCL:   F_WRLCK on lockfd
- *                     LOCK_SHRD:   F_RDLCK on lockfd
- * @args dbpath    (r) path to lockfile, only used on first call,
- *                     later the stored fd is used
- * @returns            LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
- *                     LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
- *                     success, 0 if the lock couldn't be acquired, -1 on other errors
- */
-int get_lock(int cmd, const char *dbpath)
-{
-    static int lockfd = -1;
-    int ret;
-    char lockpath[PATH_MAX];
-    struct stat st;
-
-    LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
-        cmd == LOCK_EXCL ? "LOCK_EXCL" :
-        cmd == LOCK_SHRD ? "LOCK_SHRD" :
-        cmd == LOCK_FREE ? "LOCK_FREE" :
-        cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN" , dbpath ? dbpath : "");
-
-    switch (cmd) {
-    case LOCK_FREE:
-        if (lockfd == -1)
-            return -1;
-        close(lockfd);
-        lockfd = -1;
-        return 0;
-
-    case LOCK_UNLOCK:
-        if (lockfd == -1)
-            return -1;
-        return unlock(lockfd, 0, SEEK_SET, 0);
-
-    case LOCK_EXCL:
-    case LOCK_SHRD:
-        if (lockfd == -1) {
-            if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
-                LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
-                return -1;
-            }
-            strncpy(lockpath, dbpath, PATH_MAX - 1);
-            strcat(lockpath, "/");
-            strcat(lockpath, LOCKFILENAME);
-
-            if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
-                LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
-                return -1;
-            }
-
-            if ((stat(dbpath, &st)) != 0) {
-                LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
-                return -1;
-            }
-
-            if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
-                LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
-                         strerror(errno));
-                return -1;
-            }
-        }
-    
-        if (cmd == LOCK_EXCL)
-            ret = write_lock(lockfd, 0, SEEK_SET, 0);
-        else
-            ret = read_lock(lockfd, 0, SEEK_SET, 0);
-
-        if (ret != 0) {
-            if (cmd == LOCK_SHRD)
-                LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
-            return 0; 
-        }
-
-        LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
-            cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");    
-        return cmd;
-
-    default:
-        return -1;
-    } /* switch(cmd) */
-
-    /* deadc0de, never get here */
-    return -1;
-}
-
 /* --------------- */
 DBD *dbif_init(const char *envhome, const char *filename)
 {
@@ -813,11 +727,6 @@ int dbif_env_remove(const char *path)
 
     LOG(log_debug, logtype_cnid, "Trying to remove BerkeleyDB environment");
 
-    if (get_lock(LOCK_EXCL, path) != LOCK_EXCL) {
-        LOG(log_debug, logtype_cnid, "CNID db \"%s\" in use, not removing BerkeleyDB environment", path);
-        return 0;
-    }
-    
     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
         return -1;
 
index 8c71de36b96221f598f64f4943b0878e7906f444..a6c88a98068ad30f9535e03d6cb694c12e17e7dd 100644 (file)
@@ -64,7 +64,6 @@
 #define DBIF_IDX_DIDNAME   2
 #define DBIF_IDX_NAME      3
 
-/* get_lock cmd and return value */
 #define LOCKFILENAME  "lock"
 #define LOCK_FREE          0
 #define LOCK_UNLOCK        1
@@ -91,9 +90,6 @@ typedef struct {
     db_table db_table[DBIF_DB_CNT];
 } DBD;
 
-/* Functions */
-extern int get_lock(int cmd, const char *dbpath);
-
 extern DBD *dbif_init(const char *envhome, const char *dbname);
 extern int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags);
 extern int dbif_open(DBD *dbd, struct db_param *dbp, int reindex);
index 8ed0e5ff2bcd37796511c3094689ce3b7dfb6da8..ddcf66743c8812273b16b3ad484466ecfbcfae2b 100644 (file)
 #include <sys/file.h>
 #include <arpa/inet.h>
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/logger.h>
 #include <atalk/errchk.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/util.h>
 
 #include "db_param.h"
 #include "dbif.h"
@@ -43,6 +45,9 @@
 static DBD *dbd;
 static int exit_sig = 0;
 static int db_locked;
+static bstring dbpath;
+static struct db_param *dbp;
+static struct vol *vol;
 
 static void sig_exit(int signo)
 {
@@ -77,9 +82,205 @@ static void block_sigs_onoff(int block)
   of the cnid_dbd_rply structure contains further details.
 
 */
-#ifndef min
-#define min(a,b)        ((a)<(b)?(a):(b))
-#endif
+
+/*!
+ * Get lock on db lock file
+ *
+ * @args cmd       (r) lock command:
+ *                     LOCK_FREE:   close lockfd
+ *                     LOCK_UNLOCK: unlock lockm keep lockfd open
+ *                     LOCK_EXCL:   F_WRLCK on lockfd
+ *                     LOCK_SHRD:   F_RDLCK on lockfd
+ * @args dbpath    (r) path to lockfile, only used on first call,
+ *                     later the stored fd is used
+ * @returns            LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
+ *                     LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
+ *                     success, 0 if the lock couldn't be acquired, -1 on other errors
+ */
+static int get_lock(int cmd, const char *dbpath)
+{
+    static int lockfd = -1;
+    int ret;
+    char lockpath[PATH_MAX];
+    struct stat st;
+
+    LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
+        cmd == LOCK_EXCL ? "LOCK_EXCL" :
+        cmd == LOCK_SHRD ? "LOCK_SHRD" :
+        cmd == LOCK_FREE ? "LOCK_FREE" :
+        cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN",
+        dbpath ? dbpath : "");
+
+    switch (cmd) {
+    case LOCK_FREE:
+        if (lockfd == -1)
+            return -1;
+        close(lockfd);
+        lockfd = -1;
+        return 0;
+
+    case LOCK_UNLOCK:
+        if (lockfd == -1)
+            return -1;
+        return unlock(lockfd, 0, SEEK_SET, 0);
+
+    case LOCK_EXCL:
+    case LOCK_SHRD:
+        if (lockfd == -1) {
+            if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
+                LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
+                return -1;
+            }
+            strncpy(lockpath, dbpath, PATH_MAX - 1);
+            strcat(lockpath, "/");
+            strcat(lockpath, LOCKFILENAME);
+
+            if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
+                LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
+                return -1;
+            }
+
+            if ((stat(dbpath, &st)) != 0) {
+                LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
+                return -1;
+            }
+
+            if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
+                LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
+                         strerror(errno));
+                return -1;
+            }
+        }
+    
+        if (cmd == LOCK_EXCL)
+            ret = write_lock(lockfd, 0, SEEK_SET, 0);
+        else
+            ret = read_lock(lockfd, 0, SEEK_SET, 0);
+
+        if (ret != 0) {
+            if (cmd == LOCK_SHRD)
+                LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
+            return 0; 
+        }
+
+        LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
+            cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");    
+        return cmd;
+
+    default:
+        return -1;
+    } /* switch(cmd) */
+
+    /* deadc0de, never get here */
+    return -1;
+}
+
+static int open_db(void)
+{
+    EC_INIT;
+
+    /* Get db lock */
+    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        EC_FAIL;
+    }
+
+    if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
+        EC_FAIL;
+
+    /* Only recover if we got the lock */
+    if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
+
+    if (dbif_open(dbd, dbp, 0) < 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (dbd) {
+            (void)dbif_close(dbd);
+            dbd = NULL;
+        }
+    }
+
+    EC_EXIT;
+}
+
+static int delete_db(void)
+{
+    EC_INIT;
+    int cwd = -1;
+
+    EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) );
+    EC_NEG1( cwd = open(".", O_RDONLY) );
+    chdir(cfrombstr(dbpath));
+    system("rm -f cnid2.db lock log.* __db.*");
+
+    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        EC_FAIL;
+    }
+
+    LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname);
+
+EC_CLEANUP:
+    if (cwd != -1) {
+        fchdir(cwd);
+        close(cwd);
+    }
+    EC_EXIT;
+}
+
+
+/**
+ * Close dbd if open, delete it, reopen
+ *
+ * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
+ * and last used CNID
+ **/
+static int reinit_db(void)
+{
+    EC_INIT;
+    DBT key, data;
+    bool copyRootInfo = false;
+
+    if (dbd) {
+        memset(&key, 0, sizeof(key));
+        memset(&data, 0, sizeof(data));
+
+        key.data = ROOTINFO_KEY;
+        key.size = ROOTINFO_KEYLEN;
+
+        if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
+            LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
+            copyRootInfo = false;
+        } else {
+            copyRootInfo = true;
+        }
+        (void)dbif_close(dbd);
+    }
+
+    EC_ZERO_LOG( delete_db() );
+    EC_ZERO_LOG( open_db() );
+
+    if (copyRootInfo == true) {
+        memset(&key, 0, sizeof(key));
+        key.data = ROOTINFO_KEY;
+        key.size = ROOTINFO_KEYLEN;
+
+        if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
+            LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
+            EC_FAIL;
+        }
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
 
 static int loop(struct db_param *dbp)
 {
@@ -110,7 +311,7 @@ static int loop(struct db_param *dbp)
         dbp->flush_interval, timebuf);
 
     while (1) {
-        timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
+        timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout);
         if (timeout > now)
             timeout -= now;
         else
@@ -145,7 +346,7 @@ static int loop(struct db_param *dbp)
                 ret = 1;
                 break;
             case CNID_DBD_OP_ADD:
-                ret = dbd_add(dbd, &rqst, &rply, 0);
+                ret = dbd_add(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_GET:
                 ret = dbd_get(dbd, &rqst, &rply);
@@ -154,7 +355,7 @@ static int loop(struct db_param *dbp)
                 ret = dbd_resolve(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_LOOKUP:
-                ret = dbd_lookup(dbd, &rqst, &rply, 0);
+                ret = dbd_lookup(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_UPDATE:
                 ret = dbd_update(dbd, &rqst, &rply);
@@ -171,6 +372,9 @@ static int loop(struct db_param *dbp)
             case CNID_DBD_OP_SEARCH:
                 ret = dbd_search(dbd, &rqst, &rply);
                 break;
+            case CNID_DBD_OP_WIPE:
+                ret = reinit_db();
+                break;
             default:
                 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
                 ret = -1;
@@ -270,22 +474,30 @@ static void set_signal(void)
     }
 }
 
+static uid_t uid_from_name(const char *name)
+{
+    struct passwd *pwd;
+
+    pwd = getpwnam(name);
+    if (pwd == NULL)
+        return 0;
+    return pwd->pw_uid;
+}
+
 /* ------------------------ */
 int main(int argc, char *argv[])
 {
     EC_INIT;
-    struct db_param *dbp;
     int delete_bdb = 0;
     int ctrlfd = -1, clntfd = -1;
-    char *logconfig;
     AFPObj obj = { 0 };
-    struct vol *vol;
     char *volpath = NULL;
-    bstring dbpath;
+    char *username = NULL;
 
-    while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
+    while (( ret = getopt( argc, argv, ":dF:l:p:t:u:vV")) != -1 ) {
         switch (ret) {
         case 'd':
+            /* this is now just ignored, as we do it automatically anyway */
             delete_bdb = 1;
             break;
         case 'F':
@@ -300,10 +512,15 @@ int main(int argc, char *argv[])
         case 't':
             ctrlfd = atoi(optarg);
             break;
+        case 'u':
+            username = strdup(optarg);
+            break;
         case 'v':
         case 'V':
             printf("cnid_dbd (Netatalk %s)\n", VERSION);
             return -1;
+        case ':':
+            break;
         }
     }
 
@@ -314,7 +531,18 @@ int main(int argc, char *argv[])
 
     EC_ZERO( afp_config_parse(&obj, "cnid_dbd") );
 
-    EC_ZERO( load_volumes(&obj, NULL) );
+    if (username) {
+        strlcpy(obj.username, username, MAXUSERLEN);
+        obj.uid = uid_from_name(username);
+        if (!obj.uid) {
+            EC_FAIL_LOG("unknown user: '%s'", username);
+        }
+    }
+
+    LOG(log_debug, logtype_cnid, "user: %s, path %s",
+        username ? username : "-", volpath);
+
+    EC_ZERO( load_volumes(&obj, lv_all) );
     EC_NULL( vol = getvolbypath(&obj, volpath) );
     EC_ZERO( load_charset(vol) );
     pack_setvol(vol);
@@ -326,29 +554,6 @@ int main(int argc, char *argv[])
 
     switch_to_user(bdata(dbpath));
 
-    /* Get db lock */
-    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) == -1) {
-        LOG(log_error, logtype_cnid, "main: fatal db lock error");
-        EC_FAIL;
-    }
-    if (db_locked != LOCK_EXCL) {
-        /* Couldn't get exclusive lock, try shared lock  */
-        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
-            LOG(log_error, logtype_cnid, "main: fatal db lock error");
-            EC_FAIL;
-        }
-    }
-
-    if (delete_bdb && (db_locked == LOCK_EXCL)) {
-        LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
-        chdir(bdata(dbpath));
-        system("rm -f cnid2.db lock log.* __db.*");
-        if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
-            LOG(log_error, logtype_cnid, "main: fatal db lock error");
-            EC_FAIL;
-        }
-    }
-
     set_signal();
 
     /* SIGINT and SIGTERM are always off, unless we are in pselect */
@@ -358,37 +563,11 @@ int main(int argc, char *argv[])
         EC_FAIL;
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
-        EC_FAIL;
-
-    /* Only recover if we got the lock */
-    if (dbif_env_open(dbd,
-                      dbp,
-                      (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
-        EC_FAIL;
-    LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
-
-    if (dbif_open(dbd, dbp, 0) < 0) {
-        ret = -1;
-        goto close_db;
-    }
-
-    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
-
-    /* Downgrade db lock  */
-    if (db_locked == LOCK_EXCL) {
-        if (get_lock(LOCK_UNLOCK, NULL) != 0) {
-            ret = -1;
-            goto close_db;
-        }
-
-        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
-            ret = -1;
-            goto close_db;
-        }
+    if (open_db() != 0) {
+        LOG(log_error, logtype_cnid, "Failed to open CNID database for volume \"%s\"", vol->v_localname);
+        EC_ZERO_LOG( reinit_db() );
     }
 
-
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
         ret = -1;
         goto close_db;
index 2e9a32eec6a62234ad6cee93f084794d9accd620..dc61ddedede532df09531bf054d7f153c1c66251 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/volume.h>
 #include "pack.h"
 
index 9894d49a0a32a38aa27b2549227517b404af4d10..5566bea2520f921185fb3dcdccd24d2b92c4b00e 100644 (file)
@@ -8,7 +8,7 @@
 #define CNID_DBD_PACK_H 1
 
 #include <db.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 extern unsigned char *pack_cnid_data(struct cnid_dbd_rqst *);
 extern int didname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
index ee4d4327a77100bc247b7b28f7848266542c3338..44f01ae2292d48775e47155341b7fd882413119c 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: usockfd.h,v 1.5 2009-11-05 14:38:07 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -10,7 +9,7 @@
 
 
 
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 
 
 extern int      usockfd_create  (char *, mode_t, int);
index 06e5a8c359134f366f170ae4258dab02f94ed0bb..ca3e163f68ce3d2c390cf8492325731aa7e94bcf 100644 (file)
@@ -7,10 +7,12 @@ sbin_PROGRAMS = netatalk
 netatalk_SOURCES = netatalk.c
 netatalk_CFLAGS = \
        -D_PATH_CONFDIR=\"$(pkgconfdir)/\" \
+       -D_PATH_STATEDIR='"$(localstatedir)/netatalk/"' \
        -D_PATH_AFPD=\"$(sbindir)/afpd\" \
        -D_PATH_CNID_METAD=\"$(sbindir)/cnid_metad\"
 
 netatalk_LDADD = \
+       @MYSQL_LIBS@ \
        $(top_builddir)/libatalk/libatalk.la
 
 netatalk_LDFLAGS =
index b35d264c81819787dbd2197d7f5de21bb640926a..1ce7befca67d66073dbac94b16fe7924fb4ef746 100644 (file)
@@ -1,8 +1,16 @@
-/*
- * Copyright (c) 1990,1993 Regents of The University of Michigan.
- * All Rights Reserved.  See COPYRIGHT.
- */
-
+/* 
+   Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+   
+   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
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 #include <atalk/errchk.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include <event2/event.h>
 
 /* how many seconds we wait to shutdown from SIGTERM before we send SIGKILL */
 #define KILL_GRACETIME 5
 
+/* defines that control whether services should run by default */
+#define NETATALK_SRV_NEEDED  -1
+#define NETATALK_SRV_OPTIONAL 0
+#define NETATALK_SRV_ERROR    NETATALK_SRV_NEEDED
+
 /* forward declaration */
 static pid_t run_process(const char *path, ...);
 static void kill_childs(int sig, ...);
 
 /* static variables */
 static AFPObj obj;
-static sig_atomic_t got_chldsig;
-static pid_t afpd_pid = -1,  cnid_metad_pid = -1;
-static uint afpd_restarts, cnid_metad_restarts;
+static pid_t afpd_pid = NETATALK_SRV_NEEDED;
+static pid_t cnid_metad_pid = NETATALK_SRV_NEEDED;
+static pid_t dbus_pid = NETATALK_SRV_OPTIONAL;
+static uint afpd_restarts, cnid_metad_restarts, dbus_restarts;
 static struct event_base *base;
 struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *timer_ev;
 static int in_shutdown;
+static const char *dbus_path;
+
+/******************************************************************
+ * Misc stuff
+ ******************************************************************/
+
+static bool service_running(pid_t pid)
+{
+    if ((pid != NETATALK_SRV_NEEDED) && (pid != NETATALK_SRV_OPTIONAL))
+        return true;
+    return false;
+}
+
+/* Set Tracker Miners to index all our volumes */
+static int set_sl_volumes(void)
+{
+    EC_INIT;
+    const struct vol *volumes, *vol;
+    struct bstrList *vollist = bstrListCreate();
+    bstring sep = bfromcstr(", ");
+    bstring volnamelist = NULL, cmd = NULL;
+
+    EC_NULL_LOG( volumes = getvolumes() );
+
+    for (vol = volumes; vol; vol = vol->v_next) {
+        if (vol->v_flags & AFPVOL_SPOTLIGHT) {
+            bstring volnamequot = bformat("'%s'", vol->v_path);
+            bstrListPush(vollist, volnamequot);
+        }
+    }
+
+    volnamelist = bjoin(vollist, sep);
+    cmd = bformat("gsettings set org.freedesktop.Tracker.Miner.Files index-recursive-directories \"[%s]\"",
+                  bdata(volnamelist) ? bdata(volnamelist) : "");
+    LOG(log_debug, logtype_sl, "set_sl_volumes: %s", bdata(cmd));
+    system(bdata(cmd));
+
+    /* Disable default root user home indexing */
+    system("gsettings set org.freedesktop.Tracker.Miner.Files index-single-directories \"[]\"");
+
+EC_CLEANUP:
+    if (cmd)
+        bdestroy(cmd);
+    if (sep)
+        bdestroy(sep);
+    if (vollist)
+        bstrListDestroy(vollist);
+    if (volnamelist)
+        bdestroy(volnamelist);
+    EC_EXIT;
+}
 
 /******************************************************************
  * libevent helper functions
@@ -106,72 +173,97 @@ static void sigterm_cb(evutil_socket_t fd, short what, void *arg)
     event_del(sigquit_ev);
     event_del(timer_ev);
 
-    kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, NULL);
+#ifdef HAVE_TRACKER_SPARQL
+    system("tracker-control -t");
+#endif
+    kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
 }
 
 /* SIGQUIT callback */
 static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
 {
     LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
-    kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, NULL);
+#ifdef HAVE_TRACKER_SPARQL
+    system("tracker-control -t");
+#endif
+    kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
+}
+
+/* SIGHUP callback */
+static void sighup_cb(evutil_socket_t fd, short what, void *arg)
+{
+    LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
+    kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, NULL);
 }
 
 /* SIGCHLD callback */
 static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
 {
-    int status, i;
+    int status;
     pid_t pid;
 
-    LOG(log_debug, logtype_afpd, "Got SIGCHLD event");
-  
     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
         if (WIFEXITED(status)) {
             if (WEXITSTATUS(status))
-                LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+                LOG(log_info, logtype_default, "child[%d]: exited %d", pid, WEXITSTATUS(status));
             else
-                LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+                LOG(log_info, logtype_default, "child[%d]: done", pid);
         } else {
             if (WIFSIGNALED(status))
-                LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+                LOG(log_info, logtype_default, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
             else
-                LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+                LOG(log_info, logtype_default, "child[%d]: died", pid);
         }
 
         if (pid == afpd_pid)
-            afpd_pid = -1;
-        else if (pid = cnid_metad_pid)
-            cnid_metad_pid = -1;
+            afpd_pid = NETATALK_SRV_ERROR;
+        else if (pid == cnid_metad_pid)
+            cnid_metad_pid = NETATALK_SRV_ERROR;
+        else if (pid == dbus_pid)
+            dbus_pid = NETATALK_SRV_ERROR;
         else
             LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
     }
 
-    if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1)
+    if (in_shutdown
+        && !service_running(afpd_pid)
+        && !service_running(cnid_metad_pid)
+        && !service_running(dbus_pid)) {
         event_base_loopbreak(base);
+    }
 }
 
 /* timer callback */
 static void timer_cb(evutil_socket_t fd, short what, void *arg)
 {
-    static int i = 0;
-
     if (in_shutdown)
         return;
 
-    if (afpd_pid == -1) {
+    if (afpd_pid == NETATALK_SRV_NEEDED) {
         afpd_restarts++;
         LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
         if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-            LOG(log_error, logtype_afpd, "Error starting 'afpd'");
+            LOG(log_error, logtype_default, "Error starting 'afpd'");
         }
     }
 
-    if (cnid_metad_pid == -1) {
+    if (cnid_metad_pid == NETATALK_SRV_NEEDED) {
         cnid_metad_restarts++;
         LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
         if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-            LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
+            LOG(log_error, logtype_default, "Error starting 'cnid_metad'");
+        }
+    }
+
+#ifdef HAVE_TRACKER
+    if (dbus_pid == NETATALK_SRV_NEEDED) {
+        dbus_restarts++;
+        LOG(log_note, logtype_afpd, "Restarting 'dbus' (restarts: %u)", dbus_restarts);
+        if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == -1) {
+            LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
         }
     }
+#endif
 }
 
 /******************************************************************
@@ -187,7 +279,7 @@ static void kill_childs(int sig, ...)
     va_start(args, sig);
 
     while ((pid = va_arg(args, pid_t *)) != NULL) {
-        if (*pid == -1)
+        if (*pid == NETATALK_SRV_ERROR || *pid == NETATALK_SRV_OPTIONAL)
             continue;
         kill(*pid, sig);
     }
@@ -197,15 +289,16 @@ static void kill_childs(int sig, ...)
 /* this get called when error conditions are met that require us to exit gracefully */
 static void netatalk_exit(int ret)
 {
-    server_unlock(_PATH_NETATALK_LOCK);
+    server_unlock(PATH_NETATALK_LOCK);
     exit(ret);
 }
 
 /* this forks() and exec() "path" with varags as argc[] */
 static pid_t run_process(const char *path, ...)
 {
-    int ret, i = 0;
-    char *myargv[10];
+    int i = 0;
+#define MYARVSIZE 64
+    char *myargv[MYARVSIZE];
     va_list args;
     pid_t pid;
 
@@ -217,11 +310,13 @@ static pid_t run_process(const char *path, ...)
     if (pid == 0) {
         myargv[i++] = (char *)path;
         va_start(args, path);
-        while ((myargv[i++] = va_arg(args, char *)) != NULL)
-            ;
+        while (i < MYARVSIZE) {
+            if ((myargv[i++] = va_arg(args, char *)) == NULL)
+                break;
+        }
         va_end(args);
 
-        ret = execv(path, myargv);
+        (void)execv(path, myargv);
 
         /* Yikes! We're still here, so exec failed... */
         LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
@@ -237,7 +332,6 @@ static void usage(void)
 
 int main(int argc, char **argv)
 {
-    const char *configfile = NULL;
     int c, ret, debug = 0;
     sigset_t blocksigs;
     struct timeval tv;
@@ -259,13 +353,13 @@ int main(int argc, char **argv)
         }
     }
 
-    if (check_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+    if (check_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
         exit(EXITERR_SYS);
 
     if (!debug && daemonize(0, 0) != 0)
         exit(EXITERR_SYS);
 
-    if (create_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+    if (create_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
         exit(EXITERR_SYS);
 
     sigfillset(&blocksigs);
@@ -274,17 +368,19 @@ int main(int argc, char **argv)
     if (afp_config_parse(&obj, "netatalk") != 0)
         netatalk_exit(EXITERR_CONF);
 
+    load_volumes(&obj, lv_all);
+
     event_set_log_callback(libevent_logmsg_cb);
     event_set_fatal_callback(netatalk_exit);
 
     LOG(log_note, logtype_default, "Netatalk AFP server starting");
 
-    if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-        LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
+    if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == NETATALK_SRV_ERROR) {
+        LOG(log_error, logtype_afpd, "Error starting 'afpd'");
         netatalk_exit(EXITERR_CONF);
     }
 
-    if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
+    if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == NETATALK_SRV_ERROR) {
         LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
         netatalk_exit(EXITERR_CONF);
     }
@@ -296,6 +392,7 @@ int main(int argc, char **argv)
 
     sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
     sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL | EV_PERSIST, sigquit_cb, NULL);
+    sigquit_ev = event_new(base, SIGHUP,  EV_SIGNAL | EV_PERSIST, sighup_cb, NULL);
     sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
     timer_ev = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
 
@@ -311,17 +408,48 @@ int main(int argc, char **argv)
     sigdelset(&blocksigs, SIGTERM);
     sigdelset(&blocksigs, SIGQUIT);
     sigdelset(&blocksigs, SIGCHLD);
+    sigdelset(&blocksigs, SIGHUP);
     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
 
+#ifdef HAVE_TRACKER
+    if (obj.options.flags & OPTION_SPOTLIGHT) {
+        setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=" _PATH_STATEDIR "spotlight.ipc", 1);
+        setenv("XDG_DATA_HOME", _PATH_STATEDIR, 0);
+        setenv("XDG_CACHE_HOME", _PATH_STATEDIR, 0);
+        setenv("TRACKER_USE_LOG_FILES", "1", 0);
+
+        if (atalk_iniparser_getboolean(obj.iniconfig, INISEC_GLOBAL, "start dbus", 1)) {
+            dbus_path = atalk_iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon", DBUS_DAEMON_PATH);
+            LOG(log_debug, logtype_default, "DBUS: '%s'", dbus_path);
+            if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == NETATALK_SRV_ERROR) {
+                LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
+                netatalk_exit(EXITERR_CONF);
+            }
+
+            /* Allow dbus some time to start up */
+            sleep(1);
+        }
+
+        set_sl_volumes();
+
+        if (atalk_iniparser_getboolean(obj.iniconfig, INISEC_GLOBAL, "start tracker", 1)) {
+            system(TRACKER_PREFIX "/bin/tracker-control -s");
+        }
+    }
+#endif
+
+
     /* run the event loop */
     ret = event_base_dispatch(base);
 
-    if (afpd_pid != -1 || cnid_metad_pid != -1) {
-        if (afpd_pid != -1)
+    if (service_running(afpd_pid) || service_running(cnid_metad_pid) || service_running(dbus_pid)) {
+        if (service_running(afpd_pid))
             LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
-        if (cnid_metad_pid != -1)
+        if (service_running(cnid_metad_pid))
             LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
-        kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, NULL);
+        if (service_running(dbus_pid))
+            LOG(log_error, logtype_afpd, "DBUS session daemon still running, killing it");
+        kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
     }
 
     LOG(log_note, logtype_afpd, "Netatalk AFP server exiting");
diff --git a/etc/spotlight/.gitignore b/etc/spotlight/.gitignore
new file mode 100644 (file)
index 0000000..4babbdf
--- /dev/null
@@ -0,0 +1,10 @@
+Makefile
+Makefile.in
+spot
+stp
+srp
+.deps
+.libs
+*.o
+*.la
+*.lo
\ No newline at end of file
diff --git a/etc/spotlight/Makefile.am b/etc/spotlight/Makefile.am
new file mode 100644 (file)
index 0000000..60e0c25
--- /dev/null
@@ -0,0 +1,38 @@
+# Makefile.am for etc/spotlight/
+
+pkgconfdir = @PKGCONFDIR@
+moduledir = @UAMS_PATH@
+module_LTLIBRARIES =
+noinst_PROGRAMS =
+noinst_HEADERS = slmod_sparql_map.h
+BUILT_SOURCES =
+
+AM_YFLAGS = -d
+
+if HAVE_TRACKER_SPARQL
+BUILT_SOURCES += slmod_sparql_parser.h
+noinst_PROGRAMS += srp
+module_LTLIBRARIES += slmod_sparql.la
+
+slmod_sparql_la_SOURCES = \
+       slmod_sparql.c \
+       slmod_sparql_map.c \
+       slmod_sparql_parser.y \
+       spotlight_rawquery_lexer.l
+
+slmod_sparql_la_CFLAGS  = \
+       -DDBUS_API_SUBJECT_TO_CHANGE \
+       @TRACKER_CFLAGS@ \
+       @TRACKER_MINER_CFLAGS@ \
+       -D_PATH_STATEDIR='"$(localstatedir)/netatalk"'
+
+slmod_sparql_la_LDFLAGS = -module -avoid-version @TRACKER_LIBS@ @TRACKER_MINER_LIBS@
+
+srp_SOURCES = \
+       slmod_sparql_map.c \
+       slmod_sparql_parser.y \
+       spotlight_rawquery_lexer.l
+
+srp_CFLAGS = -DMAIN -I$(top_srcdir)/include @TRACKER_CFLAGS@
+srp_LDADD = $(top_builddir)/libatalk/libatalk.la @MYSQL_LIBS@
+endif
diff --git a/etc/spotlight/slmod_sparql.c b/etc/spotlight/slmod_sparql.c
new file mode 100644 (file)
index 0000000..21e8d38
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <locale.h>
+
+#include <gio/gio.h>
+#include <tracker-sparql.h>
+#include <libtracker-miner/tracker-miner.h>
+
+#include <atalk/util.h>
+#include <atalk/errchk.h>
+#include <atalk/logger.h>
+#include <atalk/unix.h>
+#include <atalk/spotlight.h>
+
+#include "slmod_sparql_parser.h"
+
+#define MAX_SL_RESULTS 20
+
+static TrackerSparqlConnection *connection;
+#if 0
+static TrackerMinerManager *manager;
+#endif
+
+static char *tracker_to_unix_path(const char *uri)
+{
+    EC_INIT;
+    GFile *f = NULL;
+    char *path = NULL;
+
+    EC_NULL_LOG( f = g_file_new_for_uri(uri) );
+    EC_NULL_LOG( path = g_file_get_path(f) );
+
+EC_CLEANUP:
+    if (f)
+        g_object_unref(f);
+    if (ret != 0)
+        return NULL;
+    return path;
+}
+
+static int sl_mod_init(void *p)
+{
+    EC_INIT;
+    GError *error = NULL;
+    AFPObj *obj = (AFPObj *)p;
+    const char *attributes;
+
+    LOG(log_info, logtype_sl, "Initializing Spotlight module");
+
+    g_type_init();
+    setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=" _PATH_STATEDIR "/spotlight.ipc", 1);
+    setenv("TRACKER_SPARQL_BACKEND", "bus", 1);
+
+#ifdef DEBUG
+    setenv("TRACKER_VERBOSITY", "3", 1);
+    dup2(type_configs[logtype_sl].fd, 1);
+    dup2(type_configs[logtype_sl].fd, 2);
+#endif
+
+    become_root();
+    connection = tracker_sparql_connection_get(NULL, &error);
+#if 0 /* this may hang, so disable it as we don't use the miner anyway  */
+    manager = tracker_miner_manager_new_full(FALSE, &error);
+#endif
+    unbecome_root();
+
+    if (!connection) {
+        LOG(log_error, logtype_sl, "Couldn't obtain a direct connection to the Tracker store: %s",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+
+#if 0
+    if (!manager) {
+        LOG(log_error, logtype_sl, "Couldn't connect to Tracker miner");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+#endif
+
+    attributes = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "spotlight attributes", NULL);
+    if (attributes) {
+        configure_spotlight_attributes(attributes);
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+
+static void tracker_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    slq_t *slq = user_data;
+    TrackerSparqlCursor *cursor;
+    GError *error = NULL;
+
+    LOG(log_debug, logtype_sl, "tracker_cb");
+
+    cursor = tracker_sparql_connection_query_finish(connection, res, &error);
+
+    if (error) {
+        LOG(log_error, logtype_sl, "sl_mod_fetch_result: Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        return;
+    }
+
+    slq->slq_tracker_cursor = cursor;
+}
+
+static int sl_mod_start_search(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p; 
+    gchar *sparql_query;
+    GError *error = NULL;
+
+    LOG(log_debug, logtype_sl, "sl_mod_start_search: Spotlight query string: \"%s\"", slq->slq_qstring);
+
+    EC_ZERO_LOGSTR( map_spotlight_to_sparql_query(slq, &sparql_query),
+                    "Mapping Spotlight query failed: \"%s\"", slq->slq_qstring );
+    LOG(log_debug, logtype_sl, "sl_mod_start_search: SPARQL query: \"%s\"", sparql_query);
+
+#if 0
+    /* Start the async query */
+    tracker_sparql_connection_query_async(connection, sparql_query, NULL, tracker_cb, slq);
+#endif
+
+    become_root();
+    slq->slq_tracker_cursor = tracker_sparql_connection_query(connection, sparql_query, NULL, &error);
+    unbecome_root();
+
+    if (error) {
+        LOG(log_error, logtype_sl, "Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error(&error);
+        EC_FAIL;
+    }
+    slq->slq_state = SLQ_STATE_RUNNING;
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int add_filemeta(sl_array_t *reqinfo, sl_array_t *fm_array, cnid_t id, const char *path, const struct stat *sp)
+{
+    EC_INIT;
+    sl_array_t *meta;
+    sl_nil_t nil = 0;
+    int i, metacount;
+    uint64_t uint64;
+    sl_time_t sl_time;
+
+    if ((metacount = talloc_array_length(reqinfo->dd_talloc_array)) == 0) {
+        dalloc_add_copy(fm_array, &nil, sl_nil_t);
+        goto EC_CLEANUP;
+    }
+
+    LOG(log_debug, logtype_sl, "add_filemeta: metadata count: %d", metacount);
+
+    meta = talloc_zero(fm_array, sl_array_t);
+
+    for (i = 0; i < metacount; i++) {
+        if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemDisplayName") ||
+            STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSName")) {
+            char *p, *name;
+            if ((p = strrchr(path, '/'))) {
+                name = dalloc_strdup(meta, p + 1);
+                dalloc_add(meta, name, "char *");
+            }
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSSize")) {
+            uint64 = sp->st_size;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSOwnerUserID")) {
+            uint64 = sp->st_uid;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSOwnerGroupID")) {
+            uint64 = sp->st_gid;
+            dalloc_add_copy(meta, &uint64, uint64_t);
+        } else if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemFSContentChangeDate")) {
+            sl_time.tv_sec = sp->st_mtime;
+            dalloc_add_copy(meta, &sl_time, sl_time_t);
+        } else {
+            dalloc_add_copy(meta, &nil, sl_nil_t);
+        }
+    }
+
+    dalloc_add(fm_array, meta, sl_array_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int cnid_cmp_fn(const void *p1, const void *p2)
+{
+    const uint64_t *cnid1 = p1, *cnid2 = p2;
+    if (*cnid1 == *cnid2)
+        return 0;
+    if (*cnid1 < *cnid2)
+        return -1;
+    else
+        return 1;            
+}
+
+static int sl_mod_fetch_result(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+    GError *error = NULL;
+    int i = 0;
+    cnid_t did, id;
+    const gchar *uri;
+    char *path;
+    sl_cnids_t *cnids;
+    sl_filemeta_t *fm;
+    sl_array_t *fm_array;
+    sl_nil_t nil;
+    uint64_t uint64;
+    gboolean qres, firstmatch = true;
+
+    if (!slq->slq_tracker_cursor) {
+        LOG(log_debug, logtype_sl, "sl_mod_fetch_result: no results found");
+        goto EC_CLEANUP;
+    }
+
+    /* Prepare CNIDs */
+    cnids = talloc_zero(slq->slq_reply, sl_cnids_t);
+    cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
+    cnids->ca_unkn1 = 0xadd;
+    cnids->ca_context = slq->slq_ctx2;
+
+    /* Prepare FileMeta */
+    fm = talloc_zero(slq->slq_reply, sl_filemeta_t);
+    fm_array = talloc_zero(fm, sl_array_t);
+    /* For some reason the list of results always starts with a nil entry */
+    dalloc_add_copy(fm_array, &nil, sl_nil_t);
+    dalloc_add(fm, fm_array, sl_array_t);
+
+    LOG(log_debug, logtype_sl, "sl_mod_fetch_result: now interating Tracker results cursor");
+
+    while ((slq->slq_state == SLQ_STATE_RUNNING) && (i <= MAX_SL_RESULTS)) {
+        become_root();
+        qres = tracker_sparql_cursor_next(slq->slq_tracker_cursor, NULL, &error);
+        unbecome_root();
+
+        if (!qres)
+            break;
+
+#if 0
+        if (firstmatch) {
+            /* For some reason the list of results always starts with a nil entry */
+            dalloc_add_copy(fm_array, &nil, sl_nil_t);
+            firstmatch = false;
+        }
+#endif
+
+        become_root();
+        uri = tracker_sparql_cursor_get_string(slq->slq_tracker_cursor, 0, NULL);
+        unbecome_root();
+
+        EC_NULL_LOG( path = tracker_to_unix_path(uri) );
+
+        if ((id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, path, &did)) == CNID_INVALID) {
+            LOG(log_debug, logtype_sl, "sl_mod_fetch_result: cnid_for_path error: %s", path);
+            goto loop_cleanup;
+        }
+        LOG(log_debug, logtype_sl, "Result %d: CNID: %" PRIu32 ", path: \"%s\"", i, ntohl(id), path);
+
+        uint64 = ntohl(id);
+        if (slq->slq_cnids) {
+            if (!bsearch(&uint64, slq->slq_cnids, slq->slq_cnids_num, sizeof(uint64_t), cnid_cmp_fn))
+                goto loop_cleanup;
+        }
+
+        struct stat sb;
+        if (stat(path, &sb) != 0)
+            goto loop_cleanup;
+
+        dalloc_add_copy(cnids->ca_cnids, &uint64, uint64_t);
+        add_filemeta(slq->slq_reqinfo, fm_array, id, path, &sb);
+
+    loop_cleanup:
+        g_free(path);
+        i++;
+   }
+
+    if (error) {
+        LOG(log_error, logtype_sl, "Couldn't query the Tracker Store: '%s'",
+            error ? error->message : "unknown error");
+        g_clear_error (&error);
+        EC_FAIL;
+    }
+
+    if (i < MAX_SL_RESULTS)
+        slq->slq_state = SLQ_STATE_DONE;
+
+    uint64 = (i > 0) ? 35 : 0; /* OS X AFP server returns 35 here if results are found */
+    dalloc_add_copy(slq->slq_reply, &uint64, uint64_t);
+    dalloc_add(slq->slq_reply, cnids, sl_cnids_t);
+    dalloc_add(slq->slq_reply, fm, sl_filemeta_t);
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (slq->slq_tracker_cursor) {
+            g_object_unref(slq->slq_tracker_cursor);
+            slq->slq_tracker_cursor = NULL;
+        }
+    }
+    EC_EXIT;
+}
+
+/* Free ressources allocated in this module */
+static int sl_mod_close_query(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+
+    if (slq->slq_tracker_cursor) {
+        g_object_unref(slq->slq_tracker_cursor);
+        slq->slq_tracker_cursor = NULL;
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_fetch_attrs(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+    sl_filemeta_t *fm;
+    sl_array_t *fm_array;
+    sl_nil_t nil;
+
+    LOG(log_debug, logtype_sl, "sl_mod_fetch_attrs(\"%s\")", slq->slq_path);
+
+    struct stat sb;
+    EC_ZERO( stat(slq->slq_path, &sb) );
+
+    /* Prepare FileMeta */
+    fm = talloc_zero(slq->slq_reply, sl_filemeta_t);
+    fm_array = talloc_zero(fm, sl_array_t);
+    dalloc_add(fm, fm_array, fm_array_t);
+    /* For some reason the list of results always starts with a nil entry */
+    dalloc_add_copy(fm_array, &nil, sl_nil_t);
+
+    add_filemeta(slq->slq_reqinfo, fm_array, CNID_INVALID, slq->slq_path, &sb);
+
+    /* Now add result */
+    dalloc_add(slq->slq_reply, fm, sl_filemeta_t);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_error(void *p)
+{
+    EC_INIT;
+    slq_t *slq = p;
+
+    if (!slq)
+        goto EC_CLEANUP;
+
+    if (slq->slq_tracker_cursor) {
+        g_object_unref(slq->slq_tracker_cursor);
+        slq->slq_tracker_cursor = NULL;
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static int sl_mod_index_file(const void *p)
+{
+    /*
+     * This seems to cause index problems on volumes that are watched and indexed
+     * by Tracker, so we disable this extra manual indexing for now.
+     * It's primary pupose was ensuring files created via AFP are indexed on large
+     * volumes where the filesystem event notification engine (eg FAM or FEN) may
+     * impose limits on the maximum number of watched directories.
+     */
+    return 0;
+
+#if 0
+#ifdef HAVE_TRACKER_MINER
+    EC_INIT;
+    const char *f = p;
+
+    if (!f)
+        goto EC_CLEANUP;
+
+    GError *error = NULL;
+    GFile *file = NULL;
+
+    file = g_file_new_for_commandline_arg(f);
+
+    become_root();
+    tracker_miner_manager_index_file(manager, file, &error);
+    unbecome_root();
+
+    if (error)
+        LOG(log_error, logtype_sl, "sl_mod_index_file(\"%s\"): indexing failed", f);
+    else
+        LOG(log_debug, logtype_sl, "sl_mod_index_file(\"%s\"): indexing file was successful", f);
+
+EC_CLEANUP:
+    if (file)
+        g_object_unref(file);
+    EC_EXIT;
+#else
+    return 0;
+#endif /* HAVE_TRACKER_MINER */
+#endif /* 0 */
+}
+
+struct sl_module_export sl_mod = {
+    SL_MODULE_VERSION,
+    sl_mod_init,
+    sl_mod_start_search,
+    sl_mod_fetch_result,
+    sl_mod_close_query,
+    sl_mod_fetch_attrs,
+    sl_mod_error,
+    sl_mod_index_file
+};
diff --git a/etc/spotlight/slmod_sparql_map.c b/etc/spotlight/slmod_sparql_map.c
new file mode 100644 (file)
index 0000000..7ac4963
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <atalk/logger.h>
+
+#include "slmod_sparql_map.h"
+
+#define NOTSUPPORTED NULL
+#define SPECIAL      NULL
+
+struct spotlight_sparql_map spotlight_sparql_map[] = {
+    /* ssm_spotlight_attr               ssm_enabled, ssm_type,   ssm_sparql_attr */
+    {"*",                               true, ssmt_fts,   "fts:match"},
+
+    /* Filesystem metadata */
+    {"kMDItemFSLabel",                  true, ssmt_num,   NOTSUPPORTED},
+    {"kMDItemDisplayName",              true, ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSName",                   true, ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSContentChangeDate",      true, ssmt_date,  "nfo:fileLastModified"},
+
+    /* Common metadata */
+    {"kMDItemTextContent",              true, ssmt_fts,   "fts:match"},
+    {"kMDItemContentCreationDate",      true, ssmt_date,  "nie:contentCreated"},
+    {"kMDItemContentModificationDate",  true, ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemAttributeChangeDate",      true, ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemLastUsedDate",             true, ssmt_date,  "nfo:fileLastAccessed"},
+    {"kMDItemAuthors",                  true, ssmt_str,   "dc:creator"},
+    {"kMDItemCopyright",                true, ssmt_str,   "nie:copyright"},
+    {"kMDItemCountry",                  true, ssmt_str,   "nco:country"},
+    {"kMDItemCreator",                  true, ssmt_str,   "dc:creator"},
+    {"kMDItemDurationSeconds",          true, ssmt_num,   "nfo:duration"},
+    {"kMDItemNumberOfPages",            true, ssmt_num,   "nfo:pageCount"},
+    {"kMDItemTitle",                    true, ssmt_str,   "nie:title"},
+    {"_kMDItemGroupId",                 true, ssmt_type,  SPECIAL},
+    {"kMDItemContentTypeTree",          true, ssmt_type,  SPECIAL},
+
+    /* Image metadata */
+    {"kMDItemPixelWidth",               true, ssmt_num,   "nfo:width"},
+    {"kMDItemPixelHeight",              true, ssmt_num,   "nfo:height"},
+    {"kMDItemColorSpace",               true, ssmt_str,   "nexif:colorSpace"},
+    {"kMDItemBitsPerSample",            true, ssmt_num,   "nfo:colorDepth"},
+    {"kMDItemFocalLength",              true, ssmt_num,   "nmm:focalLength"},
+    {"kMDItemISOSpeed",                 true, ssmt_num,   "nmm:isoSpeed"},
+    {"kMDItemOrientation",              true, ssmt_bool,  "nfo:orientation"},
+    {"kMDItemResolutionWidthDPI",       true, ssmt_num,   "nfo:horizontalResolution"},
+    {"kMDItemResolutionHeightDPI",      true, ssmt_num,   "nfo:verticalResolution"},
+    {"kMDItemExposureTimeSeconds",      true, ssmt_num,   "nmm:exposureTime"},
+
+    /* Audio metadata */
+    {"kMDItemComposer",                 true, ssmt_str,   "nmm:composer"},
+    {"kMDItemMusicalGenre",             true, ssmt_str,   "nfo:genre"},
+
+    {NULL, false, ssmt_str, NULL}
+};
+
+struct MDTypeMap MDTypeMap[] = {
+    {"1",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email"},
+    {"2",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
+    {"3",                       kMDTypeMapNotSup,   NULL}, /* PrefPane */
+    {"4",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Font"},
+    {"5",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"},
+    {"6",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
+    {"7",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video"},
+    {"8",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Executable"},
+    {"9",                       kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder"},
+    {"10",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio"},
+    {"11",                      kMDTypeMapMime,     "application/pdf"},
+    {"12",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation"},
+    {"13",                      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image"},
+    {"public.jpeg",             kMDTypeMapMime,     "image/jpeg"},
+    {"public.tiff",             kMDTypeMapMime,     "image/tiff"},
+    {"com.compuserve.gif",      kMDTypeMapMime,     "image/gif"},
+    {"public.png",              kMDTypeMapMime,     "image/png"},
+    {"com.microsoft.bmp",       kMDTypeMapMime,     "image/bmp"},
+    {"public.content",          kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document"},
+    {"public.mp3",              kMDTypeMapMime,     "audio/mpeg"},
+    {"public.mpeg-4-audio",     kMDTypeMapMime,     "audio/x-aac"},
+    {"com.apple.application",   kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software"},
+    {"public.text",             kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument"},
+    {"public.plain-text",       kMDTypeMapMime,     "text/plain"},
+    {"public.rtf",              kMDTypeMapMime,     "text/rtf"},
+    {"public.html",             kMDTypeMapMime,     "text/html"},
+    {"public.xml",              kMDTypeMapMime,     "text/xml"},
+    {"public.source-code",      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"},
+    {NULL,                      kMDTypeMapNotSup,   NULL}
+};
+
+void configure_spotlight_attributes(const char *attributes_in)
+{
+    char *attr, *attributes;
+    int i;
+
+    for (i = 0; spotlight_sparql_map[i].ssm_spotlight_attr != NULL; i++)
+        spotlight_sparql_map[i].ssm_enabled = false;
+
+    /*
+     * Go through the attribute map and for every element scan
+     * attributes_in with strtok(). If it's contained, keep it
+     * enabled, otherwise disable it.
+     */
+
+    attributes = strdup(attributes_in);
+
+    for (attr = strtok(attributes, ","); attr; attr = strtok(NULL, ",")) {
+
+        for (i = 0; spotlight_sparql_map[i].ssm_spotlight_attr != NULL; i++)
+
+            if (strcmp(attr, spotlight_sparql_map[i].ssm_spotlight_attr) == 0) {
+                LOG(log_info, logtype_sl, "Enabling Spotlight attribute: %s",
+                    spotlight_sparql_map[i].ssm_spotlight_attr);
+                spotlight_sparql_map[i].ssm_enabled = true;
+                break;
+        }
+    }
+
+    free(attributes);
+}
diff --git a/etc/spotlight/slmod_sparql_map.h b/etc/spotlight/slmod_sparql_map.h
new file mode 100644 (file)
index 0000000..3b92474
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef SPOTLIGHT_SPARQL_MAP_H
+#define SPOTLIGHT_SPARQL_MAP_H
+
+enum ssm_type {
+    ssmt_bool,   /* a boolean value that doesn't requires a SPARQL FILTER */
+    ssmt_num,    /* a numeric value that requires a SPARQL FILTER */
+    ssmt_str,    /* a string value that requieres a SPARQL FILTER */
+    ssmt_fts,    /* a string value that will be queried with SPARQL 'fts:match' */
+    ssmt_date,   /* date values are handled in a special map function map_daterange() */
+    ssmt_type    /* kMDItemContentType, requires special mapping */
+};
+
+enum kMDTypeMap {
+    kMDTypeMapNotSup,           /* not supported */
+    kMDTypeMapRDF,              /* query with rdf:type */
+    kMDTypeMapMime              /* query with nie:mimeType */
+};
+
+struct spotlight_sparql_map {
+    const char *ssm_spotlight_attr;
+    bool ssm_enabled;
+    enum ssm_type ssm_type;
+    const char *ssm_sparql_attr;
+};
+
+struct MDTypeMap {
+    const char *mdtm_value;     /* MD query value of attributes '_kMDItemGroupId' and 'kMDItemContentTypeTree' */
+    enum kMDTypeMap mdtm_type;   /* whether SPARQL query must search attribute rdf:type or nie:mime_Type */
+    const char *mdtm_sparql;    /* the SPARQL query match string */
+};
+
+extern struct spotlight_sparql_map spotlight_sparql_map[];
+extern struct spotlight_sparql_map spotlight_sparql_date_map[];
+extern struct MDTypeMap MDTypeMap[];
+#endif
diff --git a/etc/spotlight/slmod_sparql_parser.c b/etc/spotlight/slmod_sparql_parser.c
new file mode 100644 (file)
index 0000000..ea7152c
--- /dev/null
@@ -0,0 +1,2055 @@
+/* A Bison parser, made by GNU Bison 2.7.  */
+
+/* Bison implementation for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+   
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.7"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations.  */
+/* Line 371 of yacc.c  */
+#line 1 "slmod_sparql_parser.y"
+
+  #include <atalk/standards.h>
+
+  #include <stdbool.h>
+  #include <stdio.h>
+  #include <string.h>
+  #include <time.h>
+
+  #include <gio/gio.h>
+
+  #include <atalk/talloc.h>
+  #include <atalk/logger.h>
+  #include <atalk/errchk.h>
+  #include <atalk/spotlight.h>
+
+  #include "slmod_sparql_map.h"
+
+  struct yy_buffer_state;
+  typedef struct yy_buffer_state *YY_BUFFER_STATE;
+  extern int yylex (void);
+  extern void yyerror (char const *);
+  extern void *yyterminate(void);
+  extern YY_BUFFER_STATE yy_scan_string( const char *str);
+  extern void yy_delete_buffer ( YY_BUFFER_STATE buffer );
+
+  /* forward declarations */
+  static const char *map_expr(const char *attr, char op, const char *val);
+  static const char *map_daterange(const char *dateattr, time_t date1, time_t date2);
+  static time_t isodate2unix(const char *s);
+ /* global vars, eg needed by the lexer */
+  slq_t *ssp_slq;
+
+  /* local vars */
+  static gchar *ssp_result;
+  static char sparqlvar;
+  static char *result_limit;
+
+/* Line 371 of yacc.c  */
+#line 107 "slmod_sparql_parser.c"
+
+# ifndef YY_NULL
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULL nullptr
+#  else
+#   define YY_NULL 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "y.tab.h".  */
+#ifndef YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED
+# define YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     WORD = 258,
+     BOOL = 259,
+     FUNC_INRANGE = 260,
+     DATE_ISO = 261,
+     OBRACE = 262,
+     CBRACE = 263,
+     EQUAL = 264,
+     UNEQUAL = 265,
+     GT = 266,
+     LT = 267,
+     COMMA = 268,
+     QUOTE = 269,
+     AND = 270,
+     OR = 271
+   };
+#endif
+/* Tokens.  */
+#define WORD 258
+#define BOOL 259
+#define FUNC_INRANGE 260
+#define DATE_ISO 261
+#define OBRACE 262
+#define CBRACE 263
+#define EQUAL 264
+#define UNEQUAL 265
+#define GT 266
+#define LT 267
+#define COMMA 268
+#define QUOTE 269
+#define AND 270
+#define OR 271
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 387 of yacc.c  */
+#line 46 "slmod_sparql_parser.y"
+
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+
+
+/* Line 387 of yacc.c  */
+#line 190 "slmod_sparql_parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+/* "%code provides" blocks.  */
+/* Line 387 of yacc.c  */
+#line 40 "slmod_sparql_parser.y"
+
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+
+
+/* Line 387 of yacc.c  */
+#line 222 "slmod_sparql_parser.c"
+
+#endif /* !YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+/* Line 390 of yacc.c  */
+#line 229 "slmod_sparql_parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(N) (N)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+    int yyi;
+#endif
+{
+  return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+            && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+        || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                          \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+       Stack = &yyptr->Stack_alloc;                                    \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (YYID (0))
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   52
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  17
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  7
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  22
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  51
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   271
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     7,     9,    11,    15,    17,    19,
+      23,    27,    31,    37,    43,    49,    55,    62,    69,    76,
+      83,    92,    97
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      18,     0,    -1,    -1,    18,    19,    -1,    20,    -1,     4,
+      -1,    21,    16,    21,    -1,    21,    -1,    22,    -1,     7,
+      20,     8,    -1,    20,    15,    20,    -1,    20,    16,    20,
+      -1,     3,     9,    14,     3,    14,    -1,     3,    10,    14,
+       3,    14,    -1,     3,    12,    14,     3,    14,    -1,     3,
+      11,    14,     3,    14,    -1,     3,     9,    14,     3,    14,
+       3,    -1,     3,    10,    14,     3,    14,     3,    -1,     3,
+      12,    14,     3,    14,     3,    -1,     3,    11,    14,     3,
+      14,     3,    -1,     5,     7,     3,    13,    23,    13,    23,
+       8,    -1,     6,     7,     3,     8,    -1,     3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    68,    68,    70,    74,    88,    94,   102,   103,   104,
+     105,   112,   125,   126,   127,   128,   129,   130,   131,   132,
+     136,   140,   141
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "WORD", "BOOL", "FUNC_INRANGE",
+  "DATE_ISO", "OBRACE", "CBRACE", "EQUAL", "UNEQUAL", "GT", "LT", "COMMA",
+  "QUOTE", "AND", "OR", "$accept", "input", "line", "expr", "match",
+  "function", "date", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    17,    18,    18,    19,    20,    20,    20,    20,    20,
+      20,    20,    21,    21,    21,    21,    21,    21,    21,    21,
+      22,    23,    23
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     1,     1,     3,     1,     1,     3,
+       3,     3,     5,     5,     5,     5,     6,     6,     6,     6,
+       8,     4,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     0,     5,     0,     0,     3,     4,     7,
+       8,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     9,    10,    11,     6,     0,
+       0,     0,     0,     0,    12,    13,    15,    14,    22,     0,
+       0,    16,    17,    19,    18,     0,     0,     0,     0,    21,
+      20
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     1,     7,     8,     9,    10,    40
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -10
+static const yytype_int8 yypact[] =
+{
+     -10,    10,   -10,     9,   -10,    -2,    -1,   -10,     8,    -9,
+     -10,     2,    12,    13,    14,    26,    -7,    -1,    -1,    27,
+      28,    29,    30,    31,    22,   -10,    20,   -10,   -10,    23,
+      24,    25,    32,    19,    37,    38,    39,    40,   -10,    41,
+      34,   -10,   -10,   -10,   -10,    42,    19,    36,    43,   -10,
+     -10
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -10,   -10,   -10,    -6,    33,   -10,     3
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+      16,    25,     3,     4,     5,    15,     6,    19,    17,    18,
+       2,    26,    27,     3,     4,     5,    20,     6,    11,    12,
+      13,    14,    38,    17,    18,    39,    21,    22,    23,    24,
+       3,    29,    30,    31,    32,    33,    18,    34,    35,    36,
+      41,    42,    43,    44,    49,    47,    37,    46,    45,    48,
+       0,    50,    28
+};
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-10)))
+
+#define yytable_value_is_error(Yytable_value) \
+  YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+       6,     8,     3,     4,     5,     7,     7,    16,    15,    16,
+       0,    17,    18,     3,     4,     5,    14,     7,     9,    10,
+      11,    12,     3,    15,    16,     6,    14,    14,    14,     3,
+       3,     3,     3,     3,     3,    13,    16,    14,    14,    14,
+       3,     3,     3,     3,     8,     3,    14,    13,     7,    46,
+      -1,     8,    19
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    18,     0,     3,     4,     5,     7,    19,    20,    21,
+      22,     9,    10,    11,    12,     7,    20,    15,    16,    16,
+      14,    14,    14,    14,     3,     8,    20,    20,    21,     3,
+       3,     3,     3,    13,    14,    14,    14,    14,     3,     6,
+      23,     3,     3,     3,     3,     7,    13,     3,    23,     8,
+       8
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  However,
+   YYFAIL appears to be in use.  Nevertheless, it is formally deprecated
+   in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+   discussed.  */
+
+#define YYFAIL         goto yyerrlab
+#if defined YYFAIL
+  /* This is here to suppress warnings from the GCC cpp's
+     -Wunused-macros.  Normally we don't worry about that warning, but
+     some users do, and we want to make it easy for users to remove
+     YYFAIL uses, which will produce warnings from Bison 2.5.  */
+#endif
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (YYID (0))
+
+/* Error token number */
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                   \
+do {                                                                     \
+  if (yydebug)                                                           \
+    {                                                                    \
+      YYFPRINTF (stderr, "%s ", Title);                                          \
+      yy_symbol_print (stderr,                                           \
+                 Type, Value); \
+      YYFPRINTF (stderr, "\n");                                                  \
+    }                                                                    \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+    yytype_int16 *yybottom;
+    yytype_int16 *yytop;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+            yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+                      &(yyvsp[(yyi + 1) - (yynrhs)])
+                                      );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULL;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - Assume YYFAIL is not used.  It's too flawed to consider.  See
+       <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+       for details.  YYERROR is fine as it does not invoke this
+       function.
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval YY_INITIAL_VALUE(yyval_default);
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       `yyss': related to states.
+       `yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack.  Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       yytype_int16 *yyss1 = yyss;
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       yytype_int16 *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss_alloc, yyss);
+       YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 4:
+/* Line 1792 of yacc.c  */
+#line 74 "slmod_sparql_parser.y"
+    {
+    if (ssp_slq->slq_result_limit)
+        result_limit = talloc_asprintf(ssp_slq, "LIMIT %ld", ssp_slq->slq_result_limit);
+    else
+        result_limit = "";
+    ssp_result = talloc_asprintf(ssp_slq,
+                                 "SELECT ?url WHERE "
+                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } %s",
+                                 (yyvsp[(1) - (1)].sval), ssp_slq->slq_vol->v_path, result_limit);
+    (yyval.sval) = ssp_result;
+}
+    break;
+
+  case 5:
+/* Line 1792 of yacc.c  */
+#line 88 "slmod_sparql_parser.y"
+    {
+    if ((yyvsp[(1) - (1)].bval) == false)
+        YYACCEPT;
+    else
+        YYABORT;
+}
+    break;
+
+  case 6:
+/* Line 1792 of yacc.c  */
+#line 94 "slmod_sparql_parser.y"
+    {
+    if ((yyvsp[(1) - (3)].sval) == NULL || (yyvsp[(3) - (3)].sval) == NULL)
+        YYABORT;
+    if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
+        (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+    else
+        (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval));
+}
+    break;
+
+  case 7:
+/* Line 1792 of yacc.c  */
+#line 102 "slmod_sparql_parser.y"
+    {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;}
+    break;
+
+  case 8:
+/* Line 1792 of yacc.c  */
+#line 103 "slmod_sparql_parser.y"
+    {(yyval.sval) = (yyvsp[(1) - (1)].sval);}
+    break;
+
+  case 9:
+/* Line 1792 of yacc.c  */
+#line 104 "slmod_sparql_parser.y"
+    {(yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(2) - (3)].sval));}
+    break;
+
+  case 10:
+/* Line 1792 of yacc.c  */
+#line 105 "slmod_sparql_parser.y"
+    {
+    if (!ssp_slq->slq_allow_expr) {
+        yyerror("Spotlight queries with logic expressions are disabled");
+        YYABORT;
+    }
+    (yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+}
+    break;
+
+  case 11:
+/* Line 1792 of yacc.c  */
+#line 112 "slmod_sparql_parser.y"
+    {
+    if (!ssp_slq->slq_allow_expr) {
+        yyerror("Spotlight queries with logic expressions are disabled");
+        YYABORT;
+    }
+    if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
+        (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+    else
+        (yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(1) - (3)].sval));
+}
+    break;
+
+  case 12:
+/* Line 1792 of yacc.c  */
+#line 125 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 13:
+/* Line 1792 of yacc.c  */
+#line 126 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 14:
+/* Line 1792 of yacc.c  */
+#line 127 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 15:
+/* Line 1792 of yacc.c  */
+#line 128 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));}
+    break;
+
+  case 16:
+/* Line 1792 of yacc.c  */
+#line 129 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 17:
+/* Line 1792 of yacc.c  */
+#line 130 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 18:
+/* Line 1792 of yacc.c  */
+#line 131 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 19:
+/* Line 1792 of yacc.c  */
+#line 132 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));}
+    break;
+
+  case 20:
+/* Line 1792 of yacc.c  */
+#line 136 "slmod_sparql_parser.y"
+    {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));}
+    break;
+
+  case 21:
+/* Line 1792 of yacc.c  */
+#line 140 "slmod_sparql_parser.y"
+    {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));}
+    break;
+
+  case 22:
+/* Line 1792 of yacc.c  */
+#line 141 "slmod_sparql_parser.y"
+    {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;}
+    break;
+
+
+/* Line 1792 of yacc.c  */
+#line 1602 "slmod_sparql_parser.c"
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+       {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+       }
+      else
+       {
+         yydestruct ("Error: discarding",
+                     yytoken, &yylval);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+
+      yydestruct ("Error: popping",
+                 yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+/* Line 2055 of yacc.c  */
+#line 144 "slmod_sparql_parser.y"
+
+
+static time_t isodate2unix(const char *s)
+{
+    struct tm tm;
+
+    if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL)
+        return (time_t)-1;
+    return mktime(&tm);
+}
+
+static const char *map_daterange(const char *dateattr, time_t date1, time_t date2)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    struct tm *tmp;
+    char buf1[64], buf2[64];
+
+    EC_NULL_LOG( tmp = localtime(&date1) );
+    strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+    EC_NULL_LOG( tmp = localtime(&date2) );
+    strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) {
+            result = talloc_asprintf(ssp_slq,
+                                     "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
+                                     p->ssm_sparql_attr,
+                                     sparqlvar,
+                                     sparqlvar,
+                                     buf1,
+                                     sparqlvar,
+                                     buf2);
+            sparqlvar++;
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return result;
+}
+
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+    const char *sparqlAttr;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            switch (p->mdtm_type) {
+            case kMDTypeMapRDF:
+                sparqlAttr = "rdf:type";
+                break;
+            case kMDTypeMapMime:
+                sparqlAttr = "nie:mimeType";
+                break;
+            default:
+                return NULL;
+            }
+            result = talloc_asprintf(ssp_slq, "?obj %s '%s'",
+                                     sparqlAttr,
+                                     p->mdtm_sparql);
+            break;
+        }
+    }
+    return result;
+}
+
+static const char *map_expr(const char *attr, char op, const char *val)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    time_t t;
+    struct tm *tmp;
+    char buf1[64];
+    bstring q = NULL, search = NULL, replace = NULL;
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (p->ssm_enabled && (strcmp(p->ssm_spotlight_attr, attr) == 0)) {
+            if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
+                yyerror("unsupported Spotlight attribute");
+                EC_FAIL;
+            }
+            switch (p->ssm_type) {
+            case ssmt_bool:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_num:
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         op == '!' ? '=' : ' ', /* append '=' to '!' */
+                                         val);
+                sparqlvar++;
+                break;
+            case ssmt_str:
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         bdata(q));
+                sparqlvar++;
+                break;
+            case ssmt_fts:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_date:
+                t = atoi(val) + SPRAW_TIME_OFFSET;
+                EC_NULL( tmp = localtime(&t) );
+                strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         buf1);
+                sparqlvar++;
+                break;
+            case ssmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+            default:
+                EC_FAIL;
+            }
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (q)
+        bdestroy(q);
+    if (search)
+        bdestroy(search);
+    if (replace)
+        bdestroy(replace);
+    return result;
+}
+
+void yyerror(const char *str)
+{
+#ifdef MAIN
+    printf("yyerror: %s\n", str);
+#else
+    LOG(log_error, logtype_sl, "yyerror: %s", str);
+#endif
+}
+int yywrap()
+{
+    return 1;
+} 
+
+/**
+ * Map a Spotlight RAW query string to a SPARQL query string
+ *
+ * @param[in]     slq            Spotlight query handle
+ * @param[out]    sparql_result  Mapped SPARQL query, string is allocated in
+ *                               talloc context of slq
+ * @return        0 on success, -1 on error
+ **/
+int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result)
+{
+    EC_INIT;
+    YY_BUFFER_STATE s = NULL;
+    ssp_result = NULL;
+
+    ssp_slq = slq;
+    s = yy_scan_string(slq->slq_qstring);
+    sparqlvar = 'a';
+
+    EC_ZERO( yyparse() );
+
+EC_CLEANUP:
+    if (s)
+        yy_delete_buffer(s);
+    if (ret == 0)
+        *sparql_result = ssp_result;
+    else
+        *sparql_result = NULL;
+    EC_EXIT;
+}
+
+#ifdef MAIN
+int main(int argc, char **argv)
+{
+    int ret;
+    YY_BUFFER_STATE s;
+
+    if (argc != 2) {
+        printf("usage: %s QUERY\n", argv[0]);
+        return 1;
+    }
+
+    ssp_slq = talloc_zero(NULL, slq_t);
+    struct vol *vol = talloc_zero(ssp_slq, struct vol);
+    vol->v_path = "/Volumes/test";
+    ssp_slq->slq_vol = vol;
+    ssp_slq->slq_allow_expr = false;
+    sparqlvar = 'a';
+
+    s = yy_scan_string(argv[1]);
+
+    ret = yyparse();
+
+    yy_delete_buffer(s);
+
+    if (ret == 0)
+        printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)");
+
+    return 0;
+} 
+#endif
diff --git a/etc/spotlight/slmod_sparql_parser.h b/etc/spotlight/slmod_sparql_parser.h
new file mode 100644 (file)
index 0000000..9eb22b3
--- /dev/null
@@ -0,0 +1,130 @@
+/* A Bison parser, made by GNU Bison 2.7.  */
+
+/* Bison interface for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+   
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED
+# define YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     WORD = 258,
+     BOOL = 259,
+     FUNC_INRANGE = 260,
+     DATE_ISO = 261,
+     OBRACE = 262,
+     CBRACE = 263,
+     EQUAL = 264,
+     UNEQUAL = 265,
+     GT = 266,
+     LT = 267,
+     COMMA = 268,
+     QUOTE = 269,
+     AND = 270,
+     OR = 271
+   };
+#endif
+/* Tokens.  */
+#define WORD 258
+#define BOOL 259
+#define FUNC_INRANGE 260
+#define DATE_ISO 261
+#define OBRACE 262
+#define CBRACE 263
+#define EQUAL 264
+#define UNEQUAL 265
+#define GT 266
+#define LT 267
+#define COMMA 268
+#define QUOTE 269
+#define AND 270
+#define OR 271
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 2058 of yacc.c  */
+#line 46 "slmod_sparql_parser.y"
+
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+
+
+/* Line 2058 of yacc.c  */
+#line 97 "slmod_sparql_parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+/* "%code provides" blocks.  */
+/* Line 2058 of yacc.c  */
+#line 40 "slmod_sparql_parser.y"
+
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+
+
+/* Line 2058 of yacc.c  */
+#line 129 "slmod_sparql_parser.h"
+
+#endif /* !YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED  */
diff --git a/etc/spotlight/slmod_sparql_parser.y b/etc/spotlight/slmod_sparql_parser.y
new file mode 100644 (file)
index 0000000..76d8b5a
--- /dev/null
@@ -0,0 +1,365 @@
+%{
+  #include <atalk/standards.h>
+
+  #include <stdbool.h>
+  #include <stdio.h>
+  #include <string.h>
+  #include <time.h>
+
+  #include <gio/gio.h>
+
+  #include <atalk/talloc.h>
+  #include <atalk/logger.h>
+  #include <atalk/errchk.h>
+  #include <atalk/spotlight.h>
+
+  #include "slmod_sparql_map.h"
+
+  struct yy_buffer_state;
+  typedef struct yy_buffer_state *YY_BUFFER_STATE;
+  extern int yylex (void);
+  extern void yyerror (char const *);
+  extern void *yyterminate(void);
+  extern YY_BUFFER_STATE yy_scan_string( const char *str);
+  extern void yy_delete_buffer ( YY_BUFFER_STATE buffer );
+
+  /* forward declarations */
+  static const char *map_expr(const char *attr, char op, const char *val);
+  static const char *map_daterange(const char *dateattr, time_t date1, time_t date2);
+  static time_t isodate2unix(const char *s);
+ /* global vars, eg needed by the lexer */
+  slq_t *ssp_slq;
+
+  /* local vars */
+  static gchar *ssp_result;
+  static char sparqlvar;
+  static char *result_limit;
+%}
+
+%code provides {
+  #define SPRAW_TIME_OFFSET 978307200
+  extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
+  extern slq_t *ssp_slq;
+}
+
+%union {
+    int ival;
+    const char *sval;
+    bool bval;
+    time_t tval;
+}
+
+%expect 5
+%error-verbose
+
+%type <sval> match expr line function
+%type <tval> date
+
+%token <sval> WORD
+%token <bval> BOOL
+%token FUNC_INRANGE
+%token DATE_ISO
+%token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
+%left AND
+%left OR
+%%
+
+input:
+/* empty */
+| input line
+;
+     
+line:
+expr                           {
+    if (ssp_slq->slq_result_limit)
+        result_limit = talloc_asprintf(ssp_slq, "LIMIT %ld", ssp_slq->slq_result_limit);
+    else
+        result_limit = "";
+    ssp_result = talloc_asprintf(ssp_slq,
+                                 "SELECT ?url WHERE "
+                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } %s",
+                                 $1, ssp_slq->slq_vol->v_path, result_limit);
+    $$ = ssp_result;
+}
+;
+
+expr:
+BOOL                             {
+    if ($1 == false)
+        YYACCEPT;
+    else
+        YYABORT;
+}
+| match OR match                 {
+    if ($1 == NULL || $3 == NULL)
+        YYABORT;
+    if (strcmp($1, $3) != 0)
+        $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
+    else
+        $$ = talloc_asprintf(ssp_slq, "%s", $1);
+}
+| match                        {$$ = $1; if ($$ == NULL) YYABORT;}
+| function                     {$$ = $1;}
+| OBRACE expr CBRACE           {$$ = talloc_asprintf(ssp_slq, "%s", $2);}
+| expr AND expr                {
+    if (!ssp_slq->slq_allow_expr) {
+        yyerror("Spotlight queries with logic expressions are disabled");
+        YYABORT;
+    }
+    $$ = talloc_asprintf(ssp_slq, "%s . %s", $1, $3);
+}
+| expr OR expr                 {
+    if (!ssp_slq->slq_allow_expr) {
+        yyerror("Spotlight queries with logic expressions are disabled");
+        YYABORT;
+    }
+    if (strcmp($1, $3) != 0)
+        $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
+    else
+        $$ = talloc_asprintf(ssp_slq, "%s", $1);
+}
+;
+
+match:
+WORD EQUAL QUOTE WORD QUOTE     {$$ = map_expr($1, '=', $4);}
+| WORD UNEQUAL QUOTE WORD QUOTE {$$ = map_expr($1, '!', $4);}
+| WORD LT QUOTE WORD QUOTE      {$$ = map_expr($1, '<', $4);}
+| WORD GT QUOTE WORD QUOTE      {$$ = map_expr($1, '>', $4);}
+| WORD EQUAL QUOTE WORD QUOTE WORD    {$$ = map_expr($1, '=', $4);}
+| WORD UNEQUAL QUOTE WORD QUOTE WORD {$$ = map_expr($1, '!', $4);}
+| WORD LT QUOTE WORD QUOTE WORD     {$$ = map_expr($1, '<', $4);}
+| WORD GT QUOTE WORD QUOTE WORD     {$$ = map_expr($1, '>', $4);}
+;
+
+function:
+FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {$$ = map_daterange($3, $5, $7);}
+;
+
+date:
+DATE_ISO OBRACE WORD CBRACE    {$$ = isodate2unix($3);}
+| WORD                         {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
+;
+
+%%
+
+static time_t isodate2unix(const char *s)
+{
+    struct tm tm;
+
+    if (strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm) == NULL)
+        return (time_t)-1;
+    return mktime(&tm);
+}
+
+static const char *map_daterange(const char *dateattr, time_t date1, time_t date2)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    struct tm *tmp;
+    char buf1[64], buf2[64];
+
+    EC_NULL_LOG( tmp = localtime(&date1) );
+    strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+    EC_NULL_LOG( tmp = localtime(&date2) );
+    strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (strcmp(dateattr, p->ssm_spotlight_attr) == 0) {
+            result = talloc_asprintf(ssp_slq,
+                                     "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
+                                     p->ssm_sparql_attr,
+                                     sparqlvar,
+                                     sparqlvar,
+                                     buf1,
+                                     sparqlvar,
+                                     buf2);
+            sparqlvar++;
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return result;
+}
+
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+    const char *sparqlAttr;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            switch (p->mdtm_type) {
+            case kMDTypeMapRDF:
+                sparqlAttr = "rdf:type";
+                break;
+            case kMDTypeMapMime:
+                sparqlAttr = "nie:mimeType";
+                break;
+            default:
+                return NULL;
+            }
+            result = talloc_asprintf(ssp_slq, "?obj %s '%s'",
+                                     sparqlAttr,
+                                     p->mdtm_sparql);
+            break;
+        }
+    }
+    return result;
+}
+
+static const char *map_expr(const char *attr, char op, const char *val)
+{
+    EC_INIT;
+    char *result = NULL;
+    struct spotlight_sparql_map *p;
+    time_t t;
+    struct tm *tmp;
+    char buf1[64];
+    bstring q = NULL, search = NULL, replace = NULL;
+
+    for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
+        if (p->ssm_enabled && (strcmp(p->ssm_spotlight_attr, attr) == 0)) {
+            if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
+                yyerror("unsupported Spotlight attribute");
+                EC_FAIL;
+            }
+            switch (p->ssm_type) {
+            case ssmt_bool:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_num:
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c%c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         op == '!' ? '=' : ' ', /* append '=' to '!' */
+                                         val);
+                sparqlvar++;
+                break;
+            case ssmt_str:
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(regex(?%c, '%s'))",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         bdata(q));
+                sparqlvar++;
+                break;
+            case ssmt_fts:
+                result = talloc_asprintf(ssp_slq, "?obj %s '%s'", p->ssm_sparql_attr, val);
+                break;
+            case ssmt_date:
+                t = atoi(val) + SPRAW_TIME_OFFSET;
+                EC_NULL( tmp = localtime(&t) );
+                strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+                result = talloc_asprintf(ssp_slq, "?obj %s ?%c FILTER(?%c %c '%s')",
+                                         p->ssm_sparql_attr,
+                                         sparqlvar,
+                                         sparqlvar,
+                                         op,
+                                         buf1);
+                sparqlvar++;
+                break;
+            case ssmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+            default:
+                EC_FAIL;
+            }
+            break;
+        }
+    }
+
+EC_CLEANUP:
+    if (q)
+        bdestroy(q);
+    if (search)
+        bdestroy(search);
+    if (replace)
+        bdestroy(replace);
+    return result;
+}
+
+void yyerror(const char *str)
+{
+#ifdef MAIN
+    printf("yyerror: %s\n", str);
+#else
+    LOG(log_error, logtype_sl, "yyerror: %s", str);
+#endif
+}
+int yywrap()
+{
+    return 1;
+} 
+
+/**
+ * Map a Spotlight RAW query string to a SPARQL query string
+ *
+ * @param[in]     slq            Spotlight query handle
+ * @param[out]    sparql_result  Mapped SPARQL query, string is allocated in
+ *                               talloc context of slq
+ * @return        0 on success, -1 on error
+ **/
+int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result)
+{
+    EC_INIT;
+    YY_BUFFER_STATE s = NULL;
+    ssp_result = NULL;
+
+    ssp_slq = slq;
+    s = yy_scan_string(slq->slq_qstring);
+    sparqlvar = 'a';
+
+    EC_ZERO( yyparse() );
+
+EC_CLEANUP:
+    if (s)
+        yy_delete_buffer(s);
+    if (ret == 0)
+        *sparql_result = ssp_result;
+    else
+        *sparql_result = NULL;
+    EC_EXIT;
+}
+
+#ifdef MAIN
+int main(int argc, char **argv)
+{
+    int ret;
+    YY_BUFFER_STATE s;
+
+    if (argc != 2) {
+        printf("usage: %s QUERY\n", argv[0]);
+        return 1;
+    }
+
+    ssp_slq = talloc_zero(NULL, slq_t);
+    struct vol *vol = talloc_zero(ssp_slq, struct vol);
+    vol->v_path = "/Volumes/test";
+    ssp_slq->slq_vol = vol;
+    ssp_slq->slq_allow_expr = true;
+    sparqlvar = 'a';
+
+    s = yy_scan_string(argv[1]);
+
+    ret = yyparse();
+
+    yy_delete_buffer(s);
+
+    if (ret == 0)
+        printf("SPARQL: %s\n", ssp_result ? ssp_result : "(empty)");
+
+    return 0;
+} 
+#endif
diff --git a/etc/spotlight/spotlight_rawquery_lexer.c b/etc/spotlight/spotlight_rawquery_lexer.c
new file mode 100644 (file)
index 0000000..38b7a73
--- /dev/null
@@ -0,0 +1,1862 @@
+#line 2 "spotlight_rawquery_lexer.l"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+
+
+#line 9 "spotlight_rawquery_lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via yyrestart()), so that the user can continue scanning by
+        * just pointing yyin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       (yytext_ptr) = yy_bp; \
+       yyleng = (size_t) (yy_cp - yy_bp); \
+       (yy_hold_char) = *yy_cp; \
+       *yy_cp = '\0'; \
+       (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 17
+#define YY_END_OF_BUFFER 18
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+       {
+       flex_int32_t yy_verify;
+       flex_int32_t yy_nxt;
+       };
+static yyconst flex_int16_t yy_accept[57] =
+    {   0,
+        0,    0,   18,   17,   16,   17,    5,   17,   17,    6,
+        7,   15,   14,   12,   17,   13,   15,   15,   15,   17,
+       17,   17,   17,   11,    0,    8,   15,    0,    0,    0,
+       10,   15,   15,   15,    9,    0,    0,    0,   15,   15,
+       15,    0,    0,   15,   15,    4,    0,   15,    3,    0,
+       15,    0,    1,    0,    2,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    2,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    3,    4,    1,    5,    1,    6,    1,    7,
+        8,    9,    1,   10,    9,   11,    1,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    1,   12,
+       13,   14,    1,    1,    9,    9,    9,    9,    9,    9,
+        9,    9,   15,    9,    9,    9,    9,    9,    9,    9,
+        9,   16,    9,    9,    9,    9,    9,    9,    9,    9,
+        1,    1,    1,    1,    9,    1,   17,    9,    9,    9,
+
+       18,   19,   20,    9,   21,    9,    9,   22,   23,   24,
+       25,    9,    9,   26,   27,   28,   29,    9,    9,    9,
+        9,    9,    1,   30,    1,    1,    1,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,    1,    1,   32,   32,   32,   32,   32,   32,   32,
+
+       32,   32,   32,   32,   32,   32,   32,   32,   32,   32,
+       32,   32,   32,   32,   32,   32,   32,   32,   32,   32,
+       32,   32,   32,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   34,
+       34,   34,   34,   34,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[35] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    1,
+        2,    1,    1,    1,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    1,
+        1,    2,    2,    2
+    } ;
+
+static yyconst flex_int16_t yy_base[58] =
+    {   0,
+        0,    0,   91,   92,   92,   77,   92,   61,   82,   92,
+       92,    3,   92,   92,   74,   92,   14,   25,   15,   56,
+       54,   53,   52,   92,   61,   92,   57,   49,   48,   47,
+       92,   27,   28,   16,   92,   46,   45,   52,   36,   30,
+       37,   43,   55,   48,   38,   47,   59,   39,   45,   47,
+       40,   40,   42,   40,   92,   92,   42
+    } ;
+
+static yyconst flex_int16_t yy_def[58] =
+    {   0,
+       56,    1,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   57,   56,   56,   56,   56,   57,   17,   17,   56,
+       56,   56,   56,   56,   56,   56,   17,   56,   56,   56,
+       56,   17,   17,   17,   56,   56,   56,   56,   17,   17,
+       17,   56,   56,   17,   17,   17,   56,   17,   17,   56,
+       17,   56,   17,   56,   56,    0,   56
+    } ;
+
+static yyconst flex_int16_t yy_nxt[127] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       12,   14,   15,   16,   17,   12,   12,   12,   18,   12,
+       12,   12,   12,   12,   12,   12,   12,   19,   12,   20,
+        4,   21,   22,   23,   28,   29,   30,   32,   27,   27,
+       34,   33,   39,   27,   41,   28,   29,   30,   27,   40,
+       27,   27,   44,   27,   46,   49,   45,   53,   51,   27,
+       27,   27,   27,   27,   55,   27,   54,   52,   27,   50,
+       27,   48,   47,   27,   43,   42,   27,   37,   36,   27,
+       27,   38,   37,   36,   27,   35,   31,   26,   25,   24,
+       56,    3,   56,   56,   56,   56,   56,   56,   56,   56,
+
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56
+    } ;
+
+static yyconst flex_int16_t yy_chk[127] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,   12,   12,   12,   17,   19,   34,
+       19,   18,   32,   57,   34,   17,   17,   17,   18,   33,
+       32,   33,   39,   40,   41,   45,   40,   51,   48,   39,
+       41,   45,   48,   51,   54,   53,   52,   50,   49,   47,
+       46,   44,   43,   42,   38,   37,   36,   30,   29,   28,
+       27,   25,   23,   22,   21,   20,   15,    9,    8,    6,
+        3,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "spotlight_rawquery_lexer.l"
+
+#line 8 "spotlight_rawquery_lexer.l"
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include <atalk/spotlight.h>
+#ifdef HAVE_TRACKER_SPARQL
+#include "slmod_sparql_parser.h"
+#define SLQ_VAR ssp_slq
+#endif
+#line 509 "spotlight_rawquery_lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in (void );
+
+void yyset_in  (FILE * in_str  );
+
+FILE *yyget_out (void );
+
+void yyset_out  (FILE * out_str  );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+               { \
+               int c = '*'; \
+               int n; \
+               for ( n = 0; n < max_size && \
+                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+                       buf[n] = (char) c; \
+               if ( c == '\n' ) \
+                       buf[n++] = (char) c; \
+               if ( c == EOF && ferror( yyin ) ) \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+               result = n; \
+               } \
+       else \
+               { \
+               errno=0; \
+               while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+                       { \
+                       if( errno != EINTR) \
+                               { \
+                               YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                               break; \
+                               } \
+                       errno=0; \
+                       clearerr(yyin); \
+                       } \
+               }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+    
+#line 27 "spotlight_rawquery_lexer.l"
+
+#line 693 "spotlight_rawquery_lexer.c"
+
+       if ( !(yy_init) )
+               {
+               (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! yyin )
+                       yyin = stdin;
+
+               if ( ! yyout )
+                       yyout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       yyensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               yy_create_buffer(yyin,YY_BUF_SIZE );
+               }
+
+               yy_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of yytext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               do
+                       {
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+                       if ( yy_accept[yy_current_state] )
+                               {
+                               (yy_last_accepting_state) = yy_current_state;
+                               (yy_last_accepting_cpos) = yy_cp;
+                               }
+                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                               {
+                               yy_current_state = (int) yy_def[yy_current_state];
+                               if ( yy_current_state >= 57 )
+                                       yy_c = yy_meta[(unsigned int) yy_c];
+                               }
+                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+                       ++yy_cp;
+                       }
+               while ( yy_base[yy_current_state] != 92 );
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+               if ( yy_act == 0 )
+                       { /* have to back up */
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       yy_act = yy_accept[yy_current_state];
+                       }
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+                       case 0: /* must back up */
+                       /* undo the effects of YY_DO_BEFORE_ACTION */
+                       *yy_cp = (yy_hold_char);
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 28 "spotlight_rawquery_lexer.l"
+return FUNC_INRANGE;
+       YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 29 "spotlight_rawquery_lexer.l"
+return DATE_ISO;
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 30 "spotlight_rawquery_lexer.l"
+{yylval.bval = false; return BOOL;}
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 31 "spotlight_rawquery_lexer.l"
+{yylval.bval = true; return BOOL;}
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 32 "spotlight_rawquery_lexer.l"
+return QUOTE;
+       YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 33 "spotlight_rawquery_lexer.l"
+return OBRACE;
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 34 "spotlight_rawquery_lexer.l"
+return CBRACE;
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 35 "spotlight_rawquery_lexer.l"
+return AND;
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 36 "spotlight_rawquery_lexer.l"
+return OR;
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 37 "spotlight_rawquery_lexer.l"
+return EQUAL;
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 38 "spotlight_rawquery_lexer.l"
+return UNEQUAL;
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 39 "spotlight_rawquery_lexer.l"
+return LT;
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 40 "spotlight_rawquery_lexer.l"
+return GT;
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 41 "spotlight_rawquery_lexer.l"
+return COMMA;
+       YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 42 "spotlight_rawquery_lexer.l"
+{yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;}
+       YY_BREAK
+case 16:
+/* rule 16 can match eol */
+YY_RULE_SETUP
+#line 43 "spotlight_rawquery_lexer.l"
+/* ignore */
+       YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 44 "spotlight_rawquery_lexer.l"
+ECHO;
+       YY_BREAK
+#line 862 "spotlight_rawquery_lexer.c"
+case YY_STATE_EOF(INITIAL):
+       yyterminate();
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed yyin at a new source and called
+                        * yylex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( yywrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * yytext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       int num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), (size_t) num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       yyrestart(yyin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+               /* Extend the array by 50%, plus the number we really need. */
+               yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+               YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+               if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+       }
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+    
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               if ( yy_accept[yy_current_state] )
+                       {
+                       (yy_last_accepting_state) = yy_current_state;
+                       (yy_last_accepting_cpos) = yy_cp;
+                       }
+               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                       {
+                       yy_current_state = (int) yy_def[yy_current_state];
+                       if ( yy_current_state >= 57 )
+                               yy_c = yy_meta[(unsigned int) yy_c];
+                       }
+               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+       register char *yy_cp = (yy_c_buf_p);
+
+       register YY_CHAR yy_c = 1;
+       if ( yy_accept[yy_current_state] )
+               {
+               (yy_last_accepting_state) = yy_current_state;
+               (yy_last_accepting_cpos) = yy_cp;
+               }
+       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+               {
+               yy_current_state = (int) yy_def[yy_current_state];
+               if ( yy_current_state >= 57 )
+                       yy_c = yy_meta[(unsigned int) yy_c];
+               }
+       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+       yy_is_jam = (yy_current_state == 56);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+       register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+       /* undo effects of setting up yytext */
+       *yy_cp = (yy_hold_char);
+
+       if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+               { /* need to shift things up to make room */
+               /* +2 for EOB chars. */
+               register int number_to_move = (yy_n_chars) + 2;
+               register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+                                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+               register char *source =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+               while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       *--dest = *--source;
+
+               yy_cp += (int) (dest - source);
+               yy_bp += (int) (dest - source);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+               if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+                       YY_FATAL_ERROR( "flex scanner push-back overflow" );
+               }
+
+       *--yy_cp = (char) c;
+
+       (yytext_ptr) = yy_bp;
+       (yy_hold_char) = *yy_cp;
+       (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+    
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       yyrestart(yyin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( yywrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve yytext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+       if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+       }
+
+       yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+       yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              yypop_buffer_state();
+        *              yypush_buffer_state(new_buffer);
+     */
+       yyensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       yy_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (yywrap()) processing, but the only time this flag
+        * is looked at is after yywrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+    
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       yy_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               yyfree((void *) b->yy_ch_buf  );
+
+       yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+    
+       yy_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       yyensure_buffer_stack();
+
+       /* This block is copied from yy_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from yy_switch_to_buffer. */
+       yy_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       yy_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               yy_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+       int num_to_alloc;
+    
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+                                                                 
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+                               
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+    
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       yy_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+    
+       return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+    
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = _yybytes_len + 2;
+       buf = (char *) yyalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+       for ( i = 0; i < _yybytes_len; ++i )
+               buf[i] = yybytes[i];
+
+       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = yy_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               yytext[yyleng] = (yy_hold_char); \
+               (yy_c_buf_p) = yytext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               yyleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               yy_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               yypop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       yyfree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+       free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 44 "spotlight_rawquery_lexer.l"
+
+
+
diff --git a/etc/spotlight/spotlight_rawquery_lexer.l b/etc/spotlight/spotlight_rawquery_lexer.l
new file mode 100644 (file)
index 0000000..aa1e25f
--- /dev/null
@@ -0,0 +1,44 @@
+%top{
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+}
+
+%{
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <atalk/talloc.h>
+#include <atalk/spotlight.h>
+#ifdef HAVE_TRACKER_SPARQL
+#include "slmod_sparql_parser.h"
+#define SLQ_VAR ssp_slq
+#endif
+%}
+
+ASC     [a-zA-Z0-9_\*\:\-\.]
+U       [\x80-\xbf]
+U2      [\xc2-\xdf]
+U3      [\xe0-\xef]
+U4      [\xf0-\xf4]
+
+UANY    {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+UONLY   {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+
+%%
+InRange                               return FUNC_INRANGE;
+\$time\.iso                           return DATE_ISO;
+false                                 {yylval.bval = false; return BOOL;}
+true                                  {yylval.bval = true; return BOOL;}
+\"                                    return QUOTE;
+\(                                    return OBRACE;
+\)                                    return CBRACE;
+\&\&                                  return AND;
+\|\|                                  return OR;
+\=\=                                  return EQUAL;
+\!\=                                  return UNEQUAL;
+\<                                    return LT;
+\>                                    return GT;
+\,                                    return COMMA;
+{UANY}+                               {yylval.sval = talloc_strdup(SLQ_VAR, yytext); return WORD;}
+[ \t\n]                               /* ignore */
+%%
index 3a02b8359770be9f64fdd0b2cf560fa7831817f4..23fd3d1c7eb8710627da05fa01c96ccbdb7660dd 100644 (file)
@@ -93,7 +93,7 @@ AM_CFLAGS = @SSL_CFLAGS@ @LIBGCRYPT_CFLAGS@
 uams_pam_la_CFLAGS         = @PAM_CFLAGS@
 uams_dhx_pam_la_CFLAGS     = @SSL_CFLAGS@ @PAM_CFLAGS@
 uams_dhx2_pam_la_CFLAGS    = @LIBGCRYPT_CFLAGS@ @PAM_CFLAGS@
-uams_gss_la_CFLAGS        = @GSSAPI_CFLAGS@
+uams_gss_la_CFLAGS        = @GSSAPI_CFLAGS@ @KRB5_CFLAGS@
 
 uams_guest_la_LDFLAGS      = -module -avoid-version
 uams_randnum_la_LDFLAGS    = -module -avoid-version @SSL_LIBS@
@@ -104,7 +104,7 @@ uams_dhx_passwd_la_LDFLAGS = -module -avoid-version @CRYPT_LIBS@ @SSL_LIBS@
 uams_dhx_pam_la_LDFLAGS                = -module -avoid-version @CRYPT_LIBS@ @SSL_LIBS@ @PAM_LIBS@
 uams_dhx2_passwd_la_LDFLAGS    = -module -avoid-version @CRYPT_LIBS@ @LIBGCRYPT_LIBS@
 uams_dhx2_pam_la_LDFLAGS       = -module -avoid-version @LIBGCRYPT_LIBS@ @PAM_LIBS@
-uams_gss_la_LDFLAGS       = -module -avoid-version @GSSAPI_LIBS@ 
+uams_gss_la_LDFLAGS       = -module -avoid-version @GSSAPI_LIBS@ @KRB5_LIBS@
 
 #
 # module compilation
index d033d96b12c76e322a8fd143b90e283ff8847738..8e79b450d4c802e9e6e516c598c2ced645270476 100644 (file)
@@ -136,7 +136,11 @@ error:
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) {
     int count = 0;
@@ -919,7 +923,7 @@ static int dhx2_changepw(void *obj _U_, char *uname,
     return ret;
 }
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
     if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login,
                      pam_logincont, pam_logout, pam_login_ext) < 0)
@@ -927,9 +931,6 @@ static int uam_setup(const char *path)
     if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
         return -1;
 
-    p = gcry_mpi_new(0);
-    g = gcry_mpi_new(0);
-
     LOG(log_debug, logtype_uams, "DHX2: generating mersenne primes");
     /* Generate p and g for DH */
     if (dh_params_generate(PRIMEBITS) != 0) {
@@ -945,11 +946,12 @@ static void uam_cleanup(void)
     uam_unregister(UAM_SERVER_LOGIN, "DHX2");
     uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
 
+    LOG(log_debug, logtype_uams, "DHX2: uam_cleanup");
+
     gcry_mpi_release(p);
     gcry_mpi_release(g);
 }
 
-
 UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
     UAM_MODULE_SERVER,
     UAM_MODULE_VERSION,
index c4a322ad48220ca899783ce5d0a83c17b4a4650c..b74cc124f7b59cd2127713e90a7ea049db6c24cf 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_dhx2_passwd.c,v 1.8 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
@@ -604,7 +603,7 @@ static int passwd_logincont(void *obj, struct passwd **uam_pwd,
     return ret;
 }
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
     if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", passwd_login,
                      passwd_logincont, NULL, passwd_login_ext) < 0)
index 4c7d87c0cbf03fc4e592134ea52046ba382b994b..631f84d9f97ae1c9753549e29ec4a283573fcfbf 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_dhx_pam.c,v 1.33 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -80,7 +79,11 @@ static char *PAM_password;
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) {
   int count = 0;
@@ -193,7 +196,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     DH *dh;
 
     /* get the client's public key */
-    if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+    if (!(bn = BN_bin2bn((unsigned char *)ibuf, KEYSIZE, NULL))) {
     /* Log Entry */
            LOG(log_info, logtype_uams, "uams_dhx_pam.c :PAM No Public Key -- %s",
                  strerror(errno));
@@ -255,10 +258,10 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     }
 
     /* figure out the key. store the key in rbuf for now. */
-    i = DH_compute_key(rbuf, bn, dh);
+    i = DH_compute_key((unsigned char *)rbuf, bn, dh);
     
     /* set the key */
-    CAST_set_key(&castkey, i, rbuf);
+    CAST_set_key(&castkey, i, (unsigned char *)rbuf);
     
     /* session id. it's just a hashed version of the object pointer. */
     sessid = dhxhash(obj);
@@ -267,7 +270,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     *rbuflen += sizeof(sessid);
     
     /* public key */
-    BN_bn2bin(dh->pub_key, rbuf); 
+    BN_bn2bin(dh->pub_key, (unsigned char *)rbuf); 
     rbuf += KEYSIZE;
     *rbuflen += KEYSIZE;
 
@@ -301,7 +304,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
 #endif /* 0 */
 
     /* encrypt using cast */
-    CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv, 
+    CAST_cbc_encrypt((unsigned char *)rbuf, (unsigned char *)rbuf, CRYPTBUFLEN, &castkey, msg2_iv, 
                     CAST_ENCRYPT);
     *rbuflen += CRYPTBUFLEN;
     BN_free(bn);
@@ -436,13 +439,13 @@ static int pam_logincont(void *obj, struct passwd **uam_pwd,
        hostname = NULL;
        }
 
-    CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+    CAST_cbc_encrypt((unsigned char *)ibuf, (unsigned char *)rbuf, CRYPT2BUFLEN, &castkey,
                     msg3_iv, CAST_DECRYPT);
     memset(&castkey, 0, sizeof(castkey));
 
     /* check to make sure that the random number is the same. we
      * get sent back an incremented random number. */
-    if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+    if (!(bn1 = BN_bin2bn((unsigned char *)rbuf, KEYSIZE, NULL)))
       return AFPERR_PARAM;
 
     if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
@@ -613,13 +616,13 @@ static int pam_changepw(void *obj, char *username,
     }
 
     /* grab the client's nonce, old password, and new password. */
-    CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
+    CAST_cbc_encrypt((unsigned char *)ibuf, (unsigned char *)ibuf, CHANGEPWBUFLEN, &castkey,
                     msg3_iv, CAST_DECRYPT);
     memset(&castkey, 0, sizeof(castkey));
 
     /* check to make sure that the random number is the same. we
      * get sent back an incremented random number. */
-    if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+    if (!(bn1 = BN_bin2bn((unsigned char *)ibuf, KEYSIZE, NULL))) {
     /* Log Entry */
            LOG(log_info, logtype_uams, "uams_dhx_pam.c :PAM: Random Number Not the same or not incremented-- %s",
                  strerror(errno));
@@ -716,7 +719,7 @@ static int pam_changepw(void *obj, char *username,
 }
 
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHCAST128", pam_login, 
                   pam_logincont, pam_logout, pam_login_ext) < 0)
index d390f76b4da29d556fb07bdf559849e3610e9362..e407d9d05f773c392c38214f4ee8376cb4ceff8d 100644 (file)
@@ -371,7 +371,7 @@ static int passwd_logincont(void *obj, struct passwd **uam_pwd,
 }
 
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHCAST128",
                   passwd_login, passwd_logincont, NULL, passwd_login_ext) < 0)
index f8cfc1af1434c0ad7384456e9340fe5fdd38d379..325d1c13bb1e4c54ce2fe0cadfa95899cb7864fb 100644 (file)
@@ -21,6 +21,7 @@
 #include <atalk/uam.h>
 #include <atalk/util.h>
 #include <atalk/compat.h>
+#include <atalk/globals.h>
 
 /* Kerberos includes */
 #ifdef HAVE_GSSAPI_GSSAPI_H
 #include <gssapi.h>
 #endif // HAVE_GSSAPI_GSSAPI_H
 
+#ifdef HAVE_KERBEROS
+#ifdef HAVE_KRB5_KRB5_H
+#include <krb5/krb5.h>
+#else
+#include <krb5.h>
+#endif /* HAVE_KRB5_KRB5_H */
+#endif /* HAVE_KERBEROS */
+
 #define LOG_UAMS(log_level, ...) \
     LOG(log_level, logtype_uams, __VA_ARGS__)
 
@@ -318,8 +327,6 @@ static int do_gss_auth(void *obj,
 
 cleanup_client_name:
     gss_release_name(&status, &client_name);
-
-cleanup_context:
     gss_release_buffer(&status, &authenticator_buff);
     gss_delete_sec_context(&status, &context, NULL);
 
@@ -481,8 +488,139 @@ static int gss_login_ext(void *obj,
     return gss_login(obj, uam_pwd, ibuf, ibuflen, rbuf, rbuflen);
 }
 
-static int uam_setup(const char *path)
+static int set_principal(AFPObj *obj, char *principal)
 {
+    size_t len = strlen(principal);
+
+    if (len > 255) {
+        LOG(log_error, logtype_afpd, "set_principal: principal '%s' too long (max=255)", principal, len);
+        return -1;
+    }
+
+    /* We store: 1 byte number principals (1) + 1 byte principal name length + zero terminated principal */
+    if ((obj->options.k5principal = malloc(len + 3)) == NULL) {
+        LOG(log_error, logtype_afpd, "set_principal: OOM");
+        return -1;
+    }
+
+    LOG(log_info, logtype_afpd, "Using AFP Kerberos service principal name: %s", principal);
+
+    obj->options.k5principal[0] = 1;
+    obj->options.k5principal[1] = (unsigned char)len;
+    obj->options.k5principal_buflen = len + 2;
+    strncpy(obj->options.k5principal + 2, principal, len);
+
+    return 0;
+}
+
+static int gss_create_principal(AFPObj *obj)
+{
+    int rv = -1;
+#ifdef HAVE_KERBEROS
+    krb5_context context;
+    krb5_error_code ret;
+    const char *error_msg;
+    krb5_keytab keytab;
+    krb5_keytab_entry entry;
+    krb5_principal service_principal;
+    char *principal;
+    krb5_kt_cursor cursor;
+
+    if (krb5_init_context(&context)) {
+        LOG(log_error, logtype_afpd, "gss_create_principal: failed to intialize a krb5_context");
+        goto exit;
+    }
+    if ((ret = krb5_kt_default(context, &keytab)))
+        goto krb5_error;
+
+    if (obj->options.k5service && obj->options.fqdn && obj->options.k5realm) {
+        LOG(log_debug, logtype_afpd, "gss_create_principal: using service principal specified in options");
+            
+        if ((ret = krb5_build_principal(context,
+                                        &service_principal,
+                                        strlen(obj->options.k5realm),
+                                        obj->options.k5realm,
+                                        obj->options.k5service,
+                                        obj->options.fqdn,
+                                        NULL)))
+            goto krb5_error;
+
+        if ((ret = krb5_kt_get_entry(context,
+                                     keytab,
+                                     service_principal,
+                                     0, // kvno - wildcard
+                                     0, // enctype - wildcard
+                                     &entry)) == KRB5_KT_NOTFOUND) {
+            krb5_unparse_name(context, service_principal, &principal);
+            LOG(log_error, logtype_afpd, "gss_create_principal: specified service principal '%s' not found in keytab", principal);
+#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
+            krb5_free_unparsed_name(context, principal);
+#else
+            krb5_xfree(principal);
+#endif
+            goto krb5_cleanup;
+        }
+        krb5_free_principal(context, service_principal);
+        if (ret)
+            goto krb5_error;
+    } else {
+        LOG(log_debug, logtype_afpd, "gss_create_principal: using first entry from keytab as service principal");
+        if ((ret = krb5_kt_start_seq_get(context, keytab, &cursor)))
+            goto krb5_error;
+        ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
+        krb5_kt_end_seq_get(context, keytab, &cursor);
+        if (ret)
+            goto krb5_error;
+    }
+
+    krb5_unparse_name(context, entry.principal, &principal);
+#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
+    krb5_free_keytab_entry_contents(context, &entry);
+#elif defined(HAVE_KRB5_KT_FREE_ENTRY)
+    krb5_kt_free_entry(context, &entry);
+#endif
+    set_principal(obj, principal);
+    free(principal);
+    rv = 0;
+    goto krb5_cleanup;
+
+krb5_error:
+    if (ret) {
+        error_msg = krb5_get_error_message(context, ret);
+        LOG(log_note, logtype_afpd, "Can't get principal from default keytab: %s",
+            (char *)error_msg);
+#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
+        krb5_free_error_message(context, error_msg);
+#else
+        krb5_xfree(error_msg);
+#endif
+    }
+
+krb5_cleanup:
+    krb5_kt_close(context, keytab);
+    krb5_free_context(context);
+
+#else /* ! HAVE_KERBEROS */
+    if (!obj->options.k5service || !obj->options.fqdn || !obj->options.k5realm)
+        goto exit;
+
+    char principal[255];
+    size_t len = snprintf(principal, sizeof(principal), "%s/%s@%s",
+                          obj->options.k5service, obj->options.fqdn, obj->options.k5realm);
+    (void)set_principal(obj, principal);
+    rv = 0;
+#endif /* HAVE_KERBEROS */
+
+exit:
+    return rv;
+}
+
+static int uam_setup(void *handle, const char *path)
+{
+    AFPObj *obj = (AFPObj *)handle;
+    if (gss_create_principal(obj) != 0)
+        return -1;
+
     return uam_register(UAM_SERVER_LOGIN_EXT, path, "Client Krb v2",
                         gss_login, gss_logincont, gss_logout, gss_login_ext);
 }
index 00f90ece0972ee01670d5cc1eee55879d6795b35..baaf9eb2e611560d4360dc713905acb767fc73fd 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_guest.c,v 1.18 2009-11-08 01:07:17 didg Exp $
  *
  * (c) 2001 (see COPYING)
  */
@@ -122,7 +121,7 @@ static int noauth_printer(char *start, char *stop, char *username, struct papfil
 }
 
 
-static int uam_setup(const char *path)
+static int uam_setup(void *handle, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "No User Authent",
                    noauth_login, NULL, NULL, noauth_login_ext) < 0)
index 6f15ac0a30c660bbdec3939e66b0059e06643f5f..da5be62132a0dd073f53abdc880c6f4e2e06faf6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_pam.c,v 1.24 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -54,7 +53,11 @@ extern UAM_MODULE_EXPORT void append(struct papfile *, const char *, int);
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) 
 {
@@ -442,7 +445,7 @@ static int pam_printer(char *start, char *stop, char *username, struct papfile *
 }
 
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", 
                   pam_login, NULL, pam_logout, pam_login_ext) < 0)
index bec45a360813bd502affc3a9ac5eed8902f6fae3..9cd738cc08a723e4458b60592c4731212ebd9256 100644 (file)
@@ -353,7 +353,7 @@ static int passwd_printer(char      *start, char *stop, char *username, struct papfil
     return(0);
 }
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
     if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd",
                      passwd_login, NULL, NULL, passwd_login_ext) < 0)
index 9b9c1d801dd57d53c543d8cdc1e11fb555f18f05..679a160117770a6d2914b4abbba84cf2cc92451b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_pgp.c,v 1.12 2009-10-15 11:39:48 didg Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -175,7 +174,7 @@ static int pgp_logincont(void *obj, struct passwd **uam_pwd,
 }
 
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN, path, "PGPuam 1.0",
                   pgp_login, pgp_logincont, NULL) < 0)
index 301fdd0ff4cb69d48fe7e1f81d7572a954c7dfea..50ce6ce5fadc05c594be6f541ba2827ce415b363 100644 (file)
@@ -1,5 +1,4 @@
 /* 
- * $Id: uams_randnum.c,v 1.21 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -532,7 +531,7 @@ static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
     return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
 }
 
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
 {
   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", 
                   randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
index 4ee8385436cbec5213219bccb5d72809e7b024c4..d935e1e696431facdc09f3910ad194e8805faca7 100644 (file)
@@ -1,4 +1,4 @@
 Makefile
 Makefile.in
 *.o
-lockrpc.gen.h
\ No newline at end of file
+afp_dtrace.h
index 93d28929a0f96d2e7cbc385c5255377a30846297..550741d6b473d7f7dd230343b165823ab6871485 100644 (file)
@@ -1,6 +1,8 @@
 # Makefile.am for include/atalk/
 
 atalkincludedir = $(includedir)/atalk
+BUILT_SOURCES = 
+CLEANFILES =
 
 atalkinclude_HEADERS = \
        adouble.h \
@@ -33,11 +35,25 @@ noinst_HEADERS = \
        server_child.h \
        server_ipc.h \
        tdb.h \
-       cnid_dbd_private.h \
+       cnid_bdb_private.h \
+       cnid_mysql_private.h \
        cnid_private.h \
        bstradd.h \
        errchk.h \
        ftw.h \
        dsi.h \
        ldapconfig.h \
-       fce_api.h
\ No newline at end of file
+       talloc.h \
+       dalloc.h \
+       byteorder.h \
+       fce_api.h \
+       spotlight.h
+
+EXTRA_DIST = afp_dtrace.d
+
+if WITH_DTRACE
+BUILT_SOURCES += afp_dtrace.h
+CLEANFILES += afp_dtrace.h
+afp_dtrace.h: $(top_srcdir)/include/atalk/afp_dtrace.d
+       $(LIBTOOL) --mode=execute dtrace -o afp_dtrace.h -h -s $(top_srcdir)/include/atalk/afp_dtrace.d
+endif
index 95aa0753401bb3385013192c3ea620a1fdd327b1..c7343a9e735f37878cc0b6e89dbc43fc258b4b4b 100644 (file)
 
 #ifdef HAVE_ACLS
 
+#define O_NETATALK_ACL (O_NOFOLLOW << 1)
+#define O_IGNORE (O_NOFOLLOW << 2)
+
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
+
+#ifdef HAVE_NFSV4_ACLS
 
 #define chmod_acl nfsv4_chmod
 
@@ -32,7 +41,7 @@ extern int strip_nontrivial_aces(ace_t **saces, int sacecount);
 extern ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count);
 extern int nfsv4_chmod(char *name, mode_t mode);
 
-#endif  /* HAVE_SOLARIS_ACLS */
+#endif  /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
 #include <sys/types.h>
@@ -51,6 +60,7 @@ extern int remove_acl_vfs(const char *name);
 
 #else /* HAVE_ACLS=no */
 
+#define O_NETATALK_ACL 0
 #define chmod_acl chmod
 
 #endif /* HAVE_ACLS */
index 384486ea4633d8c14085af8fb998e7853e789e3f..f279d28cc67fb1a691c2320656e1cb2fbb14a1cd 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
@@ -219,7 +219,7 @@ struct adouble {
     int                 ad_reso_refcount;
     off_t               ad_rlen;           /* ressource fork len with AFP 3.0         *
                                             * the header parameter size is too small. */
-    char                *ad_name;          /* name in server encoding (usually UTF8)  */
+    char                *ad_name;          /* mac name (maccharset or UTF8-MAC)       */
     struct adouble_fops *ad_ops;
     uint16_t            ad_open_forks;     /* open forks (by others)                  */
     char                ad_data[AD_DATASZ_MAX];
@@ -245,8 +245,10 @@ struct adouble {
 #define ADFLAGS_TRUNC     (1<<12) /* truncate, open called with O_TRUNC */
 
 #define ADVOL_NODEV      (1 << 0)
+#define ADVOL_RO         (1 << 1)
 #define ADVOL_UNIXPRIV   (1 << 2) /* adouble unix priv */
 #define ADVOL_INVDOTS    (1 << 3) /* dot files (.DS_Store) are invisible) */
+#define ADVOL_FOLLO_SYML (1 << 4)
 
 /* lock flags */
 #define ADLOCK_CLR      (0)
@@ -346,6 +348,12 @@ struct adouble {
 #define AD_AFPFILEI_GROUP       (1 << 1) /* ignore group */
 #define AD_AFPFILEI_BLANKACCESS (1 << 2) /* blank access permissions */
 
+/*
+ * String identifiers for the 16 AppleDouble filler bytes
+ */
+#define AD_FILLER_NETATALK "Netatalk        "
+#define AD_FILLER_OSX      "Mac OS X"
+
 #define ad_data_fileno(ad)  ((ad)->ad_data_fork.adf_fd)
 #define ad_reso_fileno(ad)  ((ad)->ad_rfp->adf_fd)
 #define ad_meta_fileno(ad)  ((ad)->ad_mdp->adf_fd)
@@ -369,9 +377,12 @@ struct adouble {
 #define ad_ref(ad)   (ad)->ad_refcount++
 #define ad_unref(ad) --((ad)->ad_refcount)
 
+#define ad_get_syml_opt(ad) (((ad)->ad_options & ADVOL_FOLLO_SYML) ? 0 : O_NOFOLLOW)
+
 /* ad_flush.c */
 extern int ad_rebuild_adouble_header_v2(struct adouble *);
 extern int ad_rebuild_adouble_header_ea(struct adouble *);
+extern  int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf);
 extern int ad_copy_header (struct adouble *, struct adouble *);
 extern int ad_flush (struct adouble *);
 extern int ad_close (struct adouble *, int);
@@ -398,6 +409,7 @@ extern int ad_mkdir       (const char *, mode_t);
 struct vol;
 extern void ad_init       (struct adouble *, const struct vol * restrict);
 extern void ad_init_old   (struct adouble *ad, int flags, int options);
+extern int ad_init_offsets(struct adouble *ad);
 extern int ad_open        (struct adouble *ad, const char *path, int adflags, ...);
 extern int ad_openat      (struct adouble *, int dirfd, const char *path, int adflags, ...);
 extern int ad_refresh     (const char *path, struct adouble *);
@@ -406,6 +418,7 @@ extern int ad_metadata    (const char *, int, struct adouble *);
 extern int ad_metadataat  (int, const char *, int, struct adouble *);
 extern mode_t ad_hf_mode(mode_t mode);
 extern int ad_valid_header_osx(const char *path);
+extern off_t ad_reso_size(const char *path, int adflags, struct adouble *ad);
 
 /* ad_conv.c */
 extern int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath);
@@ -418,7 +431,7 @@ extern ssize_t ad_write(struct adouble *, uint32_t, off_t, int, const char *, si
 extern ssize_t adf_pread(struct ad_fd *, void *, size_t, off_t);
 extern ssize_t adf_pwrite(struct ad_fd *, const void *, size_t, off_t);
 extern int     ad_dtruncate(struct adouble *, off_t);
-extern int     ad_rtruncate(struct adouble *, off_t);
+extern int     ad_rtruncate(struct adouble *, const char *, off_t);
 extern int     copy_fork(int eid, struct adouble *add, struct adouble *ads);
 
 /* ad_size.c */
@@ -444,5 +457,8 @@ extern uint32_t  ad_forcegetid(struct adouble *adp);
 #ifdef WITH_SENDFILE
 extern int ad_readfile_init(const struct adouble *ad, int eid, off_t *off, int end);
 #endif
+#ifdef WITH_RECVFILE
+extern ssize_t ad_recvfile(struct adouble *ad, int eid,  int sock, off_t off, size_t len, int);
+#endif
 
 #endif /* _ATALK_ADOUBLE_H */
index 76315aa41acd661b4e7b8b6c8dc454496ce6b38c..0c2489ea906d4d884c1a2b87f41c971db45e26e4 100644 (file)
@@ -49,6 +49,7 @@ typedef uint16_t AFPUserBytes;
 #define AFPSRVRINFO_FASTBOZO     (1<<15) /* fast copying */
 
 #define AFP_OK         0
+#define AFPERR_MAXSESS  -1068   /* maximum number of allowed sessions reached */
 #define AFPERR_DID1     -4000   /* not an afp error DID is 1*/
 #define AFPERR_ACCESS  -5000   /* permission denied */
 #define AFPERR_AUTHCONT        -5001   /* logincont */
diff --git a/include/atalk/afp_dtrace.d b/include/atalk/afp_dtrace.d
new file mode 100644 (file)
index 0000000..e9ee989
--- /dev/null
@@ -0,0 +1,10 @@
+provider afp {
+   probe afpfunc__start(int func, char *funcname);
+   probe afpfunc__done(int func, char *funcname);
+   probe read__start(long size);
+   probe read__done();
+   probe write__start(long size);
+   probe write__done();
+   probe cnid__start(char *cnidfunc);
+   probe cnid__done();
+};
diff --git a/include/atalk/byteorder.h b/include/atalk/byteorder.h
new file mode 100644 (file)
index 0000000..0ea3972
--- /dev/null
@@ -0,0 +1,217 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB Byte handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+#include <arpa/inet.h>
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+LVAL(buf,pos) - extract a 8 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#ifdef WORDS_BIGENDIAN
+
+#define SVAL(buf,pos) (PVAL(buf,(pos)+1)|PVAL(buf,pos)<<8)
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define LVAL(buf,pos) (IVAL(buf,pos)|IVAL(buf,(pos)+4)<<32)
+#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos))
+
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos+1)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16))
+#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32))
+
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val)))
+#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val)))
+
+#else
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define LVAL(buf,pos) (IVAL(buf,pos)|((uint64_t)IVAL(buf,(pos)+4))<<32)
+#define LVALS(buf,pos) ((int64_t)LVAL(buf,pos))
+
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,((val)&0xFFFF)),SSVALX(buf,pos+2,(val)>>16))
+#define SLVALX(buf,pos,val) (SIVALX(buf,pos,((val)&0xFFFFFFFF)),SIVALX(buf,pos+4,(val)>>32))
+
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+#define SLVAL(buf,pos,val) SLVALX((buf),(pos),((uint64_t)(val)))
+#define SLVALS(buf,pos,val) SLVALX((buf),(pos),((int64_t)(val)))
+
+#endif
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define LVAL(buf,pos) (*(const uint64_t *)((const char *)(buf) + (pos)))
+#define LVAL_NC(buf,pos) (*(uint64_t *)((char *)(buf) + (pos)))
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define LVALS(buf,pos) (*(const int64_t *)((const char *)(buf) + (pos)))
+#define LVALS_NC(buf,pos) (*(int64_t *)((char *)(buf) + (pos)))
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SLVAL(buf,pos,val) LVAL_NC(buf,pos)=((uint64_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
+#define SLVALS(buf,pos,val) LVALS_NC(buf,pos)=((int64_t)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+#define LREV(x) ((IREV(x)<<32) | (IREV((x)>>32)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RLVAL(buf,pos) LREV(LVAL(buf,pos))
+#define RLVALS(buf,pos) LREV(LVALS(buf,pos))
+
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+#define RSLVAL(buf,pos,val) SLVAL(buf,pos,LREV(val))
+#define RSLVALS(buf,pos,val) SLVALS(buf,pos,LREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
index 2d3aa062b8b601c4ac016e4aa59d5d672e8e05c6..4fefcd0439a0b902ef6548fdc9f3143629e77429 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <atalk/adouble.h>
 #include <atalk/list.h>
+#include <atalk/uuid.h>
 
 /* CNID object flags */
 #define CNID_FLAG_PERSISTENT   0x01      /* This backend implements DID persistence */
 /*
  * This is instance of CNID database object.
  */
-struct _cnid_db {
-    uint32_t flags;             /* Flags describing some CNID backend aspects. */
-    char *volpath;               /* Volume path this particular CNID db refers to. */
-    void *_private;              /* back-end speficic data */
+typedef struct _cnid_db {
+    uint32_t      cnid_db_flags;     /* Flags describing some CNID backend aspects. */
+    struct vol   *cnid_db_vol;
+    void         *cnid_db_private;   /* back-end speficic data */
 
     cnid_t (*cnid_add)         (struct _cnid_db *cdb, const struct stat *st, cnid_t did,
                                 const char *name, size_t, cnid_t hint);
@@ -69,19 +70,16 @@ struct _cnid_db {
                                 const char *, size_t, cnid_t);
     int    (*cnid_find)        (struct _cnid_db *cdb, const char *name, size_t namelen,
                                 void *buffer, size_t buflen);
-};
-typedef struct _cnid_db cnid_db;
+    int    (*cnid_wipe)        (struct _cnid_db *cdb);
+} cnid_db;
 
 /*
  * Consolidation of args passedn from main cnid_open to modules cnid_XXX_open, so
  * that it's easier to add aditional args as required.
  */
 struct cnid_open_args {
-    const char *dir;
-    mode_t mask;
-    uint32_t flags;
-    const char *cnidserver;      /* for dbd */
-    const char *cnidport;        /* for dbd */
+    uint32_t cnid_args_flags;
+    struct vol *cnid_args_vol;
 };
 
 /*
@@ -103,12 +101,7 @@ void cnid_init();
 void cnid_register(struct _cnid_module *module);
 
 /* This function opens a CNID database for selected volume. */
-struct _cnid_db *cnid_open(const char *volpath,
-                           mode_t mask,
-                           char *type,
-                           int flags,
-                           const char *cnidsrv,
-                           const char *cnidport);
+struct _cnid_db *cnid_open(struct vol *vol, char *type, int flags);
 cnid_t cnid_add        (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
                         const char *name, const size_t len, cnid_t hint);
 int    cnid_delete     (struct _cnid_db *cdb, cnid_t id);
@@ -123,6 +116,7 @@ cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_
                         char *name, const size_t len, cnid_t hint);
 int    cnid_find       (struct _cnid_db *cdb, const char *name, size_t namelen,
                         void *buffer, size_t buflen);
+int    cnid_wipe       (struct _cnid_db *cdb);
 void   cnid_close      (struct _cnid_db *db);
 
 #endif
diff --git a/include/atalk/cnid_bdb_private.h b/include/atalk/cnid_bdb_private.h
new file mode 100644 (file)
index 0000000..f87eddd
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  Interface to the cnid_dbd daemon that stores/retrieves CNIDs from a database.
+ */
+
+
+#ifndef _ATALK_CNID_DBD_PRIVATE_H
+#define _ATALK_CNID_DBD_PRIVATE_H 1
+
+#include <sys/stat.h>
+#include <atalk/adouble.h>
+#include <sys/param.h>
+
+#include <atalk/cnid_private.h>
+
+#define CNID_DBD_OP_OPEN        0x01
+#define CNID_DBD_OP_CLOSE       0x02
+#define CNID_DBD_OP_ADD         0x03
+#define CNID_DBD_OP_GET         0x04
+#define CNID_DBD_OP_RESOLVE     0x05
+#define CNID_DBD_OP_LOOKUP      0x06
+#define CNID_DBD_OP_UPDATE      0x07
+#define CNID_DBD_OP_DELETE      0x08
+#define CNID_DBD_OP_MANGLE_ADD  0x09
+#define CNID_DBD_OP_MANGLE_GET  0x0a
+#define CNID_DBD_OP_GETSTAMP    0x0b
+#define CNID_DBD_OP_REBUILD_ADD 0x0c
+#define CNID_DBD_OP_SEARCH      0x0d
+#define CNID_DBD_OP_WIPE        0x0e
+
+#define CNID_DBD_RES_OK            0x00
+#define CNID_DBD_RES_NOTFOUND      0x01
+#define CNID_DBD_RES_ERR_DB        0x02
+#define CNID_DBD_RES_ERR_MAX       0x03
+#define CNID_DBD_RES_ERR_DUPLCNID  0x04
+#define CNID_DBD_RES_SRCH_CNT      0x05
+#define CNID_DBD_RES_SRCH_DONE     0x06
+
+#define DBD_MAX_SRCH_RSLTS 100
+#define DBD_NUM_OPEN_ARGS 3
+
+struct cnid_dbd_rqst {
+    int     op;
+    cnid_t  cnid;
+    dev_t   dev;
+    ino_t   ino;
+    uint32_t type;
+    cnid_t  did;
+    const char *name;
+    size_t  namelen;
+};
+
+struct cnid_dbd_rply {
+    int     result;    
+    cnid_t  cnid;
+    cnid_t  did;
+    char    *name;
+    size_t  namelen;
+};
+
+typedef struct CNID_bdb_private {
+    struct vol *vol;
+    int       fd;              /* File descriptor to cnid_dbd */
+    char      stamp[ADEDLEN_PRIVSYN]; /* db timestamp */
+    char      *client_stamp;
+    size_t    stamp_size;
+    int       notfirst;   /* already open before */
+    int       changed;  /* stamp differ */
+} CNID_bdb_private;
+
+
+#endif /* include/atalk/cnid_dbd.h */
diff --git a/include/atalk/cnid_dbd_private.h b/include/atalk/cnid_dbd_private.h
deleted file mode 100644 (file)
index d7484e4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *  Interface to the cnid_dbd daemon that stores/retrieves CNIDs from a database.
- */
-
-
-#ifndef _ATALK_CNID_DBD_PRIVATE_H
-#define _ATALK_CNID_DBD_PRIVATE_H 1
-
-#include <sys/stat.h>
-#include <atalk/adouble.h>
-#include <sys/param.h>
-
-#include <atalk/cnid_private.h>
-
-#define CNID_DBD_OP_OPEN        0x01
-#define CNID_DBD_OP_CLOSE       0x02
-#define CNID_DBD_OP_ADD         0x03
-#define CNID_DBD_OP_GET         0x04
-#define CNID_DBD_OP_RESOLVE     0x05
-#define CNID_DBD_OP_LOOKUP      0x06
-#define CNID_DBD_OP_UPDATE      0x07
-#define CNID_DBD_OP_DELETE      0x08
-#define CNID_DBD_OP_MANGLE_ADD  0x09
-#define CNID_DBD_OP_MANGLE_GET  0x0a
-#define CNID_DBD_OP_GETSTAMP    0x0b
-#define CNID_DBD_OP_REBUILD_ADD 0x0c
-#define CNID_DBD_OP_SEARCH      0x0d
-
-#define CNID_DBD_RES_OK            0x00
-#define CNID_DBD_RES_NOTFOUND      0x01
-#define CNID_DBD_RES_ERR_DB        0x02
-#define CNID_DBD_RES_ERR_MAX       0x03
-#define CNID_DBD_RES_ERR_DUPLCNID  0x04
-#define CNID_DBD_RES_SRCH_CNT      0x05
-#define CNID_DBD_RES_SRCH_DONE     0x06
-
-#define DBD_MAX_SRCH_RSLTS 100
-
-struct cnid_dbd_rqst {
-    int     op;
-    cnid_t  cnid;
-    dev_t   dev;
-    ino_t   ino;
-    uint32_t type;
-    cnid_t  did;
-    const char *name;
-    size_t  namelen;
-};
-
-struct cnid_dbd_rply {
-    int     result;    
-    cnid_t  cnid;
-    cnid_t  did;
-    char    *name;
-    size_t  namelen;
-};
-
-typedef struct CNID_private {
-    uint32_t magic;
-    char      db_dir[MAXPATHLEN + 1]; /* Database directory without /.AppleDB appended */
-    char      *cnidserver;
-    char      *cnidport;
-    int       fd;              /* File descriptor to cnid_dbd */
-    char      stamp[ADEDLEN_PRIVSYN]; /* db timestamp */
-    char      *client_stamp;
-    size_t    stamp_size;
-    int       notfirst;   /* already open before */
-    int       changed;  /* stamp differ */
-} CNID_private;
-
-
-#endif /* include/atalk/cnid_dbd.h */
diff --git a/include/atalk/cnid_mysql_private.h b/include/atalk/cnid_mysql_private.h
new file mode 100644 (file)
index 0000000..79a54e8
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _ATALK_CNID_MYSQL_PRIVATE_H
+#define _ATALK_CNID_MYSQL_PRIVATE_H 1
+
+#include <atalk/cnid_private.h>
+#include <atalk/uuid.h>
+
+#define CNID_MYSQL_FLAG_DEPLETED (1 << 0) /* CNID set overflowed */
+
+typedef struct CNID_mysql_private {
+    struct vol *vol;
+    uint32_t      cnid_mysql_flags;
+    MYSQL        *cnid_mysql_con;
+    char         *cnid_mysql_voluuid_str;
+    cnid_t        cnid_mysql_hint;
+    MYSQL_STMT   *cnid_lookup_stmt;
+    MYSQL_STMT   *cnid_add_stmt;
+    MYSQL_STMT   *cnid_put_stmt;
+} CNID_mysql_private;
+
+#endif
index f5f825faa362f806e124cf3e0e32280166756a9c..c030ff99f404b2d99d09ef8d11b0a6d887c6e358 100644 (file)
@@ -5,9 +5,6 @@
 #ifndef _ATALK_CNID_PRIVATE_H
 #define _ATALK_CNID_PRIVATE_H 1
 
-#define CNID_DB_MAGIC   0x434E4944U  /* CNID */
-#define CNID_DATA_MAGIC 0x434E4945U  /* CNIE */
-
 #define CNID_OFS                 0
 #define CNID_LEN                 4
  
index a2d318f43a4f81942210a3a922941224f01e043b..08e15d798462fae6cedfe0dad8149c4e64203197 100644 (file)
 extern char *getusershell (void);
 #endif
 
-#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
-# include <stdio.h>
-# include <stdarg.h>
-# ifndef HAVE_SNPRINTF
-int snprintf (char *str,size_t count,const char *fmt,...);
-# endif
-
-# ifndef HAVE_VSNPRINTF
-int vsnprintf(char *str, size_t count, const char *fmt, va_list args);
-# endif
-#endif
-
 /* OpenBSD */
 #if defined(__OpenBSD__) && !defined(ENOTSUP)
 #define ENOTSUP EOPNOTSUPP
@@ -54,4 +42,10 @@ extern size_t strlcpy (char *, const char *, size_t);
 extern size_t strlcat (char *, const char *, size_t);
 #endif
 
+#ifndef HAVE_VASPRINTF
+#include <stdio.h>
+#include <stdarg.h>
+extern int vasprintf(char **ret, const char *fmt, va_list ap);
+#endif
+
 #endif
diff --git a/include/atalk/dalloc.h b/include/atalk/dalloc.h
new file mode 100644 (file)
index 0000000..4e963cd
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef DALLOC_H
+#define DALLOC_H
+
+#include <atalk/talloc.h>
+
+/* dynamic datastore */
+typedef struct {
+    void **dd_talloc_array;
+} DALLOC_CTX;
+
+/* Use dalloc_add_copy() macro, not this function */
+extern int dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *talloc_chunk, void *obj, size_t size);
+
+#define dalloc_add_copy(d, obj, type) dalloc_add_talloc_chunk((d), talloc((d), type), (obj), sizeof(type));
+#define dalloc_add(d, obj, type) dalloc_add_talloc_chunk((d), NULL, (obj), 0);
+extern void *dalloc_get(const DALLOC_CTX *d, ...);
+extern void *dalloc_value_for_key(const DALLOC_CTX *d, ...);
+extern int dalloc_size(DALLOC_CTX *d);
+extern char *dalloc_strdup(const void *ctx, const char *string);
+extern char *dalloc_strndup(const void *ctx, const char *string, size_t n);
+#endif  /* DALLOC_H */
index 7a910524699446d41745e4b5b6fb52aa0503e723..1aa22af85a154237c7899f884e20841d1d5e017a 100644 (file)
@@ -4,7 +4,6 @@
    @file    dictionary.h
    @author  N. Devillard
    @date    Sep 2007
-   @version $Revision: 1.12 $
    @brief   Implements a dictionary for string variables.
 
    This module implements a simple dictionary object, i.e. a list
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $
        $Author: ndevilla $
        $Date: 2007-11-23 21:37:00 $
-       $Revision: 1.12 $
 */
 
 #ifndef _DICTIONARY_H_
@@ -60,12 +57,12 @@ typedef struct _dictionary_ {
                                                        Function prototypes
  ---------------------------------------------------------------------------*/
 
-unsigned   dictionary_hash  (char * key);
-dictionary *dictionary_new  (int size);
-void       dictionary_del   (dictionary * vd);
-const char *dictionary_get  (const dictionary * d, const char *section, const char * key, const char * def);
-int        dictionary_set   (dictionary * vd, char *section, char * key, char * val);
-void       dictionary_unset (dictionary * d, char *section, char * key);
-void       dictionary_dump  (dictionary * d, FILE * out);
+unsigned   atalkdict_hash  (char * key);
+dictionary *atalkdict_new  (int size);
+void       atalkdict_del   (dictionary * vd);
+const char *atalkdict_get  (const dictionary * d, const char *section, const char * key, const char * def);
+int        atalkdict_set   (dictionary * vd, char *section, char * key, char * val);
+void       atalkdict_unset (dictionary * d, char *section, char * key);
+void       atalkdict_dump  (dictionary * d, FILE * out);
 
 #endif
index a2759aa18ca1cb455d73b0c1c6c3479a3c941882..d3764e53c339c15fd03add5e6518ca3c87899973 100644 (file)
@@ -36,7 +36,7 @@
 
 /* setgid directories */
 #ifndef DIRBITS
-# ifdef AFS
+# if (defined AFS) || (defined FREEBSD)
 #  define DIRBITS 0
 # else /* AFS */
 #  define DIRBITS S_ISGID
index 0e776c31cca07667a43926f5ad96d493163a25ba..c816d96494398436a6be6972ff53bf6300a73a25 100644 (file)
@@ -48,12 +48,12 @@ struct dsi_block {
     union {
         uint32_t dsi_code;   /* error code */
         uint32_t dsi_doff;   /* data offset */
-    };
+    } dsi_data;
     uint32_t dsi_len;        /* total data length */
     uint32_t dsi_reserved;   /* reserved field */
 };
 
-#define DSI_DATASIZ       8192
+#define DSI_DATASIZ       65536
 
 /* child and parent processes might interpret a couple of these
  * differently. */
@@ -138,7 +138,7 @@ typedef struct DSI {
 #define DSI_DEFQUANT        2           /* default attention quantum size */
 #define DSI_SERVQUANT_MAX   0xffffffff  /* server quantum */
 #define DSI_SERVQUANT_MIN   32000       /* minimum server quantum */
-#define DSI_SERVQUANT_DEF   0x0004A2E0L /* default server quantum */
+#define DSI_SERVQUANT_DEF   0x100000L   /* default server quantum (1 MB) */
 
 /* default port number */
 #define DSI_AFPOVERTCP_PORT 548
@@ -154,17 +154,15 @@ typedef struct DSI {
 #define DSI_RECONSOCKET      (1 << 7) /* we have a new socket from primary reconnect */
 #define DSI_RECONINPROG      (1 << 8) /* used in the new session in reconnect */
 #define DSI_AFP_LOGGED_OUT   (1 << 9) /* client called afp_logout, quit on next EOF from socket */
-#if 0
-#define DSI_GOT_ECONNRESET   (1 << 10) /* got ECONNRESET from client => exit */
-#endif
 
 /* basic initialization: dsi_init.c */
 extern DSI *dsi_init(AFPObj *obj, const char *hostname, const char *address, const char *port);
 extern void dsi_setstatus (DSI *, char *, const size_t);
 extern int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address, const char *port);
+extern void dsi_free(DSI *dsi);
 
 /* in dsi_getsess.c */
-extern int dsi_getsession (DSI *, server_child *, const int, afp_child_t **);
+extern int dsi_getsession (DSI *, server_child_t *, const int, afp_child_t **);
 extern void dsi_kill (int);
 
 
index 228970373a54567e6e35eec6935034938c5dffaf..fbbafafabef4169cc746e72dc3afa2aad2905a91 100644 (file)
 #include <sys/extattr.h>
 #endif
 
+/* FIXME: are the ACL includes really neccessary here ? */
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
+#ifdef HAVE_FREEBSD_SUNACL
+#include <sunacl.h>
+#endif
 
 #ifndef ENOATTR
 #define ENOATTR ENODATA
index 094065b9d5a7d6a799680eba3f2eac91c197e65a..47576bec029424ce74de5bbb4a48da544e904e75 100644 (file)
 
 #define EC_INIT int ret = 0
 #define EC_STATUS(a) ret = (a)
+#define EC_EXIT_STATUS(a) do { ret = (a); goto cleanup; } while (0)
 #define EC_FAIL do { ret = -1; goto cleanup; } while (0)
-#define EC_FAIL_LOG(a, ...)                     \
+#define EC_FAIL_LOG(...)                     \
     do {               \
-        LOG(log_error, logtype_default, a, __VA_ARGS__);   \
+        LOG(log_error, logtype_default, __VA_ARGS__);   \
         ret = -1;      \
         goto cleanup;  \
     } while (0)
index aebb56adffcdafdb9463b712afa9fe91685186e0..d11a2d4927a4752da0593fb3e54c2acc976cf57e 100755 (executable)
 #define FCE_DIR_DELETE      3
 #define FCE_FILE_CREATE     4
 #define FCE_DIR_CREATE      5
-#define FCE_TM_SIZE         6
 #define FCE_CONN_START     42
 #define FCE_CONN_BROKEN    99
 
+#define FCE_FIRST_EVENT     FCE_FILE_MODIFY /* keep in sync with last file event above */
+#define FCE_LAST_EVENT      FCE_DIR_CREATE  /* keep in sync with last file event above */
 
 /* fce_packet.fce_magic */
 #define FCE_PACKET_MAGIC  "at_fcapi"
@@ -31,6 +32,7 @@
  * Format is network byte order.
  */
 #define FCE_PACKET_HEADER_SIZE 8+1+1+4+2
+
 struct fce_packet
 {
     char magic[8];
@@ -41,21 +43,17 @@ struct fce_packet
     char data[MAXPATHLEN];
 };
 
+typedef uint32_t fce_ev_t;
+typedef enum { fce_file, fce_dir } fce_obj_t;
+
 struct path;
 struct ofork;
 
 void fce_pending_events(AFPObj *obj);
-
-int fce_register_delete_file( struct path *path );
-int fce_register_delete_dir( char *name );
-int fce_register_new_dir( struct path *path );
-int fce_register_new_file( struct path *path );
-int fce_register_file_modification( struct ofork *ofork );
-int fce_register_tm_size(const char *vol, size_t used);
-
+int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type);
 int fce_add_udp_socket(const char *target );  // IP or IP:Port
 int fce_set_coalesce(const char *coalesce_opt ); // all|delete|create
-int fce_set_events(const char *events);     /* fmod,fdel,ddel,fcre,dcre,tmsz (default is all except tmsz) */
+int fce_set_events(const char *events);     /* fmod,fdel,ddel,fcre,dcre */
 
 #define FCE_DEFAULT_PORT 12250
 #define FCE_DEFAULT_PORT_STRING "12250"
index b8412d15d7c7b300f8b90b3c94a58ddeb96e374b..603b5caa6c6093ad7cfec5719aa81e25eef0bbb3 100644 (file)
 #include <atalk/unicode.h>
 #include <atalk/uam.h>
 #include <atalk/iniparser.h>
+#ifdef WITH_DTRACE
+#include <atalk/afp_dtrace.h>
+#else
+/* List of empty dtrace macros */
+#define AFP_AFPFUNC_START(a,b)
+#define AFP_AFPFUNC_DONE(a, b)
+#define AFP_CNID_START(a)
+#define AFP_CNID_DONE()
+#define AFP_READ_START(a)
+#define AFP_READ_DONE()
+#define AFP_WRITE_START(a)
+#define AFP_WRITE_DONE()
+#endif
 
 /* #define DOSFILELEN 12 */             /* Type1, DOS-compat*/
 #define MACFILELEN 31                   /* Type2, HFS-compat */
 #define OPTION_CLOSEVOL      (1 << 1)
 #define OPTION_SERVERNOTIF   (1 << 2)
 #define OPTION_NOSENDFILE    (1 << 3)
-/* #define OPTION_CUSTOMICON    (1 << 4) */
+#define OPTION_VETOMSG       (1 << 4) /* whether to send an AFP message for veto file access */
 #define OPTION_AFP_READ_LOCK (1 << 5) /* whether to do AFP spec conforming read locks (default: no) */
 #define OPTION_ANNOUNCESSH   (1 << 6)
 #define OPTION_UUID          (1 << 7)
 #define OPTION_ACL2MACCESS   (1 << 8)
 #define OPTION_NOZEROCONF    (1 << 9)
-#define OPTION_KEEPSESSIONS  (1 << 10) /* preserve sessions across master afpd restart with SIGQUIT */
+#define OPTION_ACL2MODE      (1 << 10)
 #define OPTION_SHARE_RESERV  (1 << 11) /* whether to use Solaris fcntl F_SHARE locks */
+#define OPTION_DBUS_AFPSTATS (1 << 12) /* whether to run dbus thread for afpstats */
+#define OPTION_SPOTLIGHT     (1 << 13) /* whether to initialize Spotlight support */
+#define OPTION_SPOTLIGHT_VOL (1 << 14) /* whether spotlight shall be enabled by default for volumes */
+#define OPTION_RECVFILE      (1 << 15)
+#define OPTION_SPOTLIGHT_EXPR (1 << 16) /* whether to allow Spotlight logic expressions */
 
 #define PASSWD_NONE     0
 #define PASSWD_SET     (1 << 0)
@@ -80,17 +98,19 @@ struct afp_options {
     uint32_t server_quantum;
     int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
     char *hostname;
-    char *listen, *port;
+    char *listen, *interfaces, *port;
     char *Cnid_srv, *Cnid_port;
     char *configfile;
     char *uampath, *fqdn;
     char *sigconffile;
     char *uuidconf;
-    char *guest, *loginmesg, *keyfile, *passwdfile;
+    char *guest, *loginmesg, *keyfile, *passwdfile, *extmapfile;
     char *uamlist;
     char *signatureopt;
     unsigned char signature[16];
     char *k5service, *k5realm, *k5keytab;
+    size_t k5principal_buflen;
+    char *k5principal;
     char *unixcodepage, *maccodepage, *volcodepage;
     charset_t maccharset, unixcharset; 
     mode_t umask;
@@ -98,12 +118,20 @@ struct afp_options {
     gid_t admingid;
     int    volnamelen;
     /* default value for winbind authentication */
-    char *ntdomain, *ntseparator;
+    char *ntdomain, *ntseparator, *addomain;
     char *logconfig;
     char *logfile;
     char *mimicmodel;
     char *adminauthuser;
+    char *ignored_attr;
+    char *slmod_path;
+    int  splice_size;
+    char *cnid_mysql_host;
+    char *cnid_mysql_user;
+    char *cnid_mysql_pw;
+    char *cnid_mysql_db;
     struct afp_volume_name volfile;
+    uint64_t sparql_limit;
 };
 
 typedef struct AFPObj {
@@ -123,6 +151,7 @@ typedef struct AFPObj {
     gid_t *groups;
     int ngroups;
     int afp_version;
+    int cnx_cnt, cnx_max;
     /* Functions */
     void (*logout)(void);
     void (*exit)(int);
@@ -147,14 +176,9 @@ extern const char         *Cnid_port;
 extern int  get_afp_errno   (const int param);
 extern void afp_options_init (struct afp_options *);
 extern void afp_options_parse_cmdline(AFPObj *obj, int ac, char **av);
-extern void afp_options_free(struct afp_options *);
-extern void setmessage (const char *);
+extern int setmessage (const char *);
 extern void readmessage (AFPObj *);
 
-/* gettok.c */
-extern void initline   (int, char *);
-extern int  parseline  (int, char *);
-
 /* afp_util.c */
 extern const char *AfpNum2name (int );
 extern const char *AfpErr2name(int err);
index 14cddf669d671287020c6d24585106907789bbdd..0a75e4f64f9ccd5f3265769d4dc30f13c660ec91 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.h,v 1.2 2009-11-19 10:37:44 franklahm Exp $
  * $Name:  $
  */
 
index 0eefa4cb3aa64c8603d985d60741d7ff09ffe0a4..186c90e458f474331f7e7b88e6a6ab1eca97bba6 100644 (file)
@@ -10,8 +10,6 @@
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: iniparser.h,v 1.26 2011-03-02 20:15:13 ndevilla Exp $
-       $Revision: 1.26 $
 */
 
 #ifndef _INIPARSER_H_
 
 #include "dictionary.h"
 
-int        iniparser_getnsec(const dictionary * d);
-const char *iniparser_getsecname(const dictionary * d, int n);
-void       iniparser_dump_ini(const dictionary * d, FILE * f);
-void       iniparser_dump(const dictionary * d, FILE * f);
-const char *iniparser_getstring(const dictionary * d, const char *section, const char * key, const char * def);
-char       *iniparser_getstrdup(const dictionary * d, const char *section, const char * key, const char * def);
-int        iniparser_getint(const dictionary * d, const char *section, const char * key, int notfound);
-double     iniparser_getdouble(const dictionary * d, const char *section, const char * key, double notfound);
-int        iniparser_getboolean(const dictionary * d, const char *section, const char * key, int notfound);
-int        iniparser_set(dictionary * ini, char *section, char * key, char * val);
-void       iniparser_unset(dictionary * ini, char *section, char * key);
-int        iniparser_find_entry(const dictionary * ini, const char * entry) ;
-dictionary *iniparser_load(const char * ininame);
-void       iniparser_freedict(dictionary * d);
+int        atalk_iniparser_getnsec(const dictionary * d);
+const char *atalk_iniparser_getsecname(const dictionary * d, int n);
+void       atalk_iniparser_dump_ini(const dictionary * d, FILE * f);
+void       atalk_iniparser_dump(const dictionary * d, FILE * f);
+const char *atalk_iniparser_getstring(const dictionary * d, const char *section, const char * key, const char * def);
+char       *atalk_iniparser_getstrdup(const dictionary * d, const char *section, const char * key, const char * def);
+int        atalk_iniparser_getint(const dictionary * d, const char *section, const char * key, int notfound);
+double     atalk_iniparser_getdouble(const dictionary * d, const char *section, const char * key, double notfound);
+int        atalk_iniparser_getboolean(const dictionary * d, const char *section, const char * key, int notfound);
+int        atalk_iniparser_set(dictionary * ini, char *section, char * key, char * val);
+void       atalk_iniparser_unset(dictionary * ini, char *section, char * key);
+int        atalk_iniparser_find_entry(const dictionary * ini, const char * entry) ;
+dictionary *atalk_iniparser_load(const char * ininame);
+void       atalk_iniparser_freedict(dictionary * d);
 
 #endif
index 16e5484f6dd24822874f465a221918bac251ab9d..f01e0a064cce17e3216358a1bd2ad05e2eca4134 100644 (file)
@@ -7,6 +7,7 @@
 
 /* One function does the whole job */
 extern int acl_ldap_readconfig(dictionary *iniconfig);
+extern void acl_ldap_freeconfig(void);
 
 /* These are the prefvalues */
 extern char *ldap_server;
@@ -20,6 +21,8 @@ extern char *ldap_uuid_string;
 extern char *ldap_name_attr;
 extern char *ldap_group_attr;
 extern char *ldap_uid_attr;
+extern char *ldap_userfilter;
+extern char *ldap_groupfilter;
 extern int  ldap_uuid_encoding;
 
 typedef enum {
@@ -33,6 +36,7 @@ struct ldap_pref {
     int strorint;     /* string to just store in char * or convert to int ? */
     int intfromarray; /* convert to int, but use string to int mapping array pref_array[] */
     int valid;        /* -1 = mandatory, 0 = omittable/valid */
+    int valid_save;   /* copy of 'valid', used when resettting config */
 };
 
 struct pref_array {
index cf67bcad6f5260d8b1e0d42b3812e77a9b492521..3f1396558487994a65792543ffc14322043ab74f 100644 (file)
@@ -90,9 +90,10 @@ enum logtypes {
   logtype_cnid,
   logtype_afpd,
   logtype_dsi,
-  logtype_atalkd,
-  logtype_papd,
   logtype_uams,
+  logtype_fce,
+  logtype_ad,
+  logtype_sl,
   logtype_end_of_list_marker  /* don't put any logtypes after this */
 };
 
index 33459b3188b70a4c1e3732bcc5a2b808e9000d77..5b30e0bf60fdc17c66573ac1669fa87f48989d19 100644 (file)
@@ -21,9 +21,9 @@
 #include <atalk/volume.h>
 
 extern int        afp_config_parse(AFPObj *obj, char *processname);
-
+extern void       afp_config_free(AFPObj *obj);
 extern int        load_charset(struct vol *vol);
-extern int        load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *));
+extern int        load_volumes(AFPObj *obj, lv_flags_t flags);
 extern void       unload_volumes(AFPObj *obj);
 extern struct vol *getvolumes(void);
 extern struct vol *getvolbyvid(const uint16_t);
@@ -31,4 +31,9 @@ extern struct vol *getvolbypath(AFPObj *obj, const char *path);
 extern struct vol *getvolbyname(const char *name);
 extern void       volume_free(struct vol *vol);
 extern void       volume_unlink(struct vol *volume);
+
+/* Extension type/creator mapping */
+struct extmap *getdefextmap(void);
+struct extmap *getextmap(const char *path);
+
 #endif
index 1aab969094f0c22cb48b328abfa249ccdc3a4a89..f5f6486cfb634d0e585cef21923c7bd0c3e0928b 100644 (file)
 #define ATALKPATHCAT(a,b) a/**/b
 #endif
 
-
-/* lock file path. this should be re-organized a bit. */
-#if ! defined (_PATH_LOCKDIR)
-#  if defined (FHS_COMPATIBILITY) || defined (__NetBSD__) || defined (__OpenBSD__)
-#    define _PATH_LOCKDIR      "/var/run/"
-#  elif defined (BSD4_4)
-#    ifdef MACOSX_SERVER
-#      define _PATH_LOCKDIR    "/var/run/"
-#    else
-#      define _PATH_LOCKDIR    "/var/spool/lock/"
-#    endif
-#  elif defined (linux)
-#    define _PATH_LOCKDIR      "/var/lock/"
-#  else
-#    define _PATH_LOCKDIR      "/var/spool/locks/"
-#  endif
-#endif
-
-
-/*
- * netatalk paths
- */
-#define _PATH_AFPTKT           "/tmp/AFPtktXXXXXX"
-#define _PATH_AFP_IPC       ATALKPATHCAT(_PATH_LOCKDIR,"afpd_ipc")
-#if defined (FHS_COMPATIBILITY) || defined (__NetBSD__) || defined (__OpenBSD__)
-#  define _PATH_NETATALK_LOCK  ATALKPATHCAT(_PATH_LOCKDIR,"netatalk.pid")
-#else
-#  define _PATH_NETATALK_LOCK  ATALKPATHCAT(_PATH_LOCKDIR,"netatalk")
-#endif
-
 #endif /* atalk/paths.h */
 
index ad41bceccec2441f95ee4907547e1b93d39ce4ba..27ee305c7c85037058c4b04b655bc62a0a497d34 100644 (file)
@@ -8,45 +8,50 @@
 
 #include <sys/types.h>
 #include <arpa/inet.h>
+#include <pthread.h>
 
 /* useful stuff for child processes. most of this is hidden in 
  * server_child.c to ease changes in implementation */
 
-#define CHILD_NFORKS   2
-#define CHILD_ASPFORK  0
-#define CHILD_PAPFORK  0
-#define CHILD_DSIFORK  1
-
-typedef struct server_child {
-  void *fork;
-  int count, nsessions, nforks;
-} server_child;
-
-typedef struct server_child_data {
-  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
-  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
-  int       valid;             /* 1 if we have a clientid */
-  int       killed;            /* 1 if we already tried to kill the client */
-  int       disasociated; /* 1 if this is not a child, but a child from a previous afpd master */
-  uint32_t  time;              /* client boot time (from the mac client) */
-  uint32_t  idlen;             /* clientid len (from the Mac client) */
-  char      *clientid;  /* clientid (from the Mac client) */
-  int       ipc_fd; /* socket for IPC bw afpd parent and childs */
-  struct server_child_data **prevp, *next;
+#define CHILD_HASHSIZE 32
+
+/* One AFP session child process */
+typedef struct afp_child {
+    pid_t           afpch_pid;         /* afpd worker process pid (from the worker afpd process )*/
+    uid_t           afpch_uid;         /* user id of connected client (from the worker afpd process) */
+    int             afpch_valid;       /* 1 if we have a clientid */
+    int             afpch_killed;      /* 1 if we already tried to kill the client */
+    uint32_t        afpch_boottime;    /* client boot time (from the mac client) */
+    time_t          afpch_logintime;   /* time the child was added */
+    uint32_t        afpch_idlen;       /* clientid len (from the Mac client) */
+    char           *afpch_clientid;    /* clientid (from the Mac client) */
+    int             afpch_ipc_fd;      /* socket for IPC bw afpd parent and childs */
+    int16_t         afpch_state;       /* state of AFP session (eg active, sleeping, disconnected) */
+    char           *afpch_volumes;     /* mounted volumes */
+    struct afp_child **afpch_prevp;
+    struct afp_child *afpch_next;
 } afp_child_t;
 
+/* Info and table with all AFP session child processes */
+typedef struct {
+    pthread_mutex_t servch_lock;                    /* Lock */
+    int             servch_count;                   /* Current count of active AFP sessions */
+    int             servch_nsessions;               /* Number of allowed AFP sessions */
+    afp_child_t    *servch_table[CHILD_HASHSIZE];   /* Hashtable with data of AFP sesssions */
+} server_child_t;
+
 /* server_child.c */
-extern server_child *server_child_alloc (const int, const int);
-extern afp_child_t *server_child_add (server_child *, int, pid_t, int ipc_fd);
-extern int  server_child_remove (server_child *, const int, const pid_t);
-extern void server_child_free (server_child *);
-
-extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
-                                               const uint32_t len, char *id, uint32_t boottime);
-extern int  server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
-extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
-extern void server_child_handler (server_child *);
-extern void server_reset_signal (void);
+extern server_child_t *server_child_alloc(int);
+extern afp_child_t *server_child_add(server_child_t *, pid_t, int ipc_fd);
+extern int  server_child_remove(server_child_t *, pid_t);
+extern void server_child_free(server_child_t *);
+extern afp_child_t *server_child_resolve(server_child_t *childs, id_t pid);
+
+extern void server_child_kill(server_child_t *, int);
+extern void server_child_kill_one_by_id(server_child_t *children, pid_t pid, uid_t,
+                                        uint32_t len, char *id, uint32_t boottime);
+extern int  server_child_transfer_session(server_child_t *children, pid_t, uid_t, int, uint16_t);
+extern void server_child_handler(server_child_t *);
+extern void server_reset_signal(void);
 
 #endif
index aca04f80f8f33ea6b180083c252452836c21caff..0c38a7b75b7896bee929bedbffa24a476e0b73ec 100644 (file)
@@ -4,13 +4,14 @@
 #include <atalk/server_child.h>
 #include <atalk/globals.h>
 
+/* Remember to add IPC commands to server_ipc.c:ipc_cmd_str[] */
 #define IPC_DISCOLDSESSION   0
 #define IPC_GETSESSION       1
+#define IPC_STATE            2  /* pass AFP session state */
+#define IPC_VOLUMES          3  /* pass list of open volumes */
 
-extern int ipc_server_uds(const char *name);
-extern int ipc_client_uds(const char *name);
-extern int reconnect_ipc(AFPObj *);
-extern int ipc_server_read(server_child *children, int fd);
+extern int ipc_server_read(server_child_t *children, int fd);
 extern int ipc_child_write(int fd, uint16_t command, int len, void *token);
+extern int ipc_child_state(AFPObj *obj, uint16_t state);
 
 #endif /* IPC_GETSESSION_LOGIN */
diff --git a/include/atalk/spotlight.h b/include/atalk/spotlight.h
new file mode 100644 (file)
index 0000000..bafa52f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef SPOTLIGHT_H
+#define SPOTLIGHT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <atalk/dalloc.h>
+#include <atalk/globals.h>
+#include <atalk/volume.h>
+
+/**************************************************************************************************
+ * Spotlight module stuff
+ **************************************************************************************************/
+
+#define SL_MODULE_VERSION 1
+
+struct sl_module_export {
+    int sl_mod_version;
+    int (*sl_mod_init)        (void *);
+    int (*sl_mod_start_search)(void *);
+    int (*sl_mod_fetch_result)(void *);
+    int (*sl_mod_end_search)  (void *);
+    int (*sl_mod_fetch_attrs) (void *);
+    int (*sl_mod_error)       (void *);
+    int (*sl_mod_index_file)  (const void *);
+};
+
+extern int sl_mod_load(AFPObj *obj);
+extern void sl_index_file(const char *path);
+
+/**************************************************************************************************
+ * Spotlight RPC and marshalling stuff
+ **************************************************************************************************/
+
+/* FPSpotlightRPC subcommand codes */
+#define SPOTLIGHT_CMD_OPEN    1
+#define SPOTLIGHT_CMD_FLAGS   2
+#define SPOTLIGHT_CMD_RPC     3
+#define SPOTLIGHT_CMD_OPEN2   4
+
+/* Can be ored and used as flags */
+#define SL_ENC_LITTLE_ENDIAN 1
+#define SL_ENC_BIG_ENDIAN    2
+#define SL_ENC_UTF_16        4
+
+typedef DALLOC_CTX     sl_array_t;    /* an array of elements                                           */
+typedef DALLOC_CTX     sl_dict_t;     /* an array of key/value elements                                 */
+typedef DALLOC_CTX     sl_filemeta_t; /* contains one sl_array_t                                        */
+typedef int            sl_nil_t;      /* a nil element                                                  */
+typedef bool           sl_bool_t;     /* a boolean, we avoid bool_t as it's a define for something else */
+typedef struct timeval sl_time_t;     /* a boolean, we avoid bool_t as it's a define for something else */
+typedef struct {
+    char sl_uuid[16];
+}  sl_uuid_t;                         /* a UUID                                                         */
+typedef struct {
+    uint16_t   ca_unkn1;
+    uint32_t   ca_context;
+    DALLOC_CTX *ca_cnids;
+}  sl_cnids_t;                        /* an array of CNIDs                                              */
+
+/**************************************************************************************************
+ * Some helper stuff dealing with queries
+ **************************************************************************************************/
+
+/* Internal query state */
+typedef enum {
+    SLQ_STATE_NEW      = 1,           /* Query received from client                                     */
+    SLQ_STATE_RUNNING  = 2,           /* Query dispatched to Tracker                                    */
+    SLQ_STATE_DONE     = 3,           /* Tracker finished                                               */
+    SLQ_STATE_END      = 4,           /* Query results returned to client                               */
+    SLQ_STATE_ATTRS    = 5            /* Fetch metadata for an object                                   */
+} slq_state_t;
+
+/* Internal query data structure */
+typedef struct _slq_t {
+    struct list_head  slq_list;           /* queries are stored in a list                                   */
+    slq_state_t       slq_state;          /* State                                                          */
+    AFPObj           *slq_obj;            /* global AFPObj handle                                           */
+    const struct vol *slq_vol;            /* volume handle                                                  */
+    DALLOC_CTX       *slq_reply;          /* reply handle                                                   */
+    time_t            slq_time;           /* timestamp where we received this query                         */
+    uint64_t          slq_ctx1;           /* client context 1                                               */
+    uint64_t          slq_ctx2;           /* client context 2                                               */
+    sl_array_t       *slq_reqinfo;        /* array with requested metadata                                  */
+    const char       *slq_qstring;        /* the Spotlight query string                                     */
+    uint64_t         *slq_cnids;          /* Pointer to array with CNIDs to which a query applies           */
+    size_t            slq_cnids_num;      /* Size of slq_cnids array                                        */
+    const char       *slq_path;           /* Path to file or dir, used in fetchAttributes                   */
+    void             *slq_tracker_cursor; /* Tracker SPARQL query result cursor                             */
+    bool              slq_allow_expr;     /* Whether to allow logic expressions                             */
+    uint64_t          slq_result_limit;   /* Whether to LIMIT SPARQL results, default of 0 means no limit   */
+} slq_t;
+
+/**************************************************************************************************
+ * Function declarations
+ **************************************************************************************************/
+
+extern int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen);
+extern int sl_pack(DALLOC_CTX *query, char *buf);
+extern int sl_unpack(DALLOC_CTX *query, const char *buf);
+extern void configure_spotlight_attributes(const char *attributes);
+
+#endif /* SPOTLIGHT_H */
diff --git a/include/atalk/talloc.h b/include/atalk/talloc.h
new file mode 100644 (file)
index 0000000..96c7e24
--- /dev/null
@@ -0,0 +1,1711 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+
+   Copyright (C) Andrew Tridgell 2004-2005
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup talloc The talloc API
+ *
+ * talloc is a hierarchical, reference counted memory pool system with
+ * destructors. It is the core memory allocator used in Samba.
+ *
+ * @{
+ */
+
+#define TALLOC_VERSION_MAJOR 2
+#define TALLOC_VERSION_MINOR 0
+
+int talloc_version_major(void);
+int talloc_version_minor(void);
+
+/**
+ * @brief Define a talloc parent type
+ *
+ * As talloc is a hierarchial memory allocator, every talloc chunk is a
+ * potential parent to other talloc chunks. So defining a separate type for a
+ * talloc chunk is not strictly necessary. TALLOC_CTX is defined nevertheless,
+ * as it provides an indicator for function arguments. You will frequently
+ * write code like
+ *
+ * @code
+ *      struct foo *foo_create(TALLOC_CTX *mem_ctx)
+ *      {
+ *              struct foo *result;
+ *              result = talloc(mem_ctx, struct foo);
+ *              if (result == NULL) return NULL;
+ *                      ... initialize foo ...
+ *              return result;
+ *      }
+ * @endcode
+ *
+ * In this type of allocating functions it is handy to have a general
+ * TALLOC_CTX type to indicate which parent to put allocated structures on.
+ */
+typedef void TALLOC_CTX;
+
+/*
+  this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s)    #s
+#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Create a new talloc context.
+ *
+ * The talloc() macro is the core of the talloc library. It takes a memory
+ * context and a type, and returns a pointer to a new area of memory of the
+ * given type.
+ *
+ * The returned pointer is itself a talloc context, so you can use it as the
+ * context argument to more calls to talloc if you wish.
+ *
+ * The returned pointer is a "child" of the supplied context. This means that if
+ * you talloc_free() the context then the new child disappears as well.
+ * Alternatively you can free just the child.
+ *
+ * @param[in]  ctx      A talloc context to create a new reference on or NULL to
+ *                      create a new top level context.
+ *
+ * @param[in]  type     The type of memory to allocate.
+ *
+ * @return              A type casted talloc context or NULL on error.
+ *
+ * @code
+ *      unsigned int *a, *b;
+ *
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ * @endcode
+ *
+ * @see talloc_zero
+ * @see talloc_array
+ * @see talloc_steal
+ * @see talloc_free
+ */
+void *talloc(const void *ctx, #type);
+#else
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+void *_talloc(const void *context, size_t size);
+#endif
+
+/**
+ * @brief Create a new top level talloc context.
+ *
+ * This function creates a zero length named talloc context as a top level
+ * context. It is equivalent to:
+ *
+ * @code
+ *      talloc_named(NULL, 0, fmt, ...);
+ * @endcode
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Additional printf-style arguments.
+ *
+ * @return              The allocated memory chunk, NULL on error.
+ *
+ * @see talloc_named()
+ */
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+
+#ifdef DOXYGEN
+/**
+ * @brief Free a chunk of talloc memory.
+ *
+ * The talloc_free() function frees a piece of talloc memory, and all its
+ * children. You can call talloc_free() on any pointer returned by
+ * talloc().
+ *
+ * The return value of talloc_free() indicates success or failure, with 0
+ * returned for success and -1 for failure. A possible failure condition
+ * is if the pointer had a destructor attached to it and the destructor
+ * returned -1. See talloc_set_destructor() for details on
+ * destructors. Likewise, if "ptr" is NULL, then the function will make
+ * no modifications and return -1.
+ *
+ * From version 2.0 and onwards, as a special case, talloc_free() is
+ * refused on pointers that have more than one parent associated, as talloc
+ * would have no way of knowing which parent should be removed. This is
+ * different from older versions in the sense that always the reference to
+ * the most recently established parent has been destroyed. Hence to free a
+ * pointer that has more than one parent please use talloc_unlink().
+ *
+ * To help you find problems in your code caused by this behaviour, if
+ * you do try and free a pointer with more than one parent then the
+ * talloc logging function will be called to give output like this:
+ *
+ * @code
+ *   ERROR: talloc_free with references at some_dir/source/foo.c:123
+ *     reference at some_dir/source/other.c:325
+ *     reference at some_dir/source/third.c:121
+ * @endcode
+ *
+ * Please see the documentation for talloc_set_log_fn() and
+ * talloc_set_log_stderr() for more information on talloc logging
+ * functions.
+ *
+ * talloc_free() operates recursively on its children.
+ *
+ * @param[in]  ptr      The chunk to be freed.
+ *
+ * @return              Returns 0 on success and -1 on error. A possible
+ *                      failure condition is if the pointer had a destructor
+ *                      attached to it and the destructor returned -1. Likewise,
+ *                      if "ptr" is NULL, then the function will make no
+ *                      modifications and returns -1.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *
+ *      talloc_free(a); // Frees a and b
+ * @endcode
+ *
+ * @see talloc_set_destructor()
+ * @see talloc_unlink()
+ */
+int talloc_free(void *ptr);
+#else
+#define talloc_free(ctx) _talloc_free(ctx, __location__)
+int _talloc_free(void *ptr, const char *location);
+#endif
+
+/**
+ * @brief Free a talloc chunk's children.
+ *
+ * The function walks along the list of all children of a talloc context and
+ * talloc_free()s only the children, not the context itself.
+ *
+ * A NULL argument is handled as no-op.
+ *
+ * @param[in]  ptr      The chunk that you want to free the children of
+ *                      (NULL is allowed too)
+ */
+void talloc_free_children(void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Assign a destructor function to be called when a chunk is freed.
+ *
+ * The function talloc_set_destructor() sets the "destructor" for the pointer
+ * "ptr". A destructor is a function that is called when the memory used by a
+ * pointer is about to be released. The destructor receives the pointer as an
+ * argument, and should return 0 for success and -1 for failure.
+ *
+ * The destructor can do anything it wants to, including freeing other pieces
+ * of memory. A common use for destructors is to clean up operating system
+ * resources (such as open file descriptors) contained in the structure the
+ * destructor is placed on.
+ *
+ * You can only place one destructor on a pointer. If you need more than one
+ * destructor then you can create a zero-length child of the pointer and place
+ * an additional destructor on that.
+ *
+ * To remove a destructor call talloc_set_destructor() with NULL for the
+ * destructor.
+ *
+ * If your destructor attempts to talloc_free() the pointer that it is the
+ * destructor for then talloc_free() will return -1 and the free will be
+ * ignored. This would be a pointless operation anyway, as the destructor is
+ * only called when the memory is just about to go away.
+ *
+ * @param[in]  ptr      The talloc chunk to add a destructor to.
+ *
+ * @param[in]  destructor  The destructor function to be called. NULL to remove
+ *                         it.
+ *
+ * Example:
+ * @code
+ *      static int destroy_fd(int *fd) {
+ *              close(*fd);
+ *              return 0;
+ *      }
+ *
+ *      int *open_file(const char *filename) {
+ *              int *fd = talloc(NULL, int);
+ *              *fd = open(filename, O_RDONLY);
+ *              if (*fd < 0) {
+ *                      talloc_free(fd);
+ *                      return NULL;
+ *              }
+ *              // Whenever they free this, we close the file.
+ *              talloc_set_destructor(fd, destroy_fd);
+ *              return fd;
+ *      }
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_free()
+ */
+void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
+
+/**
+ * @brief Change a talloc chunk's parent.
+ *
+ * The talloc_steal() function changes the parent context of a talloc
+ * pointer. It is typically used when the context that the pointer is
+ * currently a child of is going to be freed and you wish to keep the
+ * memory for a longer time.
+ *
+ * To make the changed hierarchy less error-prone, you might consider to use
+ * talloc_move().
+ *
+ * If you try and call talloc_steal() on a pointer that has more than one
+ * parent then the result is ambiguous. Talloc will choose to remove the
+ * parent that is currently indicated by talloc_parent() and replace it with
+ * the chosen parent. You will also get a message like this via the talloc
+ * logging functions:
+ *
+ * @code
+ *   WARNING: talloc_steal with references at some_dir/source/foo.c:123
+ *     reference at some_dir/source/other.c:325
+ *     reference at some_dir/source/third.c:121
+ * @endcode
+ *
+ * To unambiguously change the parent of a pointer please see the function
+ * talloc_reparent(). See the talloc_set_log_fn() documentation for more
+ * information on talloc logging.
+ *
+ * @param[in]  new_ctx  The new parent context.
+ *
+ * @param[in]  ptr      The talloc chunk to move.
+ *
+ * @return              Returns the pointer that you pass it. It does not have
+ *                      any failure modes.
+ *
+ * @note It is possible to produce loops in the parent/child relationship
+ * if you are not careful with talloc_steal(). No guarantees are provided
+ * as to your sanity or the safety of your data if you do this.
+ */
+void *talloc_steal(const void *new_ctx, const void *ptr);
+#else /* DOXYGEN */
+/* try to make talloc_set_destructor() and talloc_steal() type safe,
+   if we have a recent gcc */
+#if (__GNUC__ >= 3)
+#define _TALLOC_TYPEOF(ptr) __typeof__(ptr)
+#define talloc_set_destructor(ptr, function)                                 \
+       do {                                                                  \
+               int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function);       \
+               _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \
+       } while(0)
+/* this extremely strange macro is to avoid some braindamaged warning
+   stupidity in gcc 4.1.x */
+#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__); __talloc_steal_ret; })
+#else /* __GNUC__ >= 3 */
+#define talloc_set_destructor(ptr, function) \
+       _talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__)
+#endif /* __GNUC__ >= 3 */
+void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
+void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location);
+#endif /* DOXYGEN */
+
+/**
+ * @brief Assign a name to a talloc chunk.
+ *
+ * Each talloc pointer has a "name". The name is used principally for
+ * debugging purposes, although it is also possible to set and get the name on
+ * a pointer in as a way of "marking" pointers in your code.
+ *
+ * The main use for names on pointer is for "talloc reports". See
+ * talloc_report() and talloc_report_full() for details. Also see
+ * talloc_enable_leak_report() and talloc_enable_leak_report_full().
+ *
+ * The talloc_set_name() function allocates memory as a child of the
+ * pointer. It is logically equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...));
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc chunk to assign a name to.
+ *
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Add printf-style additional arguments.
+ *
+ * @return              The assigned name, NULL on error.
+ *
+ * @note Multiple calls to talloc_set_name() will allocate more memory without
+ * releasing the name. All of the memory is released when the ptr is freed
+ * using talloc_free().
+ */
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#ifdef DOXYGEN
+/**
+ * @brief Change a talloc chunk's parent.
+ *
+ * This function has the same effect as talloc_steal(), and additionally sets
+ * the source pointer to NULL. You would use it like this:
+ *
+ * @code
+ *      struct foo *X = talloc(tmp_ctx, struct foo);
+ *      struct foo *Y;
+ *      Y = talloc_move(new_ctx, &X);
+ * @endcode
+ *
+ * @param[in]  new_ctx  The new parent context.
+ *
+ * @param[in]  pptr     Pointer to the talloc chunk to move.
+ *
+ * @return              The pointer of the talloc chunk it has been moved to,
+ *                      NULL on error.
+ */
+void *talloc_move(const void *new_ctx, void **pptr);
+#else
+#define talloc_move(ctx, pptr) (_TALLOC_TYPEOF(*(pptr)))_talloc_move((ctx),(void *)(pptr))
+void *_talloc_move(const void *new_ctx, const void *pptr);
+#endif
+
+/**
+ * @brief Assign a name to a talloc chunk.
+ *
+ * The function is just like talloc_set_name(), but it takes a string constant,
+ * and is much faster. It is extensively used by the "auto naming" macros, such
+ * as talloc_p().
+ *
+ * This function does not allocate any memory. It just copies the supplied
+ * pointer into the internal representation of the talloc ptr. This means you
+ * must not pass a name pointer to memory that will disappear before the ptr
+ * is freed with talloc_free().
+ *
+ * @param[in]  ptr      The talloc chunk to assign a name to.
+ *
+ * @param[in]  name     Format string for the name.
+ */
+void talloc_set_name_const(const void *ptr, const char *name);
+
+/**
+ * @brief Create a named talloc chunk.
+ *
+ * The talloc_named() function creates a named talloc pointer. It is
+ * equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(context, size);
+ *      talloc_set_name(ptr, fmt, ....);
+ * @endcode
+ *
+ * @param[in]  context  The talloc context to hang the result off.
+ *
+ * @param[in]  size     Number of char's that you want to allocate.
+ *
+ * @param[in]  fmt      Format string for the name.
+ *
+ * @param[in]  ...      Additional printf-style arguments.
+ *
+ * @return              The allocated memory chunk, NULL on error.
+ *
+ * @see talloc_set_name()
+ */
+void *talloc_named(const void *context, size_t size,
+                  const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+
+/**
+ * @brief Basic routine to allocate a chunk of memory.
+ *
+ * This is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(context, size);
+ *      talloc_set_name_const(ptr, name);
+ * @endcode
+ *
+ * @param[in]  context  The parent context.
+ *
+ * @param[in]  size     The number of char's that we want to allocate.
+ *
+ * @param[in]  name     The name the talloc block has.
+ *
+ * @return             The allocated memory chunk, NULL on error.
+ */
+void *talloc_named_const(const void *context, size_t size, const char *name);
+
+#ifdef DOXYGEN
+/**
+ * @brief Untyped allocation.
+ *
+ * The function should be used when you don't have a convenient type to pass to
+ * talloc(). Unlike talloc(), it is not type safe (as it returns a void *), so
+ * you are on your own for type checking.
+ *
+ * Best to use talloc() or talloc_array() instead.
+ *
+ * @param[in]  ctx     The talloc context to hang the result off.
+ *
+ * @param[in]  size    Number of char's that you want to allocate.
+ *
+ * @return             The allocated memory chunk, NULL on error.
+ *
+ * Example:
+ * @code
+ *      void *mem = talloc_size(NULL, 100);
+ * @endcode
+ */
+void *talloc_size(const void *ctx, size_t size);
+#else
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate into a typed pointer.
+ *
+ * The talloc_ptrtype() macro should be used when you have a pointer and want
+ * to allocate memory to point at with this pointer. When compiling with
+ * gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() and
+ * talloc_get_name() will return the current location in the source file and
+ * not the type.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The pointer you want to assign the result to.
+ *
+ * @return              The properly casted allocated memory chunk, NULL on
+ *                      error.
+ *
+ * Example:
+ * @code
+ *       unsigned int *a = talloc_ptrtype(NULL, a);
+ * @endcode
+ */
+void *talloc_ptrtype(const void *ctx, #type);
+#else
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a new 0-sized talloc chunk.
+ *
+ * This is a utility macro that creates a new memory context hanging off an
+ * existing context, automatically naming it "talloc_new: __location__" where
+ * __location__ is the source line it is called from. It is particularly
+ * useful for creating a new temporary working context.
+ *
+ * @param[in]  ctx      The talloc parent context.
+ *
+ * @return              A new talloc chunk, NULL on error.
+ */
+void *talloc_new(const void *ctx);
+#else
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a 0-initizialized structure.
+ *
+ * The macro is equivalent to:
+ *
+ * @code
+ *      ptr = talloc(ctx, type);
+ *      if (ptr) memset(ptr, 0, sizeof(type));
+ * @endcode
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @return              Pointer to a piece of memory, properly cast to 'type *',
+ *                      NULL on error.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc_zero(NULL, unsigned int);
+ *      b = talloc_zero(a, unsigned int);
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_zero_size()
+ * @see talloc_zero_array()
+ */
+void *talloc_zero(const void *ctx, #type);
+
+/**
+ * @brief Allocate untyped, 0-initialized memory.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  size     Number of char's that you want to allocate.
+ *
+ * @return              The allocated memory chunk.
+ */
+void *talloc_zero_size(const void *ctx, size_t size);
+#else
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+#endif
+
+/**
+ * @brief Return the name of a talloc chunk.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The current name for the given talloc pointer.
+ *
+ * @see talloc_set_name()
+ */
+const char *talloc_get_name(const void *ptr);
+
+/**
+ * @brief Verify that a talloc chunk carries a specified name.
+ *
+ * This function checks if a pointer has the specified name. If it does
+ * then the pointer is returned.
+ *
+ * @param[in]  ptr       The talloc chunk to check.
+ *
+ * @param[in]  name      The name to check against.
+ *
+ * @return               The pointer if the name matches, NULL if it doesn't.
+ */
+void *talloc_check_name(const void *ptr, const char *name);
+
+/**
+ * @brief Get the parent chunk of a pointer.
+ *
+ * @param[in]  ptr      The talloc pointer to inspect.
+ *
+ * @return              The talloc parent of ptr, NULL on error.
+ */
+void *talloc_parent(const void *ptr);
+
+/**
+ * @brief Get a talloc chunk's parent name.
+ *
+ * @param[in]  ptr      The talloc pointer to inspect.
+ *
+ * @return              The name of ptr's parent chunk.
+ */
+const char *talloc_parent_name(const void *ptr);
+
+/**
+ * @brief Get the total size of a talloc chunk including its children.
+ *
+ * The function returns the total size in bytes used by this pointer and all
+ * child pointers. Mostly useful for debugging.
+ *
+ * Passing NULL is allowed, but it will only give a meaningful result if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The total size.
+ */
+size_t talloc_total_size(const void *ptr);
+
+/**
+ * @brief Get the number of talloc chunks hanging off a chunk.
+ *
+ * The talloc_total_blocks() function returns the total memory block
+ * count used by this pointer and all child pointers. Mostly useful for
+ * debugging.
+ *
+ * Passing NULL is allowed, but it will only give a meaningful result if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @return              The total size.
+ */
+size_t talloc_total_blocks(const void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Duplicate a memory area into a talloc chunk.
+ *
+ * The function is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(ctx, size);
+ *      if (ptr) memcpy(ptr, p, size);
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The memory chunk you want to duplicate.
+ *
+ * @param[in]  size     Number of char's that you want copy.
+ *
+ * @return              The allocated memory chunk.
+ *
+ * @see talloc_size()
+ */
+void *talloc_memdup(const void *t, const void *p, size_t size);
+#else
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Assign a type to a talloc chunk.
+ *
+ * This macro allows you to force the name of a pointer to be of a particular
+ * type. This can be used in conjunction with talloc_get_type() to do type
+ * checking on void* pointers.
+ *
+ * It is equivalent to this:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, #type)
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc chunk to assign the type to.
+ *
+ * @param[in]  type     The type to assign.
+ */
+void talloc_set_type(const char *ptr, #type);
+
+/**
+ * @brief Get a typed pointer out of a talloc pointer.
+ *
+ * This macro allows you to do type checking on talloc pointers. It is
+ * particularly useful for void* private pointers. It is equivalent to
+ * this:
+ *
+ * @code
+ *      (type *)talloc_check_name(ptr, #type)
+ * @endcode
+ *
+ * @param[in]  ptr      The talloc pointer to check.
+ *
+ * @param[in]  type     The type to check against.
+ *
+ * @return              The properly casted pointer given by ptr, NULL on error.
+ */
+type *talloc_get_type(const void *ptr, #type);
+#else
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Safely turn a void pointer into a typed pointer.
+ *
+ * This macro is used together with talloc(mem_ctx, struct foo). If you had to
+ * assing the talloc chunk pointer to some void pointer variable,
+ * talloc_get_type_abort() is the recommended way to get the convert the void
+ * pointer back to a typed pointer.
+ *
+ * @param[in]  ptr      The void pointer to convert.
+ *
+ * @param[in]  type     The type that this chunk contains
+ *
+ * @return              The same value as ptr, type-checked and properly cast.
+ */
+void *talloc_get_type_abort(const void *ptr, #type);
+#else
+#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location);
+#endif
+
+/**
+ * @brief Find a parent context by name.
+ *
+ * Find a parent memory context of the current context that has the given
+ * name. This can be very useful in complex programs where it may be
+ * difficult to pass all information down to the level you need, but you
+ * know the structure you want is a parent of another context.
+ *
+ * @param[in]  ctx      The talloc chunk to start from.
+ *
+ * @param[in]  name     The name of the parent we look for.
+ *
+ * @return              The memory context we are looking for, NULL if not
+ *                      found.
+ */
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+
+#ifdef DOXYGEN
+/**
+ * @brief Find a parent context by type.
+ *
+ * Find a parent memory context of the current context that has the given
+ * name. This can be very useful in complex programs where it may be
+ * difficult to pass all information down to the level you need, but you
+ * know the structure you want is a parent of another context.
+ *
+ * Like talloc_find_parent_byname() but takes a type, making it typesafe.
+ *
+ * @param[in]  ptr      The talloc chunk to start from.
+ *
+ * @param[in]  type     The type of the parent to look for.
+ *
+ * @return              The memory context we are looking for, NULL if not
+ *                      found.
+ */
+void *talloc_find_parent_bytype(const void *ptr, #type);
+#else
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+#endif
+
+/**
+ * @brief Allocate a talloc pool.
+ *
+ * A talloc pool is a pure optimization for specific situations. In the
+ * release process for Samba 3.2 we found out that we had become considerably
+ * slower than Samba 3.0 was. Profiling showed that malloc(3) was a large CPU
+ * consumer in benchmarks. For Samba 3.2 we have internally converted many
+ * static buffers to dynamically allocated ones, so malloc(3) being beaten
+ * more was no surprise. But it made us slower.
+ *
+ * talloc_pool() is an optimization to call malloc(3) a lot less for the use
+ * pattern Samba has: The SMB protocol is mainly a request/response protocol
+ * where we have to allocate a certain amount of memory per request and free
+ * that after the SMB reply is sent to the client.
+ *
+ * talloc_pool() creates a talloc chunk that you can use as a talloc parent
+ * exactly as you would use any other ::TALLOC_CTX. The difference is that
+ * when you talloc a child of this pool, no malloc(3) is done. Instead, talloc
+ * just increments a pointer inside the talloc_pool. This also works
+ * recursively. If you use the child of the talloc pool as a parent for
+ * grand-children, their memory is also taken from the talloc pool.
+ *
+ * If you talloc_free() children of a talloc pool, the memory is not given
+ * back to the system. Instead, free(3) is only called if the talloc_pool()
+ * itself is released with talloc_free().
+ *
+ * The downside of a talloc pool is that if you talloc_move() a child of a
+ * talloc pool to a talloc parent outside the pool, the whole pool memory is
+ * not free(3)'ed until that moved chunk is also talloc_free()ed.
+ *
+ * @param[in]  context  The talloc context to hang the result off.
+ *
+ * @param[in]  size     Size of the talloc pool.
+ *
+ * @return              The allocated talloc pool, NULL on error.
+ */
+void *talloc_pool(const void *context, size_t size);
+
+/**
+ * @brief Free a talloc chunk and NULL out the pointer.
+ *
+ * TALLOC_FREE() frees a pointer and sets it to NULL. Use this if you want
+ * immediate feedback (i.e. crash) if you use a pointer after having free'ed
+ * it.
+ *
+ * @param[in]  ctx      The chunk to be freed.
+ */
+#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
+
+/* @} ******************************************************************/
+
+/**
+ * \defgroup talloc_ref The talloc reference function.
+ * @ingroup talloc
+ *
+ * This module contains the definitions around talloc references
+ *
+ * @{
+ */
+
+/**
+ * @brief Increase the reference count of a talloc chunk.
+ *
+ * The talloc_increase_ref_count(ptr) function is exactly equivalent to:
+ *
+ * @code
+ *      talloc_reference(NULL, ptr);
+ * @endcode
+ *
+ * You can use either syntax, depending on which you think is clearer in
+ * your code.
+ *
+ * @param[in]  ptr      The pointer to increase the reference count.
+ *
+ * @return              0 on success, -1 on error.
+ */
+int talloc_increase_ref_count(const void *ptr);
+
+/**
+ * @brief Get the number of references to a talloc chunk.
+ *
+ * @param[in]  ptr      The pointer to retrieve the reference count from.
+ *
+ * @return              The number of references.
+ */
+size_t talloc_reference_count(const void *ptr);
+
+#ifdef DOXYGEN
+/**
+ * @brief Create an additional talloc parent to a pointer.
+ *
+ * The talloc_reference() function makes "context" an additional parent of
+ * ptr. Each additional reference consumes around 48 bytes of memory on intel
+ * x86 platforms.
+ *
+ * If ptr is NULL, then the function is a no-op, and simply returns NULL.
+ *
+ * After creating a reference you can free it in one of the following ways:
+ *
+ * - you can talloc_free() any parent of the original pointer. That
+ *   will reduce the number of parents of this pointer by 1, and will
+ *   cause this pointer to be freed if it runs out of parents.
+ *
+ * - you can talloc_free() the pointer itself if it has at maximum one
+ *   parent. This behaviour has been changed since the release of version
+ *   2.0. Further informations in the description of "talloc_free".
+ *
+ * For more control on which parent to remove, see talloc_unlink()
+ * @param[in]  ctx      The additional parent.
+ *
+ * @param[in]  ptr      The pointer you want to create an additional parent for.
+ *
+ * @return              The original pointer 'ptr', NULL if talloc ran out of
+ *                      memory in creating the reference.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b, *c;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(NULL, unsigned int);
+ *      c = talloc(a, unsigned int);
+ *      // b also serves as a parent of c.
+ *      talloc_reference(b, c);
+ * @endcode
+ *
+ * @see talloc_unlink()
+ */
+void *talloc_reference(const void *ctx, const void *ptr);
+#else
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference_loc((ctx),(ptr), __location__)
+void *_talloc_reference_loc(const void *context, const void *ptr, const char *location);
+#endif
+
+/**
+ * @brief Remove a specific parent from a talloc chunk.
+ *
+ * The function removes a specific parent from ptr. The context passed must
+ * either be a context used in talloc_reference() with this pointer, or must be
+ * a direct parent of ptr.
+ *
+ * You can just use talloc_free() instead of talloc_unlink() if there
+ * is at maximum one parent. This behaviour has been changed since the
+ * release of version 2.0. Further informations in the description of
+ * "talloc_free".
+ *
+ * @param[in]  context  The talloc parent to remove.
+ *
+ * @param[in]  ptr      The talloc ptr you want to remove the parent from.
+ *
+ * @return              0 on success, -1 on error.
+ *
+ * @note If the parent has already been removed using talloc_free() then
+ * this function will fail and will return -1.  Likewise, if ptr is NULL,
+ * then the function will make no modifications and return -1.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b, *c;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(NULL, unsigned int);
+ *      c = talloc(a, unsigned int);
+ *      // b also serves as a parent of c.
+ *      talloc_reference(b, c);
+ *      talloc_unlink(b, c);
+ * @endcode
+ */
+int talloc_unlink(const void *context, void *ptr);
+
+/**
+ * @brief Provide a talloc context that is freed at program exit.
+ *
+ * This is a handy utility function that returns a talloc context
+ * which will be automatically freed on program exit. This can be used
+ * to reduce the noise in memory leak reports.
+ *
+ * Never use this in code that might be used in objects loaded with
+ * dlopen and unloaded with dlclose. talloc_autofree_context()
+ * internally uses atexit(3). Some platforms like modern Linux handles
+ * this fine, but for example FreeBSD does not deal well with dlopen()
+ * and atexit() used simultaneously: dlclose() does not clean up the
+ * list of atexit-handlers, so when the program exits the code that
+ * was registered from within talloc_autofree_context() is gone, the
+ * program crashes at exit.
+ *
+ * @return              A talloc context, NULL on error.
+ */
+void *talloc_autofree_context(void);
+
+/**
+ * @brief Get the size of a talloc chunk.
+ *
+ * This function lets you know the amount of memory allocated so far by
+ * this context. It does NOT account for subcontext memory.
+ * This can be used to calculate the size of an array.
+ *
+ * @param[in]  ctx      The talloc chunk.
+ *
+ * @return              The size of the talloc chunk.
+ */
+size_t talloc_get_size(const void *ctx);
+
+/**
+ * @brief Show the parentage of a context.
+ *
+ * @param[in]  context            The talloc context to look at.
+ *
+ * @param[in]  file               The output to use, a file, stdout or stderr.
+ */
+void talloc_show_parents(const void *context, FILE *file);
+
+/**
+ * @brief Check if a context is parent of a talloc chunk.
+ *
+ * This checks if context is referenced in the talloc hierarchy above ptr.
+ *
+ * @param[in]  context  The assumed talloc context.
+ *
+ * @param[in]  ptr      The talloc chunk to check.
+ *
+ * @return              Return 1 if this is the case, 0 if not.
+ */
+int talloc_is_parent(const void *context, const void *ptr);
+
+/**
+ * @brief Change the parent context of a talloc pointer.
+ *
+ * The function changes the parent context of a talloc pointer. It is typically
+ * used when the context that the pointer is currently a child of is going to be
+ * freed and you wish to keep the memory for a longer time.
+ *
+ * The difference between talloc_reparent() and talloc_steal() is that
+ * talloc_reparent() can specify which parent you wish to change. This is
+ * useful when a pointer has multiple parents via references.
+ *
+ * @param[in]  old_parent
+ * @param[in]  new_parent
+ * @param[in]  ptr
+ *
+ * @return              Return the pointer you passed. It does not have any
+ *                      failure modes.
+ */
+void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_array The talloc array functions
+ * @ingroup talloc
+ *
+ * Talloc contains some handy helpers for handling Arrays conveniently
+ *
+ * @{
+ */
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array.
+ *
+ * The macro is equivalent to:
+ *
+ * @code
+ *      (type *)talloc_size(ctx, sizeof(type) * count);
+ * @endcode
+ *
+ * except that it provides integer overflow protection for the multiply,
+ * returning NULL if the multiply overflows.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @param[in]  count    The number of 'type' elements you want to allocate.
+ *
+ * @return              The allocated result, properly cast to 'type *', NULL on
+ *                      error.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc_zero(NULL, unsigned int);
+ *      b = talloc_array(a, unsigned int, 100);
+ * @endcode
+ *
+ * @see talloc()
+ * @see talloc_zero_array()
+ */
+void *talloc_array(const void *ctx, #type, unsigned count);
+#else
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  size     The size of an array element.
+ *
+ * @param[in]  count    The number of elements you want to allocate.
+ *
+ * @return              The allocated result, NULL on error.
+ */
+void *talloc_array_size(const void *ctx, size_t size, unsigned count);
+#else
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate an array into a typed pointer.
+ *
+ * The macro should be used when you have a pointer to an array and want to
+ * allocate memory of an array to point at with this pointer. When compiling
+ * with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size()
+ * and talloc_get_name() will return the current location in the source file
+ * and not the type.
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  ptr      The pointer you want to assign the result to.
+ *
+ * @param[in]  count    The number of elements you want to allocate.
+ *
+ * @return              The allocated memory chunk, properly casted. NULL on
+ *                      error.
+ */
+void *talloc_array_ptrtype(const void *ctx, const void *ptr, unsigned count);
+#else
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Get the number of elements in a talloc'ed array.
+ *
+ * A talloc chunk carries its own size, so for talloc'ed arrays it is not
+ * necessary to store the number of elements explicitly.
+ *
+ * @param[in]  ctx      The allocated array.
+ *
+ * @return              The number of elements in ctx.
+ */
+size_t talloc_array_length(const void *ctx);
+#else
+#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx))
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Allocate a zero-initialized array
+ *
+ * @param[in]  ctx      The talloc context to hang the result off.
+ *
+ * @param[in]  type     The type that we want to allocate.
+ *
+ * @param[in]  count    The number of "type" elements you want to allocate.
+ *
+ * @return              The allocated result casted to "type *", NULL on error.
+ *
+ * The talloc_zero_array() macro is equivalent to:
+ *
+ * @code
+ *     ptr = talloc_array(ctx, type, count);
+ *     if (ptr) memset(ptr, sizeof(type) * count);
+ * @endcode
+ */
+void *talloc_zero_array(const void *ctx, #type, unsigned count);
+#else
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+void *_talloc_zero_array(const void *ctx,
+                        size_t el_size,
+                        unsigned count,
+                        const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Change the size of a talloc array.
+ *
+ * The macro changes the size of a talloc pointer. The 'count' argument is the
+ * number of elements of type 'type' that you want the resulting pointer to
+ * hold.
+ *
+ * talloc_realloc() has the following equivalences:
+ *
+ * @code
+ *      talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type);
+ *      talloc_realloc(ctx, NULL, type, N) ==> talloc_array(ctx, type, N);
+ *      talloc_realloc(ctx, ptr, type, 0)  ==> talloc_free(ptr);
+ * @endcode
+ *
+ * The "context" argument is only used if "ptr" is NULL, otherwise it is
+ * ignored.
+ *
+ * @param[in]  ctx      The parent context used if ptr is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  type     The type of the array element inside ptr.
+ *
+ * @param[in]  count    The intended number of array elements.
+ *
+ * @return              The new array, NULL on error. The call will fail either
+ *                      due to a lack of memory, or because the pointer has more
+ *                      than one parent (see talloc_reference()).
+ */
+void *talloc_realloc(const void *ctx, void *ptr, #type, size_t count);
+#else
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Untyped realloc to change the size of a talloc array.
+ *
+ * The macro is useful when the type is not known so the typesafe
+ * talloc_realloc() cannot be used.
+ *
+ * @param[in]  ctx      The parent context used if 'ptr' is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  size     The new chunk size.
+ *
+ * @return              The new array, NULL on error.
+ */
+void *talloc_realloc_size(const void *ctx, void *ptr, size_t size);
+#else
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+#endif
+
+/**
+ * @brief Provide a function version of talloc_realloc_size.
+ *
+ * This is a non-macro version of talloc_realloc(), which is useful as
+ * libraries sometimes want a ralloc function pointer. A realloc()
+ * implementation encapsulates the functionality of malloc(), free() and
+ * realloc() in one call, which is why it is useful to be able to pass around
+ * a single function pointer.
+ *
+ * @param[in]  context  The parent context used if ptr is NULL.
+ *
+ * @param[in]  ptr      The chunk to be resized.
+ *
+ * @param[in]  size     The new chunk size.
+ *
+ * @return              The new chunk, NULL on error.
+ */
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_string The talloc string functions.
+ * @ingroup talloc
+ *
+ * talloc string allocation and manipulation functions.
+ * @{
+ */
+
+/**
+ * @brief Duplicate a string into a talloc chunk.
+ *
+ * This function is equivalent to:
+ *
+ * @code
+ *      ptr = talloc_size(ctx, strlen(p)+1);
+ *      if (ptr) memcpy(ptr, p, strlen(p)+1);
+ * @endcode
+ *
+ * This functions sets the name of the new pointer to the passed
+ * string. This is equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The string you want to duplicate.
+ *
+ * @return              The duplicated string, NULL on error.
+ */
+char *talloc_strdup(const void *t, const char *p);
+
+/**
+ * @brief Append a string to given string and duplicate the result.
+ *
+ * @param[in]  s        The destination to append to.
+ *
+ * @param[in]  a        The string you want to append.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strdup()
+ */
+char *talloc_strdup_append(char *s, const char *a);
+
+/**
+ * @brief Append a string to a given buffer and duplicate the result.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  a        The string you want to append.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strdup()
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+/**
+ * @brief Duplicate a length-limited string into a talloc chunk.
+ *
+ * This function is the talloc equivalent of the C library function strndup(3).
+ *
+ * This functions sets the name of the new pointer to the passed string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  p        The string you want to duplicate.
+ *
+ * @param[in]  n        The maximum string length to duplicate.
+ *
+ * @return              The duplicated string, NULL on error.
+ */
+char *talloc_strndup(const void *t, const char *p, size_t n);
+
+/**
+ * @brief Append at most n characters of a string to given string and duplicate
+ *        the result.
+ *
+ * @param[in]  s        The destination string to append to.
+ *
+ * @param[in]  a        The source string you want to append.
+ *
+ * @param[in]  n        The number of characters you want to append from the
+ *                      string.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strndup()
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+
+/**
+ * @brief Append at most n characters of a string to given buffer and duplicate
+ *        the result.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  a        The source string you want to append.
+ *
+ * @param[in]  n        The number of characters you want to append from the
+ *                      string.
+ *
+ * @return              The duplicated string, NULL on error.
+ *
+ * @see talloc_strndup()
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+/**
+ * @brief Format a string given a va_list.
+ *
+ * This function is the talloc equivalent of the C library function
+ * vasprintf(3).
+ *
+ * This functions sets the name of the new pointer to the new string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string given a va_list and append it to the given destination
+ *        string.
+ *
+ * @param[in]  s        The destination string to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ *
+ * @see talloc_vasprintf()
+ */
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string given a va_list and append it to the given destination
+ *        buffer.
+ *
+ * @param[in]  s        The destination buffer to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ap       The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ *
+ * @see talloc_vasprintf()
+ */
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/**
+ * @brief Format a string.
+ *
+ * This function is the talloc equivalent of the C library function asprintf(3).
+ *
+ * This functions sets the name of the new pointer to the new string. This is
+ * equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  t        The talloc context to hang the result off.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/**
+ * @brief Append a formatted string to another string.
+ *
+ * This function appends the given formatted string to the given string. Use
+ * this variant when the string in the current talloc buffer may have been
+ * truncated in length.
+ *
+ * This functions sets the name of the new pointer to the new
+ * string. This is equivalent to:
+ *
+ * @code
+ *      talloc_set_name_const(ptr, ptr)
+ * @endcode
+ *
+ * @param[in]  s        The string to append to.
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/**
+ * @brief Append a formatted string to another string.
+ *
+ * @param[in]  s        The string to append to
+ *
+ * @param[in]  fmt      The format string.
+ *
+ * @param[in]  ...      The parameters used to fill fmt.
+ *
+ * @return              The formatted string, NULL on error.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+/* @} ******************************************************************/
+
+/**
+ * @defgroup talloc_debug The talloc debugging support functions
+ * @ingroup talloc
+ *
+ * To aid memory debugging, talloc contains routines to inspect the currently
+ * allocated memory hierarchy.
+ *
+ * @{
+ */
+
+/**
+ * @brief Walk a complete talloc hierarchy.
+ *
+ * This provides a more flexible reports than talloc_report(). It
+ * will recursively call the callback for the entire tree of memory
+ * referenced by the pointer. References in the tree are passed with
+ * is_ref = 1 and the pointer that is referenced.
+ *
+ * You can pass NULL for the pointer, in which case a report is
+ * printed for the top level memory context, but only if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full()
+ * has been called.
+ *
+ * The recursion is stopped when depth >= max_depth.
+ * max_depth = -1 means only stop at leaf nodes.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  depth    Internal parameter to control recursion. Call with 0.
+ *
+ * @param[in]  max_depth  Maximum recursion level.
+ *
+ * @param[in]  callback  Function to be called on every chunk.
+ *
+ * @param[in]  private_data  Private pointer passed to callback.
+ */
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+                           void (*callback)(const void *ptr,
+                                            int depth, int max_depth,
+                                            int is_ref,
+                                            void *private_data),
+                           void *private_data);
+
+/**
+ * @brief Print a talloc hierarchy.
+ *
+ * This provides a more flexible reports than talloc_report(). It
+ * will let you specify the depth and max_depth.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  depth    Internal parameter to control recursion. Call with 0.
+ *
+ * @param[in]  max_depth  Maximum recursion level.
+ *
+ * @param[in]  f        The file handle to print to.
+ */
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+
+/**
+ * @brief Print a summary report of all memory used by ptr.
+ *
+ * This provides a more detailed report than talloc_report(). It will
+ * recursively print the entire tree of memory referenced by the
+ * pointer. References in the tree are shown by giving the name of the
+ * pointer that is referenced.
+ *
+ * You can pass NULL for the pointer, in which case a report is printed
+ * for the top level memory context, but only if
+ * talloc_enable_leak_report() or talloc_enable_leak_report_full() has
+ * been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  f        The file handle to print to.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *      fprintf(stderr, "Dumping memory tree for a:\n");
+ *      talloc_report_full(a, stderr);
+ * @endcode
+ *
+ * @see talloc_report()
+ */
+void talloc_report_full(const void *ptr, FILE *f);
+
+/**
+ * @brief Print a summary report of all memory used by ptr.
+ *
+ * This function prints a summary report of all memory used by ptr. One line of
+ * report is printed for each immediate child of ptr, showing the total memory
+ * and number of blocks used by that child.
+ *
+ * You can pass NULL for the pointer, in which case a report is printed
+ * for the top level memory context, but only if talloc_enable_leak_report()
+ * or talloc_enable_leak_report_full() has been called.
+ *
+ * @param[in]  ptr      The talloc chunk.
+ *
+ * @param[in]  f        The file handle to print to.
+ *
+ * Example:
+ * @code
+ *      unsigned int *a, *b;
+ *      a = talloc(NULL, unsigned int);
+ *      b = talloc(a, unsigned int);
+ *      fprintf(stderr, "Summary of memory tree for a:\n");
+ *      talloc_report(a, stderr);
+ * @endcode
+ *
+ * @see talloc_report_full()
+ */
+void talloc_report(const void *ptr, FILE *f);
+
+/**
+ * @brief Enable tracking the use of NULL memory contexts.
+ *
+ * This enables tracking of the NULL memory context without enabling leak
+ * reporting on exit. Useful for when you want to do your own leak
+ * reporting call via talloc_report_null_full();
+ */
+void talloc_enable_null_tracking(void);
+
+/**
+ * @brief Enable tracking the use of NULL memory contexts.
+ *
+ * This enables tracking of the NULL memory context without enabling leak
+ * reporting on exit. Useful for when you want to do your own leak
+ * reporting call via talloc_report_null_full();
+ */
+void talloc_enable_null_tracking_no_autofree(void);
+
+/**
+ * @brief Disable tracking of the NULL memory context.
+ *
+ * This disables tracking of the NULL memory context.
+ */
+void talloc_disable_null_tracking(void);
+
+/**
+ * @brief Enable leak report when a program exits.
+ *
+ * This enables calling of talloc_report(NULL, stderr) when the program
+ * exits. In Samba4 this is enabled by using the --leak-report command
+ * line option.
+ *
+ * For it to be useful, this function must be called before any other
+ * talloc function as it establishes a "null context" that acts as the
+ * top of the tree. If you don't call this function first then passing
+ * NULL to talloc_report() or talloc_report_full() won't give you the
+ * full tree printout.
+ *
+ * Here is a typical talloc report:
+ *
+ * @code
+ * talloc report on 'null_context' (total 267 bytes in 15 blocks)
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      iconv(UTF8,CP850)              contains     42 bytes in   2 blocks
+ *      libcli/auth/spnego_parse.c:55  contains     31 bytes in   2 blocks
+ *      iconv(CP850,UTF8)              contains     42 bytes in   2 blocks
+ *      iconv(UTF8,UTF-16LE)           contains     45 bytes in   2 blocks
+ *      iconv(UTF-16LE,UTF8)           contains     45 bytes in   2 blocks
+ * @endcode
+ */
+void talloc_enable_leak_report(void);
+
+/**
+ * @brief Enable full leak report when a program exits.
+ *
+ * This enables calling of talloc_report_full(NULL, stderr) when the
+ * program exits. In Samba4 this is enabled by using the
+ * --leak-report-full command line option.
+ *
+ * For it to be useful, this function must be called before any other
+ * talloc function as it establishes a "null context" that acts as the
+ * top of the tree. If you don't call this function first then passing
+ * NULL to talloc_report() or talloc_report_full() won't give you the
+ * full tree printout.
+ *
+ * Here is a typical full report:
+ *
+ * @code
+ * full talloc report on 'root' (total 18 bytes in 8 blocks)
+ *      p1                             contains     18 bytes in   7 blocks (ref 0)
+ *      r1                             contains     13 bytes in   2 blocks (ref 0)
+ *      reference to: p2
+ *      p2                             contains      1 bytes in   1 blocks (ref 1)
+ *      x3                             contains      1 bytes in   1 blocks (ref 0)
+ *      x2                             contains      1 bytes in   1 blocks (ref 0)
+ *      x1                             contains      1 bytes in   1 blocks (ref 0)
+ * @endcode
+ */
+void talloc_enable_leak_report_full(void);
+
+/* @} ******************************************************************/
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason));
+void talloc_set_log_fn(void (*log_fn)(const char *message));
+void talloc_set_log_stderr(void);
+
+#if TALLOC_DEPRECATED
+#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
+#define talloc_p(ctx, type) talloc(ctx, type)
+#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
+#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
+#define talloc_destroy(ctx) talloc_free(ctx)
+#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a))
+#endif
+
+#ifndef TALLOC_MAX_DEPTH
+#define TALLOC_MAX_DEPTH 10000
+#endif
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif
index 789649a1a6ded108932ddd9d90f52b0be10a4b14..b13ee350a2fe1c79071f658dc3d57c57d4865980 100644 (file)
@@ -1,5 +1,8 @@
-#ifndef __TDB_H__
-#define __TDB_H__
+#ifndef USE_BUILTIN_TDB
+#  include <tdb.h>
+#else
+#  ifndef __TDB_H__
+#    define __TDB_H__
 
 /* 
    Unix SMB/CIFS implementation.
@@ -174,4 +177,5 @@ extern TDB_DATA tdb_null;
 }
 #endif
 
-#endif /* tdb.h */
+#  endif /* tdb.h */
+#endif /* USE_BUILTIN_TDB */
index dcb32e99d2e726b7feaaf67c0799e9454f35de59..d2890e75674cdd0c978fed8c87b575a5567e22b6 100644 (file)
@@ -60,7 +60,7 @@
  * support is braindead. it also allows me to do a little versioning. */
 struct uam_export {
   int uam_type, uam_version;
-  int (*uam_setup)(const char *);
+  int (*uam_setup)(void *, const char *);
   void (*uam_cleanup)(void);
 };
 
index 0bb43b52de51419d59c73c93ed527722793e6d15..75a970b631efa16167194b0108cb33cb5ca05f63 100644 (file)
@@ -56,8 +56,6 @@ typedef struct {
 #define CONV_DECOMPOSE      (1<<7) /* precompose */
 #define CONV_FORCE          (1<<8) /* force convertion */
 #define CONV__EILSEQ        (1<<9) /* ignore EILSEQ, replace with IGNORE_CHAR (try USC2) */
-#define CONV_ALLOW_COLON   (1<<10) /* Allow ':' in name. Needed for Extended Attributes */
-#define CONV_ALLOW_SLASH   (1<<11) /* Allow '/' in name. Needed for volume name */
 
 /* conversion return flags */
 #define CONV_REQMANGLE  (1<<14) /* mangling of returned name is required */
@@ -125,6 +123,7 @@ extern size_t   utf8_strlen_validate ( char *);
 
 /* from charcnv.c */
 extern int      set_charset_name(charset_t, const char *);
+extern void     free_charset_names(void);
 extern void     init_iconv (void);
 extern size_t   convert_string (charset_t, charset_t, void const *, size_t, void *, size_t);
 extern size_t   convert_string_allocate (charset_t, charset_t, void const *, size_t, char **);
index 01997e9400f8b74d5e4c77a6a45c4f036029d4b0..c12bf5bc0919f296934cf404435ceedd8b3e9772 100644 (file)
 #include <sys/types.h>
 #include <dirent.h>
 
+#include <atalk/globals.h>
+
 #define NETATALK_DIOSZ_STACK 65536
 #define NETATALK_DIOSZ_HEAP  (1024*1024)
 
+struct vol;
+
 /* vfs/unix.c */
 extern int netatalk_unlink(const char *name);
 extern int netatalk_unlinkat(int dirfd, const char *name);
 extern int statat(int dirfd, const char *path, struct stat *st);
-extern int lstatat(int dirfd, const char *path, struct stat *st);
 extern DIR *opendirat(int dirfd, const char *path);
 
 /* rmdir ENOENT not an error */
 extern int netatalk_rmdir(int dirfd, const char *name);
 extern int netatalk_rmdir_all_errors(int dirfd, const char *name);
 
-extern int setfilmode(const char *, mode_t, struct stat *, mode_t);
+extern int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st);
 extern int dir_rx_set(mode_t mode);
 extern int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath);
 extern int copy_file(int sfd, const char *src, const char *dst, mode_t mode);
@@ -47,5 +50,6 @@ extern int copy_ea(const char *ea, int sfd, const char *src, const char *dst, mo
 extern void become_root(void);
 extern void unbecome_root(void);
 extern int gmem(gid_t gid, int ngroups, gid_t *groups);
-
+extern int set_groups(AFPObj *obj, struct passwd *pwd);
+extern const char *print_groups(int ngroups, gid_t *groups);
 #endif  /* ATALK_UNIX_H */
index c5b23ee6f9eaeca3687c0a884d6b5a0c5c614c63..312160be008947f71dfa066dd90d7f7e2c9fb6a3 100644 (file)
 #include <sys/socket.h>
 #include <unistd.h>
 #include <poll.h>
+#include <sys/stat.h>
 
 #include <atalk/unicode.h>
 #include <atalk/bstrlib.h>
+#include <atalk/cnid.h>
 
 /* exit error codes */
 #define EXITERR_CLNT 1  /* client related error */
 #define EXITERR_CONF 2  /* error in config files/cmd line parameters */
 #define EXITERR_SYS  3  /* local system error */
+#define EXITERR_CLOSED 4  /* connection was immediately closed after TCP handshake */
 
 /* Print a SBT and exit */
 #define AFP_PANIC(why) \
 #define ntoh64(x)       (hton64(x))
 #endif
 
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+#endif
+
 #ifdef WITH_SENDFILE
 extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
 #endif
@@ -146,9 +153,10 @@ extern const char *getip_string(const struct sockaddr *sa);
 extern unsigned int getip_port(const struct sockaddr *sa);
 extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
 extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
+extern int tokenize_ip_port(const char *ipurl, char **address, char **port);
 
 /* Structures and functions dealing with dynamic pollfd arrays */
-enum fdtype {IPC_FD, LISTEN_FD, DISASOCIATED_IPC_FD};
+enum fdtype {IPC_FD, LISTEN_FD};
 struct polldata {
     enum fdtype fdtype; /* IPC fd or listening socket fd                 */
     void *data;         /* pointer to AFPconfig for listening socket and *
@@ -178,15 +186,31 @@ extern int recv_fd(int fd, int nonblocking);
 extern const char *getcwdpath(void);
 extern const char *fullpathname(const char *);
 extern char *stripped_slashes_basename(char *p);
-extern int lchdir(const char *dir);
 extern void randombytes(void *buf, int n);
 extern int daemonize(int nochdir, int noclose);
 extern int run_cmd(const char *cmd, char **cmd_argv);
+extern char *realpath_safe(const char *path);
+extern const char *basename_safe(const char *path);
+extern char *strtok_quote (char *s, const char *delim);
+
+extern int ochdir(const char *dir, int options);
+extern int ostat(const char *path, struct stat *buf, int options);
+extern int ostatat(int dirfd, const char *path, struct stat *st, int options);
+extern int ochown(const char *path, uid_t owner, gid_t group, int options);
+extern int ochmod(char *path, mode_t mode, const struct stat *st, int options);
 
 /******************************************************************
  * cnid.c
  *****************************************************************/
 
 extern bstring rel_path_in_vol(const char *path, const char *volpath);
+extern cnid_t cnid_for_path(struct _cnid_db *cdb, const char *volpath, const char *path, cnid_t *did);
+
+/******************************************************************
+ * cnid.c
+ *****************************************************************/
+
+extern void initline   (int, char *);
+extern int  parseline  (int, char *);
 
 #endif  /* _ATALK_UTIL_H */
index 0bd5079655c56c0dea583c537bfd1d3da1247070..6021c150987db56b02c701a5bac934ef54c90aa4 100644 (file)
@@ -62,7 +62,7 @@
 #define VFS_FUNC_ARGS_COPYFILE const struct vol *vol, int sfd, const char *src, const char *dst
 #define VFS_FUNC_VARS_COPYFILE vol, sfd, src, dst
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 #define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, int cmd, int count, void *aces
 #define VFS_FUNC_VARS_ACL vol, path, cmd, count, aces
 #endif
index 116a4ceca336e947c6ea9e21038ab267ddbb7249..084f5a0ad48cefd564492b8c4ccb90e7714824be 100644 (file)
 
 typedef uint64_t VolSpace;
 
+/* This should belong in a file.h */
+struct extmap {
+    char               *em_ext;
+    char               em_creator[4];
+    char               em_type[4];
+};
+
 struct vol {
     struct vol      *v_next;
     AFPObj          *v_obj;
@@ -91,10 +98,11 @@ struct vol {
     int             v_preexec_close;
     char            *v_uuid;    /* For TimeMachine zeroconf record */
     int             v_qfd;
+    uint32_t        v_ignattr;  /* AFP attributes that shall be ignored */
 };
 
 /* load_volumes() flags */
-#define LV_ALL (1 << 0)
+typedef enum {lv_none = 0, lv_all = 1} lv_flags_t;
 
 /* volume flags */
 #define AFPVOL_OPEN (1<<0)
@@ -105,12 +113,11 @@ struct vol {
 #define AFPVOL_USTATFS  (1<<3)
 #define AFPVOL_UQUOTA   (1<<4)
 
-/*
-  Flags that alter volume behaviour.
-  Keep in sync with libatalk/util/volinfo.c
-*/
 #define AFPVOL_NOV2TOEACONV (1 << 5) /* no adouble:v2 to adouble:ea conversion */
+#define AFPVOL_SPOTLIGHT (1 << 6)   /* Index volume for Spotlight searches */
 #define AFPVOL_RO        (1 << 8)   /* read-only volume */
+#define AFPVOL_CHMOD_PRESERVE_ACL (1 << 9) /* try to preserve ACLs */
+#define AFPVOL_CHMOD_IGNORE (1 << 10) /* try to preserve ACLs */
 #define AFPVOL_NOSTAT    (1 << 16)  /* advertise the volume even if we can't stat() it
                                      * maybe because it will be mounted later in preexec */
 #define AFPVOL_UNIX_PRIV (1 << 17)  /* support unix privileges */
@@ -125,6 +132,8 @@ struct vol {
 #define AFPVOL_ACLS      (1 << 24)   /* Volume supports ACLS */
 #define AFPVOL_SEARCHDB  (1 << 25)   /* Use fast CNID db search instead of filesystem */
 #define AFPVOL_NONETIDS  (1 << 26)   /* signal the client it shall do privelege mapping */
+#define AFPVOL_FOLLOWSYM (1 << 27)   /* follow symlinks on the server, default is not to */
+#define AFPVOL_DELVETO   (1 << 28)   /* delete veto files and dirs */
 
 /* Extended Attributes vfs indirection  */
 #define AFPVOL_EA_NONE           0   /* No EAs */
@@ -183,6 +192,8 @@ struct vol {
 #define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
 #define vol_unix_priv(vol) ((vol)->v_obj->afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
 #define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
-
+#define vol_syml_opt(vol) (((vol)->v_flags & AFPVOL_FOLLOWSYM) ? 0 : O_NOFOLLOW)
+#define vol_chmod_opt(vol) (((vol)->v_flags & AFPVOL_CHMOD_PRESERVE_ACL) ? O_NETATALK_ACL : \
+                            ((vol)->v_flags & AFPVOL_CHMOD_IGNORE) ? O_IGNORE : 0)
 
 #endif
index 33a5b662a8d4d689fd366a470abd24c71c971487..e9f48faee67710afcf92d2996c7131409e825e4e 100644 (file)
@@ -6,3 +6,4 @@ Makefile.in
 .libs
 dummy.o
 libatalk.abi.tmp
+*dev.abi
index ed597dcb5f0a608c389079722dd38bff5118cc8d..e9c8dca745d7c568cf90c8c62c79feef76d82a1e 100644 (file)
@@ -18,7 +18,7 @@
 #        current+1:0:0
 #
 
-VERSION_INFO = 2:0:0
+VERSION_INFO = 15:0:0
 
 # History:          VERSION_INFO
 #
@@ -29,15 +29,29 @@ VERSION_INFO = 2:0:0
 #   3.0.0-beta2     1:0:0
 #   3.0             1:0:0
 #   3.0.1           2:0:0
+#   3.0.2           3:0:0
+#   3.0.3           4:0:0
+#   3.0.4           5:0:0
+#   3.0.5           6:0:0
+#   3.0.6           7:0:0
 
-SUBDIRS = acl adouble bstring compat cnid dsi iniparser tdb util unicode vfs
+#   3.1.0           12:0:0
+#   3.1.1           13:0:0
+#   3.1.2           14:0:0
+#   3.1.2           14:0:0
+#   3.1.2           15:0:0
+
+SUBDIRS = acl adouble bstring compat cnid dsi iniparser talloc util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
 libatalk_la_SOURCES = dummy.c
 
+libatalk_la_CFLAGS = \
+       @PTHREAD_CFLAGS@
+
 libatalk_la_LIBADD  = \
-       @WRAP_LIBS@ @ACL_LIBS@ \
+       @WRAP_LIBS@ @ACL_LIBS@ @PTHREAD_LIBS@ @MYSQL_LIBS@ \
        acl/libacl.la \
        adouble/libadouble.la   \
        bstring/libbstring.la \
@@ -45,7 +59,7 @@ libatalk_la_LIBADD  = \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        iniparser/libiniparser.la \
-       tdb/libtdb.la       \
+       talloc/libtalloc.la       \
        unicode/libunicode.la \
        util/libutil.la         \
        vfs/libvfs.la
@@ -57,14 +71,20 @@ libatalk_la_DEPENDENCIES = \
        cnid/libcnid.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
+       talloc/libtalloc.la       \
        iniparser/libiniparser.la \
-       tdb/libtdb.la       \
        unicode/libunicode.la \
        util/libutil.la         \
        vfs/libvfs.la
 
 libatalk_la_LDFLAGS = -version-info $(VERSION_INFO)
 
+if USE_BUILTIN_TDB
+SUBDIRS += tdb
+libatalk_la_LIBADD += tdb/libtdb.la
+libatalk_la_DEPENDENCIES += tdb/libtdb.la
+endif
+
 if DEVELOPER
 all-local: .libs/libatalk.so
        @$(top_srcdir)/abigen.sh .libs/libatalk.so > libatalk.abi.tmp
@@ -88,5 +108,14 @@ endif
 EXTRA_DIST = \
        libatalk-3.0beta1.abi \
        libatalk-3.0beta2.abi \
+       libatalk-3.0.abi \
        libatalk-3.0.1.abi \
-       libatalk-3.0.abi
+       libatalk-3.0.2.abi \
+       libatalk-3.0.3.abi \
+       libatalk-3.0.4.abi \
+       libatalk-3.0.5.abi \
+       libatalk-3.0.6.abi \
+       libatalk-3.1.0.abi \
+       libatalk-3.1.1.abi \
+       libatalk-3.1.2.abi \
+       libatalk-3.1.3.abi
index 0e75ee9ee420ff98c151043d533ab13c2f1150be..1013f46c15ddd55a22f8f5da570235e507a2e5be 100644 (file)
@@ -6,7 +6,7 @@ noinst_LTLIBRARIES = libacl.la
 libacl_la_SOURCES = cache.c unix.c uuid.c
 libacl_la_CFLAGS =
 libacl_la_LDFLAGS =
-libacl_la_LIBADD  =
+libacl_la_LIBADD  = @ACL_LIBS@
 
 if HAVE_LDAP
 libacl_la_SOURCES += ldap.c ldap_config.c
index bc9806d9d323f1e58af3c44e0c94c79b62ad1037..15eea478d099e1b361c69c89a37fefa30a2125cc 100644 (file)
@@ -46,7 +46,6 @@ static cacheduser_t *uuidcache[256];   /* indexed by hash of uuid */
 
 void uuidcache_dump(void) {
     int i;
-    int ret = 0;
     cacheduser_t *entry;
     char timestr[200];
     struct tm *tmp = NULL;
@@ -301,7 +300,6 @@ int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const
     char *name = NULL;
     unsigned char *uuid = NULL;
     cacheduser_t *cacheduser = NULL;
-    cacheduser_t *entry;
     unsigned char hash;
 
     /* allocate mem and copy values */
index 11731a26c69d5bdeb683be1667a8c2c6bd351e03..8e33004a9e9a644778b776c612159f2adcc7ca32 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: cache.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index 8743bafeabcd90b7f0e16afd6fa79bf3738b332a..e805a990542deb3c883a4419e1757c101977867c 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/time.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
 #define LDAP_DEPRECATED 1
 #include <ldap.h>
 
@@ -30,6 +31,7 @@
 #include <atalk/afp.h>
 #include <atalk/uuid.h>
 #include <atalk/ldapconfig.h>   /* For struct ldap_pref */
+#include <atalk/errchk.h>
 
 typedef enum {
     KEEPALIVE = 1
@@ -53,24 +55,30 @@ char *ldap_uuid_string;
 char *ldap_name_attr;
 char *ldap_group_attr;
 char *ldap_uid_attr;
+char *ldap_userfilter;
+char *ldap_groupfilter;
 int  ldap_uuid_encoding;
 
 struct ldap_pref ldap_prefs[] = {
-    {&ldap_server,     "ldap server",      0, 0, -1},
-    {&ldap_auth_method,"ldap auth method", 1, 1, -1},
-    {&ldap_auth_dn,    "ldap auth dn",     0, 0,  0},
-    {&ldap_auth_pw,    "ldap auth pw",     0, 0,  0},
-    {&ldap_userbase,   "ldap userbase",    0, 0, -1},
-    {&ldap_userscope,  "ldap userscope",   1 ,1, -1},
-    {&ldap_groupbase,  "ldap groupbase",   0, 0, -1},
-    {&ldap_groupscope, "ldap groupscope",  1 ,1, -1},
-    {&ldap_uuid_attr,  "ldap uuid attr",   0, 0, -1},
-    {&ldap_uuid_string,"ldap uuid string", 0, 0,  0},
-    {&ldap_name_attr,  "ldap name attr",   0, 0, -1},
-    {&ldap_group_attr, "ldap group attr",  0, 0, -1},
-    {&ldap_uid_attr,   "ldap uid attr",    0, 0,  0},
-    {&ldap_uuid_encoding,"ldap uuid encoding", 1, 1,  0},
-    {NULL,             NULL,               0, 0, -1}
+    /* pointer to pref,    prefname,              strorint, intfromarray, valid, valid_save */
+    {&ldap_server,         "ldap server",         0, 0, -1, -1},
+    {&ldap_auth_method,    "ldap auth method",    1, 1, -1, -1},
+    {&ldap_auth_dn,        "ldap auth dn",        0, 0,  0,  0},
+    {&ldap_auth_pw,        "ldap auth pw",        0, 0,  0,  0},
+    {&ldap_userbase,       "ldap userbase",       0, 0, -1, -1},
+    {&ldap_userscope,      "ldap userscope",      1 ,1, -1, -1},
+    {&ldap_groupbase,      "ldap groupbase",      0, 0, -1, -1},
+    {&ldap_groupscope,     "ldap groupscope",     1 ,1, -1, -1},
+    {&ldap_uuid_attr,      "ldap uuid attr",      0, 0, -1, -1},
+    {&ldap_uuid_string,    "ldap uuid string",    0, 0,  0,  0},
+    {&ldap_name_attr,      "ldap name attr",      0, 0, -1, -1},
+    {&ldap_group_attr,     "ldap group attr",     0, 0, -1, -1},
+    {&ldap_uid_attr,       "ldap uid attr",       0, 0,  0,  0},
+    {&ldap_uuid_encoding,  "ldap uuid encoding",  1, 1,  0,  0},
+    {&ldap_userfilter,     "ldap user filter",    0, 0,  0,  0},
+    {&ldap_groupfilter,    "ldap group filter",   0, 0,  0,  0},
+    {&ldap_auth_pw,        "ldap auth pw",        0, 0,  0,  0},
+    {NULL,                 NULL,                  0, 0,  0,  0}
 };
 
 struct pref_array prefs_array[] = {
@@ -254,6 +262,69 @@ cleanup:
     return ret;
 }
 
+/*!
+ * Generate LDAP filter string for UUID query
+
+ * @param[in] uuidstr      the UUID as string
+ * @param[in] attr_filter  optional attribute
+ * @returns   pointer to static filter string
+ */
+static char *gen_uuid_filter(const char *uuidstr_in, const char *attr_filter)
+{
+    EC_INIT;
+    int len;
+    const char *uuidstr = uuidstr_in;
+
+#define MAX_FILTER_SIZE 512
+    static char filter[MAX_FILTER_SIZE];
+    char stripped[MAX_FILTER_SIZE];
+
+#define LDAP_BIN_UUID_LEN 49 /* LDAP Binary Notation is \XX * 16 bytes of UUID + terminator = 49 */
+    char ldap_bytes[LDAP_BIN_UUID_LEN];
+
+    if (ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
+        /* Convert to LDAP-safe binary encoding for direct query of AD objectGUID attribute */
+        int i = 0, s = 0;
+        char c;
+        while ((c = uuidstr[i])) {
+            if((c >='a' && c <= 'f')
+                || (c >= 'A' && c <= 'F')
+                || (c >= '0' && c <= '9')) {
+                stripped[s++] = toupper(c);
+            }
+            i++;
+        }
+
+        snprintf(ldap_bytes, LDAP_BIN_UUID_LEN,
+                 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c"
+                 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c",
+                 /* Data1 (uint32) */
+                 stripped[6], stripped[7], stripped[4], stripped[5],
+                 stripped[2], stripped[3], stripped[0], stripped[1],
+                 /* Data2 (uint16) */
+                 stripped[10], stripped[11], stripped[8], stripped[9],
+                 /* Data3 (uint16) */
+                 stripped[14], stripped[15], stripped[12], stripped[13],
+                 /* Data4 (uint64) */
+                 stripped[16], stripped[17], stripped[18], stripped[19],
+                 stripped[20], stripped[21], stripped[22], stripped[23],
+                 stripped[24], stripped[25], stripped[26], stripped[27],
+                 stripped[28], stripped[29], stripped[30], stripped[31]);
+        uuidstr = ldap_bytes;
+    }
+
+    if (attr_filter) {
+        len = snprintf(filter, 256, "(&(%s=%s)(%s))", ldap_uuid_attr, uuidstr, attr_filter);
+    } else {
+        len = snprintf(filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return filter;
+}
+
 /********************************************************
  * Interface
  ********************************************************/
@@ -330,76 +401,50 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string)
  * returns 0 on success, -1 on errror
  */
 int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
-    int ret;
-    int len;
-    char filter[256];       /* this should really be enough. we dont want to malloc everything! */
+    EC_INIT;
+    char *filter;
     char *attributes[]  = { NULL, NULL};
 
     if (!ldap_config_valid)
-        return -1;
-
-    if(ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
-        /* Convert to LDAP-safe binary encoding for direct query of AD objectGUID attribute */
-        char* stripped = malloc(strlen(uuidstr));
-
-        int i = 0;
-        int s = 0;
-        char c;
-        while(c = uuidstr[i]) {
-            if((c >='a' && c <= 'f')
-                || (c >= 'A' && c <= 'F')
-                || (c >= '0' && c <= '9')) {
-                stripped[s++] = toupper(c);
-            }
-            i++;
-        }
+        EC_FAIL;
 
-        /* LDAP Binary Notation is \XX * 16 bytes of UUID + terminator = 49 */
-        char* ldap_bytes = malloc(49);
-        snprintf(ldap_bytes, 49,
-            "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c"
-            "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c",
-            /* Data1 (uint32) */
-            stripped[6], stripped[7], stripped[4], stripped[5],
-            stripped[2], stripped[3], stripped[0], stripped[1],
-            /* Data2 (uint16) */
-            stripped[10], stripped[11], stripped[8], stripped[9],
-            /* Data3 (uint16) */
-            stripped[14], stripped[15], stripped[12], stripped[13],
-            /* Data4 (uint64) */
-            stripped[16], stripped[17], stripped[18], stripped[19],
-            stripped[20], stripped[21], stripped[22], stripped[23],
-            stripped[24], stripped[25], stripped[26], stripped[27],
-            stripped[28], stripped[29], stripped[30], stripped[31]);
-        len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, ldap_bytes);
-
-        free(ldap_bytes);
-        free(stripped);
-    } else {
-        len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
-    }
+    /*
+     * Search groups first as group acls are probably used more often.
+     * Note the special case of AD where users and groups are stored
+     * under the same subtree.
+     */
 
-    if (len >= 256 || len == -1) {
-        LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
-        return -1;
-    }
-    /* search groups first. group acls are probably used more often */
     attributes[0] = ldap_group_attr;
-    ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, name);
-    if (ret == -1)
-        return -1;
+    EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_groupfilter) );
+    EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
+                 ldap_groupbase,
+                 filter,
+                 attributes,
+                 ldap_groupscope,
+                 KEEPALIVE,
+                 name) );
     if (ret == 1) {
         *type = UUID_GROUP;
-        return 0;
+        EC_EXIT_STATUS(0);
     }
 
     attributes[0] = ldap_name_attr;
-    ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, name);
+    EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_userfilter) );
+    EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
+                 ldap_userbase,
+                 filter,
+                 attributes,
+                 ldap_userscope,
+                 KEEPALIVE,
+                 name) );
     if (ret == 1) {
         *type = UUID_USER;
-        return 0;
+        EC_EXIT_STATUS(0);
     }
 
-    return -1;
+    EC_FAIL;
+
+EC_CLEANUP:
+    EC_EXIT;
 }
 #endif  /* HAVE_LDAP */
index bd9d414b85758ce8439863100e9752f33b0785af..9bf01c1818f4c8f403865ffb6790dec8c248408f 100644 (file)
 #include <atalk/logger.h>
 #include <atalk/iniparser.h>
 
+void acl_ldap_freeconfig(void)
+{
+    for (int i = 0; ldap_prefs[i].name != NULL; i++) {
+        if (ldap_prefs[i].intfromarray == 0 && ldap_prefs[i].strorint == 0) {
+            free(*((char **)(ldap_prefs[i].pref)));
+            *((char **)(ldap_prefs[i].pref)) = NULL;
+        }
+        ldap_prefs[i].valid = ldap_prefs[i].valid_save;
+    }
+}
+
 int acl_ldap_readconfig(dictionary *iniconfig)
 {
     int i, j;
@@ -38,7 +49,7 @@ int acl_ldap_readconfig(dictionary *iniconfig)
     i = 0;
     /* now see if its a correct pref */
     for (i = 0; ldap_prefs[i].name != NULL; i++) {
-        if ((val = iniparser_getstring(iniconfig, INISEC_GLOBAL, ldap_prefs[i].name, NULL))) {
+        if ((val = atalk_iniparser_getstring(iniconfig, INISEC_GLOBAL, ldap_prefs[i].name, NULL))) {
             /* check if we have pre-defined values */
             if (ldap_prefs[i].intfromarray == 0) {
                 /* no, its just a string */
index 6284935173d87c7cced42ce80419977cc1df40f8..9b22511e2f5325f1116d2de8d590848add4daaca 100644 (file)
@@ -35,7 +35,7 @@
 #include <atalk/acl.h>
 #include <atalk/unix.h>
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 
 /* Get ACL. Allocates storage as needed. Caller must free.
  * Returns no of ACEs or -1 on error.  */
@@ -276,7 +276,7 @@ exit:
     return ret;
 }
 
-#endif /* HAVE_SOLARIS_ACLS */
+#endif /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
 
@@ -308,6 +308,14 @@ int posix_chmod(const char *name, mode_t mode) {
     /* Call chmod() first because there might be some special bits to be set which
      * aren't related to access control.
      */
+#ifdef BSD4_4
+    /*
+     * On FreeBSD chmod_acl() ends up in here too, but on
+     * FreeBSD sine ~9.1 with ZFS doesn't allow setting the g+s bit.
+     * Fixes PR #491.
+     */
+    mode &= 0777;
+#endif
     ret = chmod(name, mode);
 
     if (ret)
index b71c13e689cd3cab889cbb6a24dfb7bbf1c45417..ff58c3dd2202f562f3b757bca7053a85cbfdf4ca 100644 (file)
@@ -209,7 +209,6 @@ int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid) {
         add_cachebyname(name, uuid, mytype, 0);
     }
 
-cleanup:
 #ifdef HAVE_LDAP
     if (uuid_string) free(uuid_string);
 #endif
@@ -255,6 +254,7 @@ int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
         uid = ntohl(tmp);
         if ((pwd = getpwuid(uid)) == NULL) {
             /* not found, add negative entry to cache */
+            *name = NULL;
             add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
             ret = -1;
         } else {
@@ -264,7 +264,7 @@ int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
         }
         LOG(log_debug, logtype_afpd,
             "getnamefromuuid{local}: UUID: %s -> name: %s, type:%s",
-            uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+            uuid_bin2string(uuidp), *name ? *name : "-", uuidtype[(*type) & UUIDTYPESTR_MASK]);
         return ret;
     } else if (memcmp(uuidp, local_group_uuid, 12) == 0) {
         *type = UUID_GROUP;
index 49144a2aa62831042f33efff428316378d724b60..1ae236657eaa9c1678194d893a65f295750c1b05 100644 (file)
@@ -11,6 +11,7 @@ libadouble_la_SOURCES = \
        ad_mmap.c \
        ad_open.c \
        ad_read.c \
+       ad_recvfile.c \
        ad_sendfile.c \
        ad_size.c \
        ad_write.c
index e5fe323fd7814249cf21b412695a85f02739f3ae..43d798bb647f7e0f6921a22ec33652b822bc204b 100644 (file)
@@ -117,25 +117,22 @@ int ad_setid (struct adouble *adp, const dev_t dev, const ino_t ino , const uint
 }
 
 /* ----------------------------- */
-uint32_t ad_getid (struct adouble *adp, const dev_t st_dev, const ino_t st_ino , const cnid_t did, const void *stamp)
+uint32_t ad_getid (struct adouble *adp, const dev_t st_dev, const ino_t st_ino , const cnid_t did, const void *stamp _U_)
 {
     uint32_t aint = 0;
     dev_t  dev;
     ino_t  ino;
     cnid_t a_did;
-    char   temp[ADEDLEN_PRIVSYN];
 
     if (adp) {
         if (sizeof(dev_t) == ad_getentrylen(adp, ADEID_PRIVDEV)) {
             memcpy(&dev, ad_entry(adp, ADEID_PRIVDEV), sizeof(dev_t));
             memcpy(&ino, ad_entry(adp, ADEID_PRIVINO), sizeof(ino_t));
-            memcpy(temp, ad_entry(adp, ADEID_PRIVSYN), sizeof(temp));
             memcpy(&a_did, ad_entry(adp, ADEID_DID), sizeof(cnid_t));
 
             if (((adp->ad_options & ADVOL_NODEV) || (dev == st_dev))
                 && ino == st_ino
-                && (!did || a_did == did)
-                && (memcmp(stamp, temp, sizeof(temp)) == 0) ) {
+                && (!did || a_did == did) ) {
                 memcpy(&aint, ad_entry(adp, ADEID_PRIVID), sizeof(aint));
                 if (adp->ad_vers == AD_VERSION2)
                     return aint;
index eaac8db97032eab7aead8b644de35657854ea9f3..43f4332baab3e4fe2a4ed464e62e2998fe2ff95d 100644 (file)
@@ -57,15 +57,23 @@ static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struc
     EC_INIT;
     struct adouble adv2;
     struct adouble adea;
-    const char *adpath;
     int adflags;
     uint32_t ctime, mtime, afpinfo = 0;
     char *emptyad;
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
+
+    switch (S_IFMT & sp->st_mode) {
+    case S_IFREG:
+    case S_IFDIR:
+        break;
+    default:
+        return 0;
+    }
 
     ad_init(&adea, vol);
     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
+
     adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
 
     /* Open and lock adouble:v2 file */
@@ -97,12 +105,12 @@ static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struc
             goto copy;
     }
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
     goto EC_CLEANUP;
 
 copy:
     /* Create a adouble:ea meta EA */
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
     EC_ZERO_LOGSTR( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE),
                     "ad_conv_v22ea_hf(\"%s\"): error creating metadata EA: %s",
                     fullpathname(path), strerror(errno));
@@ -112,7 +120,7 @@ copy:
 EC_CLEANUP:
     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
@@ -122,7 +130,14 @@ static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struc
     struct adouble adv2;
     struct adouble adea;
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
+
+    switch (S_IFMT & sp->st_mode) {
+    case S_IFREG:
+        break;
+    default:
+        return 0;
+    }
 
     if (S_ISDIR(sp->st_mode))
         return 0;
@@ -137,17 +152,19 @@ static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struc
         EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) );
 
         /* Create a adouble:ea resource fork */
-        EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) );
+        EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_RF|ADFLAGS_RDWR|ADFLAGS_CREATE|ADFLAGS_SETSHRMD, 0666) );
 
         EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) );
         adea.ad_rlen = adv2.ad_rlen;
         ad_flush(&adea);
+        fchmod(ad_reso_fileno(&adea), sp->st_mode & 0666);
+        fchown(ad_reso_fileno(&adea), sp->st_uid, sp->st_gid);
     }
 
 EC_CLEANUP:
     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) );
     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
@@ -159,11 +176,14 @@ static int ad_conv_v22ea(const char *path, const struct stat *sp, const struct v
 
     become_root();
 
-    EC_ZERO( ad_conv_v22ea_hf(path, sp, vol) );
-    EC_ZERO( ad_conv_v22ea_rf(path, sp, vol) );
+    if (ad_conv_v22ea_hf(path, sp, vol) != 0)
+        goto delete;
+    if (ad_conv_v22ea_rf(path, sp, vol) != 0)
+        goto delete;
 
+delete:
     EC_NULL( adpath = ad_path(path, adflags) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
         path, fullpathname(adpath));
 
     unlink(adpath);
@@ -184,25 +204,41 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
 {
     EC_INIT;
     static char buf[MAXPATHLEN];
-    const char *adpath, *p;
     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
     bstring newpath = NULL;
+    static bstring str2e = NULL;
+    static bstring str2f = NULL;
+    static bstring strdot = NULL;
+    static bstring strcolon = NULL;
+    char *newadpath = NULL;
+
+    if (str2e == NULL) {
+        str2e = bfromcstr(":2e");
+        str2f = bfromcstr(":2f");
+        strdot = bfromcstr(".");
+        strcolon = bfromcstr(":");
+    }
 
-    LOG(log_debug, logtype_default,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
 
     *newpathp = NULL;
 
-    if ((p = strchr(path, ':')) == NULL)
+    if (((strstr(path, ":2e")) == NULL) && ((strstr(path, ":2f")) == NULL) )
         goto EC_CLEANUP;
 
     EC_NULL( newpath = bfromcstr(path) );
 
-    EC_ZERO( bfindreplace(newpath, bfromcstr(":2e"), bfromcstr("."), 0) );
-    EC_ZERO( bfindreplace(newpath, bfromcstr(":2f"), bfromcstr(":"), 0) );
+    EC_ZERO( bfindreplace(newpath, str2e, strdot, 0) );
+    EC_ZERO( bfindreplace(newpath, str2f, strcolon, 0) );
     
     become_root();
-    if (adflags != ADFLAGS_DIR)
-        rename(vol->ad_path(path, 0), vol->ad_path(bdata(newpath), 0));
+    if (adflags != ADFLAGS_DIR) {
+        if ((newadpath = strdup(vol->ad_path(bdata(newpath), 0))) == NULL) {
+            unbecome_root();
+            EC_FAIL;
+        }
+        rename(vol->ad_path(path, 0), newadpath);
+    }
     rename(path, bdata(newpath));
     unbecome_root();
 
@@ -212,6 +248,8 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
 EC_CLEANUP:
     if (newpath)
         bdestroy(newpath);
+    if (newadpath)
+        free(newadpath);
     EC_EXIT;
 }
 
@@ -231,11 +269,14 @@ int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, c
     EC_INIT;
     const char *p;
 
-    LOG(log_debug, logtype_default,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
 
     if (newpath)
         *newpath = NULL;
 
+    if (vol->v_flags & AFPVOL_RO)
+        EC_EXIT_STATUS(0);
+
     if ((vol->v_adouble == AD_VERSION_EA) && !(vol->v_flags & AFPVOL_NOV2TOEACONV))
         EC_ZERO( ad_conv_v22ea(path, sp, vol) );
 
@@ -246,7 +287,7 @@ int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, c
     }
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
index a637b27f2ebeb108db55d5eeeb9674684e43beb2..6a1900c72f2f718e4e334139a19dd1b4bea43369 100644 (file)
@@ -59,9 +59,8 @@ int ad_rebuild_adouble_header_v2(struct adouble *ad)
     uint32_t       temp;
     uint16_t       nent;
     char        *buf, *nentp;
-    int             len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_v2");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_v2");
 
     buf = ad->ad_data;
 
@@ -105,9 +104,8 @@ int ad_rebuild_adouble_header_ea(struct adouble *ad)
     uint32_t       temp;
     uint16_t       nent;
     char        *buf, *nentp;
-    int             len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_ea");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_ea");
 
     buf = ad->ad_data;
 
@@ -148,14 +146,13 @@ int ad_rebuild_adouble_header_ea(struct adouble *ad)
 /*!
  * Prepare adbuf buffer from struct adouble for writing on disk
  */
-static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
+int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
 {
     uint32_t       temp;
     uint16_t       nent;
     char           *buf;
-    int            len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_osx");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_osx");
 
     buf = &adbuf[0];
 
@@ -167,7 +164,7 @@ static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
     memcpy(buf, &temp, sizeof( temp ));
     buf += sizeof( temp );
 
-    memset(buf, 0, sizeof(ad->ad_filler));
+    memcpy(buf, AD_FILLER_NETATALK, strlen(AD_FILLER_NETATALK));
     buf += sizeof( ad->ad_filler );
 
     nent = htons(ADEID_NUM_OSX);
@@ -220,15 +217,12 @@ int ad_copy_header(struct adouble *add, struct adouble *ads)
             continue;
 
         len = ads->ad_eid[ eid ].ade_len;
-        if (!len || len != add->ad_eid[ eid ].ade_len)
+        if (len == 0)
             continue;
 
         switch (eid) {
         case ADEID_COMMENT:
-        case ADEID_PRIVDEV:
-        case ADEID_PRIVINO:
-        case ADEID_PRIVSYN:
-        case ADEID_PRIVID:
+        case ADEID_RFORK:
             continue;
         default:
             ad_setentrylen( add, eid, len );
@@ -237,8 +231,8 @@ int ad_copy_header(struct adouble *add, struct adouble *ads)
     }
     add->ad_rlen = ads->ad_rlen;
 
-    if ((ads->ad_vers == AD_VERSION2) && (add->ad_vers = AD_VERSION_EA)
-        || (ads->ad_vers == AD_VERSION_EA) && (add->ad_vers = AD_VERSION2)) {
+    if (((ads->ad_vers == AD_VERSION2) && (add->ad_vers == AD_VERSION_EA))
+        || ((ads->ad_vers == AD_VERSION_EA) && (add->ad_vers == AD_VERSION2))) {
         cnid_t id;
         memcpy(&id, ad_entry(add, ADEID_PRIVID), sizeof(cnid_t));
         id = htonl(id);
@@ -253,7 +247,7 @@ static int ad_flush_hf(struct adouble *ad)
     int len;
     int cwd = -1;
 
-    LOG(log_debug, logtype_default, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags));
 
     struct ad_fd *adf;
 
@@ -265,7 +259,7 @@ static int ad_flush_hf(struct adouble *ad)
         adf = &ad->ad_data_fork;
         break;
     default:
-        LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+        LOG(log_error, logtype_ad, "ad_flush: unexpected adouble version");
         return -1;
     }
 
@@ -292,7 +286,8 @@ static int ad_flush_hf(struct adouble *ad)
                 if (ad->ad_adflags & ADFLAGS_DIR) {
                     EC_NEG1_LOG( cwd = open(".", O_RDONLY) );
                     EC_NEG1_LOG( fchdir(ad_data_fileno(ad)) );
-                    EC_ZERO_LOG( sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) );
+                    EC_ZERO_LOGSTR( sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0),
+                                    "sys_lsetxattr(\"%s\"): %s", fullpathname(".") ,strerror(errno));
                     EC_NEG1_LOG( fchdir(cwd) );
                     EC_NEG1_LOG( close(cwd) );
                     cwd = -1;
@@ -302,7 +297,7 @@ static int ad_flush_hf(struct adouble *ad)
             }
             break;
         default:
-            LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+            LOG(log_error, logtype_ad, "ad_flush: unexpected adouble version");
             return -1;
         }
     }
@@ -329,7 +324,7 @@ static int ad_flush_rf(struct adouble *ad)
     if (ad->ad_vers != AD_VERSION_EA)
         return 0;
 
-    LOG(log_debug, logtype_default, "ad_flush_rf(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush_rf(%s)", adflags2logstr(ad->ad_adflags));
 
     if ((ad->ad_rfp->adf_flags & O_RDWR)) {
         if (ad_getentryoff(ad, ADEID_RFORK)) {
@@ -353,7 +348,7 @@ int ad_flush(struct adouble *ad)
 {
     EC_INIT;
 
-    LOG(log_debug, logtype_default, "ad_flush(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush(%s)", adflags2logstr(ad->ad_adflags));
 
     if (AD_META_OPEN(ad)) {
         EC_ZERO( ad_flush_hf(ad) );
@@ -393,7 +388,7 @@ int ad_close(struct adouble *ad, int adflags)
         return err;
 
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_close(%s): BEGIN: {d: %d, m: %d, r: %d} "
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags),
@@ -412,11 +407,11 @@ int ad_close(struct adouble *ad, int adflags)
 
     if ((adflags & ADFLAGS_DF) && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == AD_SYMLINK)) {        
         if (ad->ad_data_refcount)
-            ad->ad_data_refcount--;
+            if (--ad->ad_data_refcount == 0)
+                adf_lock_free(&ad->ad_data_fork);                
         if (--ad->ad_data_fork.adf_refcount == 0) {
             if (ad_data_closefd(ad) < 0)
                 err = -1;
-            adf_lock_free(&ad->ad_data_fork);
         }
     }
 
@@ -427,14 +422,24 @@ int ad_close(struct adouble *ad, int adflags)
             if (close( ad_meta_fileno(ad)) < 0)
                 err = -1;
             ad_meta_fileno(ad) = -1;
-            if (ad->ad_vers == AD_VERSION2)
-                adf_lock_free(ad->ad_mdp);
         }
     }
 
     if (adflags & ADFLAGS_RF) {
+        /* HF is automatically opened when opening an RF, close it. */
+        if ((ad->ad_vers == AD_VERSION2) && (ad_meta_fileno(ad) != -1)) {
+            if (ad->ad_meta_refcount)
+                ad->ad_meta_refcount--;
+            if (!(--ad->ad_mdp->adf_refcount)) {
+                if (close( ad_meta_fileno(ad)) < 0)
+                    err = -1;
+                ad_meta_fileno(ad) = -1;
+            }
+        }
+
         if (ad->ad_reso_refcount)
-            ad->ad_reso_refcount--;
+            if (--ad->ad_reso_refcount == 0)
+                adf_lock_free(ad->ad_rfp);
         if (ad->ad_vers == AD_VERSION_EA) {
             if ((ad_reso_fileno(ad) != -1)
                 && !(--ad->ad_rfp->adf_refcount)) {
@@ -442,12 +447,11 @@ int ad_close(struct adouble *ad, int adflags)
                     err = -1;
                 ad->ad_rlen = 0;
                 ad_reso_fileno(ad) = -1;
-                adf_lock_free(ad->ad_rfp);
             }
         }
     }
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_close(%s): END: %d {d: %d, m: %d, r: %d} "
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags), err,
index 92f0ab89224501331c5a010dd323196ced0258b3..173adc4c1f113f22b9bdeeee05d000f993b4cdee 100644 (file)
@@ -64,7 +64,7 @@ static int set_lock(int fd, int cmd,  struct flock *lock)
 {
     EC_INIT;
 
-    LOG(log_debug, logtype_default, "set_lock(fd: %d, %s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "set_lock(fd: %d, %s, %s, off: %jd (%s), len: %jd): BEGIN",
         fd, cmd == F_SETLK ? "F_SETLK" : "F_GETLK",
         lock->l_type == F_RDLCK ? "F_RDLCK" : lock->l_type == F_WRLCK ? "F_WRLCK" : "F_UNLCK",
         (intmax_t)lock->l_start,
@@ -361,7 +361,7 @@ int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len
     int type;  
     int ret = 0, fcntl_lock_err = 0;
 
-    LOG(log_debug, logtype_default, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         locktypetostr(locktype),
         (intmax_t)off,
@@ -496,7 +496,7 @@ exit:
             set_lock(adf->adf_fd, F_SETLK, &lock);
         }
     }
-    LOG(log_debug, logtype_default, "ad_lock: END: %d", ret);
+    LOG(log_debug, logtype_ad, "ad_lock: END: %d", ret);
     return ret;
 }
 
@@ -507,7 +507,7 @@ int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t
     int err;
     int type;  
 
-    LOG(log_debug, logtype_default, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         locktypetostr(locktype),
         (intmax_t)off,
@@ -564,14 +564,14 @@ int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t
         adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
 
 exit:
-    LOG(log_debug, logtype_default, "ad_tmplock: END: %d", err);
+    LOG(log_debug, logtype_ad, "ad_tmplock: END: %d", err);
     return err;
 }
 
 /* --------------------- */
 void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
 {
-    LOG(log_debug, logtype_default, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
+    LOG(log_debug, logtype_ad, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
 
     if (ad_data_fileno(ad) != -1) {
         adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl);
@@ -580,7 +580,7 @@ void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
         adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl);
     }
 
-    LOG(log_debug, logtype_default, "ad_unlock: END");
+    LOG(log_debug, logtype_ad, "ad_unlock: END");
 }
 
 /*!
@@ -598,7 +598,7 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
     int ret = 0;
     off_t lock_offset;
 
-    LOG(log_debug, logtype_default, "ad_testlock(%s, off: %jd (%s): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_testlock(%s, off: %jd (%s): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         (intmax_t)off,
         shmdstrfromoff(off));
@@ -611,7 +611,7 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
 
     ret = testlock(&ad->ad_data_fork, lock_offset, 1);
 
-    LOG(log_debug, logtype_default, "ad_testlock: END: %d", ret);
+    LOG(log_debug, logtype_ad, "ad_testlock: END: %d", ret);
     return ret;
 }
 
@@ -630,7 +630,6 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
 uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
 {
     uint16_t ret = 0;
-    struct ad_fd *adf;
     off_t off;
     off_t len;
 
index 3e06e93fb6cfe7e715ac0de66be0d58cf0ae72a7..53ec4eb97ba96c600580898ae8015a48addacf43 100644 (file)
@@ -108,10 +108,12 @@ static int ad_mkrf(const char *path);
 static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst);
 static int ad_header_upgrade(struct adouble *ad, const char *name);
 
+#ifdef HAVE_EAFD
 static int ad_mkrf_ea(const char *path);
+#endif
 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst);
 static int ad_header_upgrade_ea(struct adouble *ad, const char *name);
-static int ad_reso_size(const char *path, int adflags, struct adouble *ad);
+off_t ad_reso_size(const char *path, int adflags, struct adouble *ad);
 static int ad_mkrf_osx(const char *path);
 
 
@@ -309,19 +311,16 @@ static uint32_t get_eid(uint32_t eid)
     return 0;
 }
 
-/* ----------------------------------- */
-static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp, int adflags)
+
+/**
+ * Initialize offset pointers
+ */
+int ad_init_offsets(struct adouble *ad)
 {
     const struct entry  *eid;
-    uint16_t            ashort;
-    struct stat         st;
-
-    LOG(log_debug, logtype_default, "new_ad_header(\"%s\")", path);
 
-    if (ad->ad_magic == AD_MAGIC) {
-        LOG(log_debug, logtype_default, "new_ad_header(\"%s\"): already initialized", path);
+    if (ad->ad_magic == AD_MAGIC)
         return 0;
-    }
 
     ad->ad_magic = AD_MAGIC;
     ad->ad_version = ad->ad_vers & 0x0f0000;
@@ -344,22 +343,32 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
         eid++;
     }
 
-    /* put something sane in the directory finderinfo */
-    if (stp == NULL) {
-        stp = &st;
-        if (lstat(path, &st) != 0)
-            return -1;
-    }
+    /*
+     * Ensure the resource fork offset is always set
+     */
+#ifndef HAVE_EAFD
+    if (ad->ad_vers == AD_VERSION_EA)
+        ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_OSX);
+#endif
 
-    if ((adflags & ADFLAGS_DIR)) {
-        /* set default view */
-        ashort = htons(FINDERINFO_CLOSEDVIEW);
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
-    } else {
-        /* set default creator/type fields */
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
-    }
+    return 0;
+}
+
+/* ----------------------------------- */
+static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp, int adflags)
+{
+    const struct entry  *eid;
+    uint16_t            ashort;
+    struct stat         st;
+
+    LOG(log_debug, logtype_ad, "new_ad_header(\"%s\")", path);
+
+    if (ad_init_offsets(ad) != 0)
+        return -1;
+
+    /* set default creator/type fields */
+    memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
+    memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
 
     /* make things invisible */
     if ((ad->ad_options & ADVOL_INVDOTS)
@@ -373,6 +382,11 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     }
 
     /* put something sane in the date fields */
+    if (stp == NULL) {
+        stp = &st;
+        if (lstat(path, &st) != 0)
+            return -1;
+    }
     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
@@ -381,13 +395,13 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     return 0;
 }
 
-/* -------------------------------------
-   read in the entries
-*/
-static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
+/**
+ * Read an AppleDouble buffer, returns 0 on success, -1 if an entry was malformatted
+ **/
+static int parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
 {
     uint32_t   eid, len, off;
-    int        warning = 0;
+    int        ret = 0;
 
     /* now, read in the entry bits */
     for (; nentries > 0; nentries-- ) {
@@ -401,17 +415,21 @@ static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
         len = ntohl( len );
         buf += sizeof( len );
 
-        if (eid
-            && eid < ADEID_MAX
-            && off < sizeof(ad->ad_data)
-            && (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
-            ad->ad_eid[ eid ].ade_off = off;
-            ad->ad_eid[ eid ].ade_len = len;
-        } else if (!warning) {
-            warning = 1;
-            LOG(log_warning, logtype_default, "parse_entries: bogus eid: %d", eid);
+        ad->ad_eid[eid].ade_off = off;
+        ad->ad_eid[eid].ade_len = len;
+
+        if (!eid
+            || eid > ADEID_MAX
+            || off >= sizeof(ad->ad_data)
+            || ((eid != ADEID_RFORK) && (off + len >  sizeof(ad->ad_data))))
+        {
+            ret = -1;
+            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
+                (uint)eid, (uint)off, (uint)len);
         }
     }
+
+    return ret;
 }
 
 /* this reads enough of the header so that we can figure out all of
@@ -421,7 +439,7 @@ static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
  * NOTE: we're assuming that the resource fork is kept at the end of
  *       the file. also, mmapping won't work for the hfs fs until it
  *       understands how to mmap header files. */
-static int ad_header_read(const char *path _U_, struct adouble *ad, const struct stat *hst)
+static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst)
 {
     char                *buf = ad->ad_data;
     uint16_t            nentries;
@@ -444,7 +462,7 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
     ad->ad_version = ntohl( ad->ad_version );
 
     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
+        LOG(log_error, logtype_ad, "ad_open: can't parse AppleDouble header.");
         errno = EIO;
         return -1;
     }
@@ -461,7 +479,7 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
 
     buf += AD_HEADER_LEN;
     if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
+        LOG(log_error, logtype_ad, "ad_header_read: can't read entry info.");
         errno = EIO;
         return -1;
     }
@@ -469,17 +487,22 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
     /* figure out all of the entry offsets and lengths. if we aren't
      * able to read a resource fork entry, bail. */
     nentries = len / AD_ENTRY_LEN;
-    parse_entries(ad, buf, nentries);
+    if (parse_entries(ad, buf, nentries) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EIO;
+        return -1;
+    }
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
         ) {
-        LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset.");
+        LOG(log_error, logtype_ad, "ad_header_read: problem with rfork entry offset.");
         errno = EIO;
         return -1;
     }
 
     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
-        LOG(log_error, logtype_default, "ad_header_read: can't read in entries.");
+        LOG(log_error, logtype_ad, "ad_header_read: can't read in entries.");
         errno = EIO;
         return -1;
     }
@@ -505,7 +528,7 @@ int ad_valid_header_osx(const char *path)
     char                *buf = &adosx.ad_data[0];
     ssize_t             header_len;
 
-    LOG(log_debug, logtype_afpd, "ad_valid_header_osx(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): BEGIN", fullpathname(path));
 
     EC_NEG1( fd = open(path, O_RDONLY) );
 
@@ -521,12 +544,22 @@ int ad_valid_header_osx(const char *path)
     adosx.ad_version = ntohl(adosx.ad_version);
 
     if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_afpd, "ad_valid_header_osx: not an adouble:ox file");
+        LOG(log_warning, logtype_ad, "ad_valid_header_osx(\"%s\"): not an adouble:osx file", fullpathname(path));
         EC_FAIL;
     }
 
+    if (strncmp(buf + ADEDOFF_FILLER,
+                AD_FILLER_NETATALK,
+                strlen(AD_FILLER_NETATALK)) != 0)
+        /*
+         * It's a split fork created by OS X, it's not our "own" ._ file
+         * and thus not a valid header in this context.
+         * We allow enumeration and access.
+         */
+        EC_FAIL;
+
 EC_CLEANUP:
-    LOG(log_debug, logtype_afpd, "ad_valid_header_osx(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): END: %d", fullpathname(path), ret);
     if (fd != -1)
         close(fd);
     if (ret != 0)
@@ -534,18 +567,99 @@ EC_CLEANUP:
     return 0;
 }
 
+/**
+ * Convert from Apple's ._ file to Netatalk
+ *
+ * Apple's AppleDouble may contain a FinderInfo entry longer then 32 bytes
+ * containing packed xattrs. Netatalk can't deal with that, so we
+ * simply discard the packed xattrs.
+ *
+ * As we call ad_open() which might result in a recursion, just to be sure
+ * use static variable in_conversion to check for that.
+ *
+ * Returns -1 in case an error occured, 0 if no conversion was done, 1 otherwise
+ **/
+static int ad_convert_osx(const char *path, struct adouble *ad)
+{
+    EC_INIT;
+    static bool in_conversion = false;
+    char *map;
+    int finderlen = ad_getentrylen(ad, ADEID_FINDERI);
+    ssize_t origlen;
+
+    if (in_conversion || finderlen == ADEDLEN_FINDERI)
+        return 0;
+    in_conversion = true;
+
+    LOG(log_debug, logtype_ad, "Converting OS X AppleDouble %s, FinderInfo length: %d",
+        fullpathname(path), finderlen);
+
+    origlen = ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK);
+
+    map = mmap(NULL, origlen, PROT_READ | PROT_WRITE, MAP_SHARED, ad_reso_fileno(ad), 0);
+    if (map == MAP_FAILED) {
+        LOG(log_error, logtype_ad, "mmap AppleDouble: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+
+    memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
+            map + ad_getentryoff(ad, ADEID_RFORK),
+            ad_getentrylen(ad, ADEID_RFORK));
+
+    ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+    ad->ad_rlen = ad_getentrylen(ad, ADEID_RFORK);
+    ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
+
+    EC_ZERO_LOG( ftruncate(ad_reso_fileno(ad),
+                           ad_getentryoff(ad, ADEID_RFORK)
+                           + ad_getentrylen(ad, ADEID_RFORK)) );
+
+    (void)ad_rebuild_adouble_header_osx(ad, map);
+    munmap(map, origlen);
+
+    /* Create a metadata EA if one doesn't exit */
+    if (strlen(path) < 3)
+        EC_EXIT_STATUS(0);
+    struct adouble adea;
+    ad_init_old(&adea, AD_VERSION_EA, ad->ad_options);
+
+    if (ad_open(&adea, path + 2, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
+        LOG(log_error, logtype_ad, "create metadata: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+    if (adea.ad_mdp->adf_flags & O_CREAT) {
+        memcpy(ad_entry(&adea, ADEID_FINDERI),
+               ad_entry(ad, ADEID_FINDERI),
+               ADEDLEN_FINDERI);
+        ad_flush(&adea);
+    }
+    ad_close(&adea, ADFLAGS_HF);
+
+EC_CLEANUP:
+    in_conversion = false;
+    if (ret != 0)
+        return -1;
+    return 1;
+}
+
 /* Read an ._ file, only uses the resofork, finderinfo is taken from EA */
-static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const struct stat *hst)
+static int ad_header_read_osx(const char *path, struct adouble *ad, const struct stat *hst)
 {
     EC_INIT;
     struct adouble      adosx;
-    char                *buf = &adosx.ad_data[0];
+    char                *buf;
     uint16_t            nentries;
     int                 len;
     ssize_t             header_len;
     struct stat         st;
+    int                 retry_read = 0;
 
+reread:
+    LOG(log_debug, logtype_ad, "ad_header_read_osx: %s", path ? fullpathname(path) : "");
+    ad_init_old(&adosx, AD_VERSION_EA, ad->ad_options);
+    buf = &adosx.ad_data[0];
     memset(buf, 0, sizeof(adosx.ad_data));
+    adosx.ad_rfp->adf_fd = ad_reso_fileno(ad);
 
     /* read the header */
     EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
@@ -561,7 +675,7 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
     adosx.ad_version = ntohl(adosx.ad_version);
 
     if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: can't parse AppleDouble header");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: can't parse AppleDouble header");
         errno = EIO;
         return -1;
     }
@@ -575,19 +689,39 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
 
     buf += AD_HEADER_LEN;
     if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: can't read entry info.");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: can't read entry info.");
         errno = EIO;
         return -1;
     }
 
     nentries = len / AD_ENTRY_LEN;
-    parse_entries(&adosx, buf, nentries);
+    if (parse_entries(&adosx, buf, nentries) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+    }
+
+    if (ad_getentrylen(&adosx, ADEID_FINDERI) != ADEDLEN_FINDERI) {
+        LOG(log_warning, logtype_ad, "Convert OS X to Netatalk AppleDouble: %s",
+            path ? fullpathname(path) : "");
+
+        if (retry_read > 0) {
+            LOG(log_error, logtype_ad, "ad_header_read_osx: %s, giving up", path ? fullpathname(path) : "");
+            errno = EIO;
+            EC_FAIL;
+        }
+        retry_read++;
+        if (ad_convert_osx(path, &adosx) == 1) {
+            goto reread;
+        }
+        errno = EIO;
+        EC_FAIL;
+    }
 
     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
         || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
         ) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: problem with rfork entry offset.");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: problem with rfork entry offset.");
         errno = EIO;
         return -1;
     }
@@ -606,6 +740,7 @@ EC_CLEANUP:
 
 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst _U_)
 {
+    EC_INIT;
     uint16_t nentries;
     int      len;
     ssize_t  header_len;
@@ -614,16 +749,16 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     if (ad_meta_fileno(ad) != -1)
         header_len = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA);
     else
-        header_len = sys_lgetxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
-     if (header_len < 1) {
-        LOG(log_debug, logtype_default, "ad_header_read_ea: %s", strerror(errno));
-        return -1;
+        header_len = sys_getxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
+    if (header_len < 1) {
+        LOG(log_debug, logtype_ad, "ad_header_read_ea: %s", strerror(errno));
+        EC_FAIL;
     }
 
-    if (header_len < AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: bogus AppleDouble header.");
-        errno = EIO;
-        return -1;
+    if (header_len < AD_DATASZ_EA) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): short metadata EA", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
     }
 
     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
@@ -633,29 +768,58 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     ad->ad_version = ntohl( ad->ad_version );
 
     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: wrong magic or version");
-        errno = EIO;
-        return -1;
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): wrong magic or version", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
     }
 
     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
     nentries = ntohs( nentries );
-
-    /* Protect against bogus nentries */
-    len = nentries * AD_ENTRY_LEN;
-    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
-        len = sizeof(ad->ad_data) - AD_HEADER_LEN;
-    if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: can't read entry info.");
-        errno = EIO;
-        return -1;
+    if (nentries != ADEID_NUM_EA) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid number of entries: %d", fullpathname(path), nentries);
+        errno = EINVAL;
+        EC_FAIL;
     }
-    nentries = len / AD_ENTRY_LEN;
 
     /* Now parse entries */
-    parse_entries(ad, buf + AD_HEADER_LEN, nentries);
+    if (parse_entries(ad, buf + AD_HEADER_LEN, nentries)) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EINVAL;
+        EC_FAIL;
+    }
 
-    return 0;
+    if (nentries != ADEID_NUM_EA
+        || !ad_entry(ad, ADEID_FINDERI)
+        || !ad_entry(ad, ADEID_COMMENT)
+        || !ad_entry(ad, ADEID_FILEDATESI)
+        || !ad_entry(ad, ADEID_AFPFILEI)
+        || !ad_entry(ad, ADEID_PRIVDEV)
+        || !ad_entry(ad, ADEID_PRIVINO)
+        || !ad_entry(ad, ADEID_PRIVSYN)
+        || !ad_entry(ad, ADEID_PRIVID)) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid metadata EA", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
+    }
+
+    /*
+     * Ensure the resource fork offset is always set
+     */
+#ifndef HAVE_EAFD
+    if (ad->ad_vers == AD_VERSION_EA)
+        ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_OSX);
+#endif
+
+EC_CLEANUP:
+    if (ret != 0 && errno == EINVAL) {
+        become_root();
+        (void)sys_removexattr(path, AD_EA_META);
+        unbecome_root();
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): deleted invalid metadata EA", fullpathname(path), nentries);
+        errno = ENOENT;
+    }
+    EC_EXIT;
 }
 
 /*!
@@ -683,11 +847,13 @@ static int ad_mkrf(const char *path)
     return 0;
 }
 
+#ifdef HAVE_EAFD
 static int ad_mkrf_ea(const char *path _U_)
 {
     AFP_PANIC("ad_mkrf_ea: dont use");
     return 0;
 }
+#endif
 
 static int ad_mkrf_osx(const char *path _U_)
 {
@@ -754,7 +920,8 @@ static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
  *
  * We're called because opening ADFLAGS_HF caused an error.
  * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
- * 2. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
+ * 2. Open non-existent ressource fork, this will just result in first read return EOF
+ * 3. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
  *    ought to close it before returning with an error condition.
  */
 static int ad_error(struct adouble *ad, int adflags)
@@ -763,7 +930,9 @@ static int ad_error(struct adouble *ad, int adflags)
     if (adflags & ADFLAGS_NOHF) { /* 1 */
         return 0;
     }
-    if (adflags & (ADFLAGS_DF | ADFLAGS_SETSHRMD | ADFLAGS_CHECK_OF)) { /* 2 */
+    if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_RF) && (errno == ENOENT)) /* 2 */
+        return 0;
+    if (adflags & (ADFLAGS_DF | ADFLAGS_SETSHRMD | ADFLAGS_CHECK_OF)) { /* 3 */
         ad_close( ad, ADFLAGS_DF );
         err = errno;
     }
@@ -800,6 +969,9 @@ static int ad2openflags(const struct adouble *ad, int adfile, int adflags)
     if (adflags & ADFLAGS_TRUNC)
         oflags |= O_TRUNC;
 
+    if (!(ad->ad_options & ADVOL_FOLLO_SYML))
+        oflags |= O_NOFOLLOW;
+
     return oflags;
 }
 
@@ -812,7 +984,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
     int         st_invalid = -1;
     ssize_t     lsz;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_df(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -833,7 +1005,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
         goto EC_CLEANUP;
     }
 
-    oflags = O_NOFOLLOW | ad2openflags(ad, ADFLAGS_DF, adflags);
+    oflags = ad2openflags(ad, ADFLAGS_DF, adflags);
 
     admode = mode;
     if ((adflags & ADFLAGS_CREATE)) {
@@ -878,7 +1050,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
     ad->ad_data_fork.adf_refcount++;
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_df(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -898,7 +1070,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
     mode_t      admode;
     int         st_invalid = -1;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_v2(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -921,8 +1093,8 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
     }
 
     ad_p = ad->ad_ops->ad_path(path, adflags);
-    oflags = O_NOFOLLOW | ad2openflags(ad, ADFLAGS_HF, adflags);
-    LOG(log_debug, logtype_default,"ad_open_hf_v2(\"%s\"): open flags: %s",
+    oflags = ad2openflags(ad, ADFLAGS_HF, adflags);
+    LOG(log_debug, logtype_ad,"ad_open_hf_v2(\"%s\"): open flags: %s",
         fullpathname(path), openflags2logstr(oflags));
     nocreatflags = oflags & ~(O_CREAT | O_EXCL);
 
@@ -949,7 +1121,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
             /*
              * We're expecting to create a new adouble header file here
              */
-            LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
+            LOG(log_debug, logtype_ad, "ad_open(\"%s\"): creating adouble file",
                 fullpathname(path));
             admode = mode;
             errno = 0;
@@ -1010,7 +1182,7 @@ EC_CLEANUP:
         ad_meta_fileno(ad) = -1;
         ad->ad_mdp->adf_refcount = 0;
     }
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_v2(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -1022,18 +1194,17 @@ EC_CLEANUP:
 static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
 {
     EC_INIT;
-    ssize_t rforklen;
     int oflags;
     int opened = 0;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_ea(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
 
-    oflags = O_NOFOLLOW | (ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC));
+    oflags = ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC);
 
     if (ad_meta_fileno(ad) == AD_SYMLINK)
         goto EC_CLEANUP;
@@ -1043,7 +1214,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
         if ((oflags & O_RDWR) &&
             /* and it was already denied: */
             (ad->ad_mdp->adf_flags & O_RDONLY)) {
-            LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s",
+            LOG(log_error, logtype_ad, "ad_open_hf_ea(%s): rw request for ro file: %s",
                 fullpathname(path), strerror(errno));
             errno = EACCES;
             EC_FAIL;
@@ -1056,7 +1227,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
             if (adflags & ADFLAGS_DIR)
                 /* For directories we open the directory RDONYL so we can later fchdir()  */
                 oflags = (oflags & ~O_RDWR) | O_RDONLY;
-            LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
+            LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
             EC_NEG1(ad_meta_fileno(ad) = open(path, oflags));
             opened = 1;
             ad->ad_mdp->adf_flags = oflags;
@@ -1066,16 +1237,20 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
     /* Read the adouble header in and parse it.*/
     if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) {
         if (!(adflags & ADFLAGS_CREATE)) {
-            LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): can't read metadata EA", path);
+            LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): can't read metadata EA", path);
             errno = ENOENT;
             EC_FAIL;
         }
+        if ((adflags & ADFLAGS_CREATE) && (ad->ad_options & ADVOL_RO)) {
+            errno = EROFS;
+            EC_FAIL;
+        }
 
-        LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
+        LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
 
         /* It doesnt exist, EPERM or another error */
         if (!(errno == ENOATTR || errno == ENOENT)) {
-            LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
+            LOG(log_error, logtype_ad, "ad_open_hf_ea: unexpected: %s", strerror(errno));
             EC_FAIL;
         }
 
@@ -1083,12 +1258,12 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
         EC_NEG1_LOG(new_ad_header(ad, path, NULL, adflags));
         ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
         ad_flush(ad);
-        LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
+        LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
     }
 
     if (ad_meta_fileno(ad) != -1)
         ad->ad_mdp->adf_refcount++;
-    (void)ad_reso_size(path, adflags, ad);
+    ad->ad_rlen = ad_reso_size(path, adflags, ad);
 
 EC_CLEANUP:
     if (ret != 0 && opened && ad_meta_fileno(ad) != -1) {
@@ -1096,7 +1271,7 @@ EC_CLEANUP:
         ad_meta_fileno(ad) = -1;
         ad->ad_mdp->adf_refcount = 0;
     }
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_ea(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -1110,8 +1285,7 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
 {
     int ret = 0;
 
-    memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
-    ad->ad_rlen = 0;
+    ad->ad_meta_refcount++;
 
     switch (ad->ad_vers) {
     case AD_VERSION2:
@@ -1125,66 +1299,77 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
         break;
     }
 
-    if (ret != 0)
+    if (ret != 0) {
+        ad->ad_meta_refcount--;
         ret = ad_error(ad, adflags);
+    }
 
     return ret;
 }
 
 /*!
- * Get resofork length for adouble:ea
+ * Get resofork length for adouble:ea, parameter 'ad' may be NULL
  */
-static int ad_reso_size(const char *path, int adflags, struct adouble *ad)
+off_t ad_reso_size(const char *path, int adflags, struct adouble *ad)
 {
     EC_INIT;
     struct stat st;
+    off_t rlen;
 
-    if (adflags & ADFLAGS_DIR) {
-        ad->ad_rlen = 0;
-        goto EC_CLEANUP;
-    }
+    if (adflags & ADFLAGS_DIR)
+        EC_FAIL;
 
-    LOG(log_debug, logtype_default, "ad_reso_size(\"%s\"): BEGIN", path);
+    LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): BEGIN", path);
 
 #ifdef HAVE_EAFD
     ssize_t easz;
 
-    if (ad_reso_fileno(ad) != -1) {
+    if (ad && ad_reso_fileno(ad) != -1) {
         EC_NEG1( fstat(ad_reso_fileno(ad), &st) );
-        ad->ad_rlen = st.st_size;
-    } else if (ad_meta_fileno(ad) != -1) {
-        EC_NEG1( (ad->ad_rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) );
+        rlen = st.st_size;
+    } else if (ad && ad_meta_fileno(ad) != -1) {
+        EC_NEG1( (rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) );
     } else {
-        EC_NEG1( (ad->ad_rlen = sys_lgetxattr(path, AD_EA_RESO, NULL, 0)) );
+        EC_NEG1( (rlen = sys_lgetxattr(path, AD_EA_RESO, NULL, 0)) );
     }
 
 #else
     const char *rfpath;
-    EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags));
+    EC_NULL_LOG( rfpath = ad_path_osx(path, adflags));
     EC_ZERO( lstat(rfpath, &st));
     if (st.st_size > ADEDOFF_RFORK_OSX)
-        ad->ad_rlen = st.st_size - ADEDOFF_RFORK_OSX;
+        rlen = st.st_size - ADEDOFF_RFORK_OSX;
     else
-        ad->ad_rlen = 0;
+        rlen = 0;
 #endif
 
-    LOG(log_debug, logtype_default, "ad_reso_size(\"%s\"): size: %zd", path, ad->ad_rlen);
+    LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): size: %zd", path, rlen);
 
 EC_CLEANUP:
     if (ret != 0)
-        ad->ad_rlen = 0;
-    EC_EXIT;
+        rlen = 0;
+    return rlen;
 }
 
 static int ad_open_rf_v2(const char *path, int adflags, int mode, struct adouble *ad)
 {
+    EC_INIT;
+
     /*
      * ad_open_hf_v2() does the work, but if it failed and adflags are ADFLAGS_NOHF | ADFLAGS_RF
      * ad_open_hf_v2() didn't give an error, but we're supposed to return a reso fork fd
      */
-    if (!AD_RSRC_OPEN(ad) && !(adflags & ADFLAGS_NORF))
-        return -1;
-    return 0;
+
+    LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): BEGIN", fullpathname(path));
+
+    if (!AD_META_OPEN(ad) && !(adflags & (ADFLAGS_NORF | ADFLAGS_RDONLY)))
+        EC_FAIL;
+    if (AD_META_OPEN(ad))
+        ad->ad_reso_refcount++;
+
+EC_CLEANUP:
+    LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): END: %d", fullpathname(path), ret);
+    EC_EXIT;
 }
 
 static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble *ad)
@@ -1193,15 +1378,14 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
     int oflags;
     int opened = 0;
     int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF);
-    ssize_t rlen;
 #ifndef HAVE_EAFD
     const char *rfpath;
     struct stat st;
 #endif
 
-    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
 
-    oflags = O_NOFOLLOW | (ad2openflags(ad, ADFLAGS_RF, adflags) & ~O_CREAT);
+    oflags = ad2openflags(ad, ADFLAGS_RF, adflags) & ~O_CREAT;
 
     if (ad_reso_fileno(ad) != -1) {
         /* the file is already open, but we want write access: */
@@ -1212,8 +1396,9 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
             EC_FAIL;
         }
         ad->ad_rfp->adf_flags &= ~( O_TRUNC | O_CREAT );
+        ad->ad_reso_refcount++;
         ad->ad_rfp->adf_refcount++;
-        EC_NEG1_LOG( ad_reso_size(path, adflags, ad));
+        EC_NEG1_LOG( ad->ad_rlen = ad_reso_size(path, adflags, ad));
         goto EC_CLEANUP;
     }
 #ifdef HAVE_EAFD
@@ -1221,53 +1406,97 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
         EC_FAIL;
     if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
         if (!(adflags & ADFLAGS_CREATE)) {
-            errno = ENOENT;
-            EC_FAIL;
+            switch (errno) {
+            case EACCES:
+            case EPERM:
+            case EROFS:
+                if (!(adflags & ADFLAGS_RDONLY)) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                    EC_FAIL;
+                }
+                oflags &= ~O_RDWR;
+                oflags |= O_RDONLY;
+                if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                    EC_FAIL;
+                }
+                break;
+            case ENOENT:
+                EC_EXIT_STATUS(0);
+            default:
+                LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                EC_FAIL;
+            }
+        } else {
+            oflags |= O_CREAT;
+            EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad),
+                                                             AD_EA_RESO, oflags, 0666) );
         }
-        oflags |= O_CREAT;
-        EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad),
-                                                         AD_EA_RESO, oflags, 0666) ); 
     }
 #else
     EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags) );
     if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
-        if (!(adflags & ADFLAGS_CREATE))
-            EC_FAIL;
-        oflags |= O_CREAT;
-        EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
-            path, rfpath);
+        if (!(adflags & ADFLAGS_CREATE)) {
+            switch (errno) {
+            case EACCES:
+            case EPERM:
+            case EROFS:
+                if (!(adflags & ADFLAGS_RDONLY)) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                    EC_FAIL;
+                }
+                oflags &= ~O_RDWR;
+                oflags |= O_RDONLY;
+                if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                    EC_FAIL;
+                }
+                break;
+            case ENOENT:
+                EC_EXIT_STATUS(0);
+            default:
+                LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                EC_FAIL;
+            }
+        } else {
+            oflags |= O_CREAT;
+            EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
+            LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
+                path, rfpath);
+        }
     }
 #endif
     opened = 1;
     ad->ad_rfp->adf_refcount = 1;
     ad->ad_rfp->adf_flags = oflags;
+    ad->ad_reso_refcount++;
 
 #ifndef HAVE_EAFD
     EC_ZERO_LOG( fstat(ad_reso_fileno(ad), &st) );
     if (ad->ad_rfp->adf_flags & O_CREAT) {
         /* This is a new adouble header file, create it */
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
             path, rfpath);
         EC_NEG1_LOG( new_ad_header(ad, path, NULL, adflags) );
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
             path, rfpath);
         ad_flush(ad);
     } else {
         /* Read the adouble header */
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
             path, rfpath);
-        EC_NEG1_LOG( ad_header_read_osx(NULL, ad, &st) );
+        EC_NEG1_LOG( ad_header_read_osx(rfpath, ad, &st) );
     }
 #endif
 
-    (void)ad_reso_size(path, adflags, ad);
+    ad->ad_rlen = ad_reso_size(path, adflags, ad);
 
 EC_CLEANUP:
     if (ret != 0) {
         if (opened && (ad_reso_fileno(ad) != -1)) {
             close(ad_reso_fileno(ad));
             ad_reso_fileno(ad) = -1;
+            ad->ad_reso_refcount--;
             ad->ad_rfp->adf_refcount = 0;
         }
         if (adflags & ADFLAGS_NORF) {
@@ -1280,7 +1509,7 @@ EC_CLEANUP:
         ad->ad_rlen = 0;
     }
 
-    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
 
     EC_EXIT;
 }
@@ -1323,7 +1552,7 @@ off_t ad_getentryoff(const struct adouble *ad, int eid)
 #ifdef HAVE_EAFD
         return 0;
 #else
-        return ADEDOFF_RFORK_OSX;
+        return ad->ad_eid[eid].ade_off;
 #endif
     default:
         return ad->ad_eid[eid].ade_off;
@@ -1478,7 +1707,7 @@ int ad_stat(const char *path, struct stat *stbuf)
     p = ad_dir(path);
     if (!p)
         return -1;
-    return lstat( p, stbuf );
+    return stat( p, stbuf );
 }
 
 /* ----------------
@@ -1500,7 +1729,7 @@ int ad_mkdir( const char *path, mode_t mode)
     int st_invalid;
     struct stat stbuf;
 
-    LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
+    LOG(log_debug, logtype_ad, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
         path, mode, getcwdpath());
 
     st_invalid = ad_mode_st(path, &mode, &stbuf);
@@ -1534,6 +1763,7 @@ static void ad_init_func(struct adouble *ad)
     ad_reso_fileno(ad) = -1;
     ad_meta_fileno(ad) = -1;
     ad->ad_refcount = 1;
+    ad->ad_rlen = 0;
     return;
 }
 
@@ -1595,6 +1825,12 @@ void ad_init(struct adouble *ad, const struct vol * restrict vol)
  * - we remember open fds for files because me must avoid a single close releasing fcntl locks for other
  *   fds of the same file
  *
+ * BUGS:
+ *
+ * * on Solaris (HAVE_EAFD) ADFLAGS_RF doesn't work without
+ *   ADFLAGS_HF, because it checks whether ad_meta_fileno() is already
+ *   openend. As a workaround pass ADFLAGS_SETSHRMD.
+ *
  * @returns 0 on success, any other value indicates an error
  */
 int ad_open(struct adouble *ad, const char *path, int adflags, ...)
@@ -1603,7 +1839,7 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     va_list args;
     mode_t mode = 0;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open(\"%s\", %s): BEGIN {d: %d, m: %d, r: %d}"
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
@@ -1650,17 +1886,13 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     }
 
     if (adflags & ADFLAGS_HF) {
-        ad->ad_meta_refcount++;
         if (ad_open_hf(path, adflags, mode, ad) != 0) {
-            ad->ad_meta_refcount--;
             EC_FAIL;
         }
     }
 
     if (adflags & ADFLAGS_RF) {
-        ad->ad_reso_refcount++;
         if (ad_open_rf(path, adflags, mode, ad) != 0) {
-            ad->ad_reso_refcount--;
             EC_FAIL;
         }
     }
@@ -1670,7 +1902,7 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     }
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open(\"%s\"): END: %d {d: %d, m: %d, r: %d}"
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), ret,
@@ -1720,7 +1952,7 @@ int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
     int cwdfd = -1;
 
     if (dirfd != -1) {
-        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
+        if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
             ret = -1;
             goto exit;
         }
@@ -1734,7 +1966,7 @@ int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
     if (dirfd != -1) {
 
         if (fchdir(cwdfd) != 0) {
-            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+            LOG(log_error, logtype_ad, "ad_openat: cant chdir back, exiting");
             exit(EXITERR_SYS);
         }
     }
@@ -1801,10 +2033,10 @@ int ad_openat(struct adouble  *ad,
     EC_INIT;
     int cwdfd = -1;
     va_list args;
-    mode_t mode;
+    mode_t mode = 0;
 
     if (dirfd != -1) {
-        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0))
+        if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0))
             EC_FAIL;
     }
 
index ed7c0cb4da2b182b93a7c203e615403751682bb0..852a6e963b94f964e3b47d1ea8b67df6cac0717e 100644 (file)
@@ -62,7 +62,6 @@ ssize_t adf_pread(struct ad_fd *ad_fd, void *buf, size_t count, off_t offset)
 ssize_t ad_read( struct adouble *ad, const uint32_t eid, off_t off, char *buf, const size_t buflen)
 {
     ssize_t     cc;
-    ssize_t     rlen;
     off_t r_off = 0;
 
     /* We're either reading the data fork (and thus the data file)
diff --git a/libatalk/adouble/ad_recvfile.c b/libatalk/adouble/ad_recvfile.c
new file mode 100644 (file)
index 0000000..1703ee8
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) Jeremy Allison 2007
+ * Copyright (c) 2013 Ralph Boehme <sloowfranklin@gmail.com>
+ * All rights reserved. See COPYRIGHT.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef WITH_RECVFILE
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/select.h>
+
+#include <atalk/adouble.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
+static int ad_recvfile_init(const struct adouble *ad, int eid, off_t *off)
+{
+    int fd;
+
+    if (eid == ADEID_DFORK) {
+        fd = ad_data_fileno(ad);
+    } else {
+        *off += ad_getentryoff(ad, eid);
+        fd = ad_reso_fileno(ad);
+    }
+
+    return fd;
+}
+
+/*
+ * If tofd is -1, drain the incoming socket of count bytes without writing to the outgoing fd,
+ * if a write fails we do the same.
+ *
+ * Returns -1 on short reads from fromfd (read error) and sets errno.
+ *
+ * Returns number of bytes written to 'tofd'  or thrown away if 'tofd == -1'.
+ * return != count then sets errno.
+ * Returns count if complete success.
+ */
+
+#define TRANSFER_BUF_SIZE (128*1024)
+
+static ssize_t default_sys_recvfile(int fromfd,
+                                    int tofd,
+                                    off_t offset,
+                                    size_t count)
+{
+    int saved_errno = 0;
+    size_t total = 0;
+    size_t bufsize = MIN(TRANSFER_BUF_SIZE, count);
+    size_t total_written = 0;
+    char *buffer = NULL;
+
+    if (count == 0) {
+        return 0;
+    }
+
+    LOG(log_maxdebug, logtype_dsi, "default_recvfile: from = %d, to = %d, offset = %.0f, count = %lu\n",
+        fromfd, tofd, (double)offset, (unsigned long)count);
+
+    if ((buffer = malloc(bufsize)) == NULL)
+        return -1;
+
+    while (total < count) {
+        size_t num_written = 0;
+        ssize_t read_ret;
+        size_t toread = MIN(bufsize,count - total);
+
+        /* Read from socket - ignore EINTR. */
+        read_ret = read(fromfd, buffer, toread);
+        if (read_ret <= 0) {
+            /* EOF or socket error. */
+            free(buffer);
+            return -1;
+        }
+
+        num_written = 0;
+
+        while (num_written < read_ret) {
+            ssize_t write_ret;
+
+            if (tofd == -1) {
+                write_ret = read_ret;
+            } else {
+                /* Write to file - ignore EINTR. */
+                write_ret = pwrite(tofd, buffer + num_written, read_ret - num_written, offset);
+                if (write_ret <= 0) {
+                    /* write error - stop writing. */
+                    tofd = -1;
+                    saved_errno = errno;
+                    continue;
+                }
+            }
+            num_written += (size_t)write_ret;
+            total_written += (size_t)write_ret;
+        }
+        total += read_ret;
+    }
+
+    free(buffer);
+    if (saved_errno) {
+        /* Return the correct write error. */
+        errno = saved_errno;
+    }
+    return (ssize_t)total_written;
+}
+
+#ifdef HAVE_SPLICE
+static int waitfordata(int socket)
+{
+    fd_set readfds;
+    int maxfd = socket + 1;
+    int ret;
+
+    FD_ZERO(&readfds);
+
+    while (1) {
+        FD_ZERO(&readfds);
+        FD_SET(socket, &readfds);
+        if ((ret = select(maxfd, &readfds, NULL, NULL, NULL)) <= 0) {
+            if (ret == -1 && errno == EINTR)
+                continue;
+            LOG(log_error, logtype_dsi, "waitfordata: unexpected select return: %d %s",
+                ret, ret < 0 ? strerror(errno) : "");
+            return -1;
+        }
+        if (FD_ISSET(socket, &readfds))
+            return 0;
+        return -1;
+    }
+
+}
+
+/*
+ * Try and use the Linux system call to do this.
+ * Remember we only return -1 if the socket read
+ * failed. Else we return the number of bytes
+ * actually written. We always read count bytes
+ * from the network in the case of return != -1.
+ */
+static ssize_t sys_recvfile(int fromfd, int tofd, off_t offset, size_t count, int splice_size)
+{
+    static int pipefd[2] = { -1, -1 };
+    static bool try_splice_call = true;
+    size_t total_written = 0;
+    loff_t splice_offset = offset;
+
+    LOG(log_debug, logtype_dsi, "sys_recvfile: from = %d, to = %d, offset = %.0f, count = %lu",
+        fromfd, tofd, (double)offset, (unsigned long)count);
+
+    if (count == 0)
+        return 0;
+
+    /*
+     * Older Linux kernels have splice for sendfile,
+     * but it fails for recvfile. Ensure we only try
+     * this once and always fall back to the userspace
+     * implementation if recvfile splice fails. JRA.
+     */
+
+    if (!try_splice_call) {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
+        try_splice_call = false;
+        errno = ENOSYS;
+        return -1;
+    }
+
+    while (count > 0) {
+        int nread, to_write;
+
+        nread = splice(fromfd, NULL, pipefd[1], NULL, MIN(count, splice_size), SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+
+        if (nread == -1) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EAGAIN) {
+                if (waitfordata(fromfd) != -1)
+                    continue;
+                return -1;
+            }
+            if (total_written == 0 && (errno == EBADF || errno == EINVAL)) {
+                LOG(log_warning, logtype_dsi, "splice() doesn't work for recvfile");
+                try_splice_call = false;
+                errno = ENOSYS;
+                return -1;
+            }
+            break;
+        }
+
+        to_write = nread;
+        while (to_write > 0) {
+            int thistime;
+            thistime = splice(pipefd[0], NULL, tofd, &splice_offset, to_write, SPLICE_F_MOVE);
+            if (thistime == -1)
+                return -1;
+            to_write -= thistime;
+        }
+
+        total_written += nread;
+        count -= nread;
+    }
+
+done:
+    LOG(log_maxdebug, logtype_dsi, "sys_recvfile: total_written: %zu", total_written);
+
+    return total_written;
+}
+#else
+
+/*****************************************************************
+ No recvfile system call - use the default 128 chunk implementation.
+*****************************************************************/
+
+ssize_t sys_recvfile(int fromfd, int tofd, off_t offset, size_t count)
+{
+    return default_sys_recvfile(fromfd, tofd, offset, count);
+}
+#endif
+
+/* read from a socket and write to an adouble file */
+ssize_t ad_recvfile(struct adouble *ad, int eid, int sock, off_t off, size_t len, int splice_size)
+{
+    ssize_t cc;
+    int fd;
+    off_t off_fork = off;
+
+    fd = ad_recvfile_init(ad, eid, &off_fork);
+    if ((cc = sys_recvfile(sock, fd, off_fork, len, splice_size)) != len)
+        return -1;
+
+    if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid)))
+        ad_setentrylen(ad, eid, off);
+
+    return cc;
+}
+#endif
index 3cb28e7802c935db16ab4acf80645db577b43816..2730644b69adc1669868fe5d3da4bb6b4cd1971e 100644 (file)
@@ -103,30 +103,4 @@ int ad_readfile_init(const struct adouble *ad,
 
   return fd;
 }
-
-
-/* ------------------------ */
-#if 0
-#ifdef HAVE_SENDFILE_WRITE
-/* read from a socket and write to an adouble file */
-ssize_t ad_writefile(struct adouble *ad, const int eid, 
-                    const int sock, off_t off, const int end,
-                    const size_t len)
-{
-#ifdef __linux__
-  ssize_t cc;
-  int fd;
-
-  fd = ad_sendfile_init(ad, eid, &off, end);
-  if ((cc = sys_sendfile(fd, sock, &off, len)) < 0)
-    return -1;
-
-  if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid))) 
-    ad_setentrylen(ad, eid, off);
-
-  return cc;
-#endif /* __linux__ */
-}
-#endif /* HAVE_SENDFILE_WRITE */
-#endif /* 0 */
 #endif
index 337ff6ed3d5f07075ff286dfa58964488c9158f4..6b4ed96d40a9fd09a8173becbce1d28dca3d43f6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: ad_size.c,v 1.8 2010-02-26 14:13:16 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index bfed9b400f27f8fd577310bc4052a76e2e6a5017..4ae1cf60adf1cfcbd61907d20a5fc430c1f4d31a 100644 (file)
@@ -52,7 +52,6 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
     EC_INIT;
     struct stat                st;
     ssize_t            cc;
-    size_t roundup;
     off_t    r_off;
 
     if (ad_data_fileno(ad) == AD_SYMLINK) {
@@ -60,7 +59,7 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
         return -1;
     }
 
-    LOG(log_debug, logtype_default, "ad_write: off: %ju, size: %zu, eabuflen: %zu",
+    LOG(log_debug, logtype_ad, "ad_write: off: %ju, size: %zu, eabuflen: %zu",
         (uintmax_t)off, buflen, ad->ad_rlen);
     
     if ( eid == ADEID_DFORK ) {
@@ -94,7 +93,6 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
         return -1; /* we don't know how to write if it's not a ressource or data fork */
     }
 
-EC_CLEANUP:
     if (ret != 0)
         return ret;
     return( cc );
@@ -159,30 +157,35 @@ char            c = 0;
 }
 
 /* ------------------------ */
-int ad_rtruncate( struct adouble *ad, const off_t size)
+int ad_rtruncate(struct adouble *ad, const char *uname, const off_t size)
 {
     EC_INIT;
 
-#ifndef HAVE_EAFD
-    if (ad->ad_vers == AD_VERSION_EA && size == 0)
-        EC_NEG1( unlink(ad->ad_ops->ad_path(ad->ad_name, 0)) );
-    else
-#endif
-        EC_NEG1( sys_ftruncate(ad_reso_fileno(ad), size + ad->ad_eid[ ADEID_RFORK ].ade_off) );
+    /*
+     * We can't delete 0 byte size resource forks either, because a
+     * fork may reference the adouble handle with an open fd for the
+     * file, which means we would only delete the directory entry, not
+     * the file. Subsequently all code that works with fork handles
+     * finds the fork open, so eg flushing a fork (ad_flush()) will
+     * recreate ._ files.  The correct place to delete 0 byte sized
+     * resource forks is in of_closefork().
+     */
+
+    EC_NEG1( sys_ftruncate(ad_reso_fileno(ad), size + ad->ad_eid[ ADEID_RFORK ].ade_off) );
+
+    ad->ad_rlen = size;
 
 EC_CLEANUP:
-    if (ret == 0)
-        ad->ad_rlen = size;    
-    else
-        LOG(log_error, logtype_default, "ad_rtruncate(\"%s\"): %s",
-            fullpathname(ad->ad_name), strerror(errno));
+    if (ret != 0)
+        LOG(log_error, logtype_ad, "ad_rtruncate(\"%s\"): %s",
+            fullpathname(uname), strerror(errno));
     EC_EXIT;
 }
 
 int ad_dtruncate(struct adouble *ad, const off_t size)
 {
     if (sys_ftruncate(ad_data_fileno(ad), size) < 0) {
-        LOG(log_error, logtype_default, "sys_ftruncate(fd: %d): %s",
+        LOG(log_error, logtype_ad, "sys_ftruncate(fd: %d): %s",
             ad_data_fileno(ad), strerror(errno));
         return -1;
     }
index 7b59b2e8ee67a9340da551ad81a164a1df544e27..98e1a1553864bf2fc820d080b99ae56ab01b295b 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -77,7 +76,6 @@
  */
 bstring brefcstr (char *str) {
     bstring b;
-    int i;
     size_t j;
 
        if (str == NULL)
@@ -90,7 +88,7 @@ bstring brefcstr (char *str) {
 
        b->slen = (int) j;
     b->mlen = -1;
-    b->data = str;
+    b->data = (unsigned char *)str;
 
        return b;
 }
index d1051e678783251d09452815fa7ad1fbce16b109..5c9dd52c93f1e7d5a7e6b7358bd2b61daaf04110 100644 (file)
@@ -17,6 +17,11 @@ if USE_TDB_BACKEND
 LIBCNID_DEPS += tdb/libcnid_tdb.la
 endif
 
+if USE_MYSQL_BACKEND
+SUBDIRS += mysql
+LIBCNID_DEPS += @MYSQL_LIBS@ mysql/libcnid_mysql.la
+endif
+
 libcnid_la_SOURCES = cnid.c cnid_init.c
 libcnid_la_LIBADD = $(LIBCNID_DEPS)
 
index a1e69a76ac5446a37afde531b208c9cbdc1a46bf..18f42c3cc0ffc36c89fde8c2ba6310d2871bdba0 100644 (file)
@@ -173,7 +173,7 @@ cnid_t cnid_cdb_add(struct _cnid_db *cdb, const struct stat *st,
     cnid_t id;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
     }
@@ -192,7 +192,7 @@ cnid_t cnid_cdb_add(struct _cnid_db *cdb, const struct stat *st,
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    if ((data.data = make_cnid_data(cdb->flags, st, did, name, len)) == NULL) {
+    if ((data.data = make_cnid_data(cdb->cnid_db_flags, st, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
         errno = CNID_ERR_PATH;
         return CNID_INVALID;
@@ -248,7 +248,7 @@ int cnid_cdb_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
     int rc;
     CNID_private *db;
 
-    if (!cdb || !(db = cdb->_private) || !buffer || !len) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !buffer || !len) {
         errno = CNID_ERR_PARAM;
         return -1;
     }
index fcc01df2eb6ded40fc339f039389e548f6ab97e7..64ac20370f616b0ffce3831fa6f90e4086137106 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_close.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
@@ -17,7 +16,7 @@ void cnid_cdb_close(struct _cnid_db *cdb) {
            return;
     }
 
-    if (!(db = cdb->_private)) {
+    if (!(db = cdb->cnid_db_private)) {
         return;
     }
     db->db_didname->sync(db->db_didname, 0); 
@@ -31,7 +30,6 @@ void cnid_cdb_close(struct _cnid_db *cdb) {
     db->dbenv->close(db->dbenv, 0);
 
     free(db);
-    free(cdb->volpath);
     free(cdb);
 }
 
index 1d14640af1fb68c77b2e9903a07385a36e47b619..deaeca44a816349a123bc9dbd5109c96658c0964 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_delete.c,v 1.4 2009-10-29 13:38:16 didg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
@@ -22,7 +21,7 @@ int cnid_cdb_delete(struct _cnid_db *cdb, const cnid_t id) {
     DBT key;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || !id || (db->flags & CNIDFLAG_DB_RO)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || (db->flags & CNIDFLAG_DB_RO)) {
         return -1;
     }
 
index d4e090550eab6f85e3de0aa6671f7aa7d6006453..9c682a8c1d260781ed3d734e994c74f34917995e 100644 (file)
@@ -14,7 +14,7 @@ cnid_t cnid_cdb_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t l
     cnid_t id;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || (len > MAXPATHLEN)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || (len > MAXPATHLEN)) {
         return 0;
     }
 
index 7b2f933c5182b4e1c4d4974f5a46c3bd7aa695ac..950709d5d201a6d3081b8c301201f2e324a20cd6 100644 (file)
@@ -26,11 +26,11 @@ cnid_t cnid_cdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     cnid_t id_devino, id_didname,id = 0;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
         return 0;
     }
     
-    if ((buf = make_cnid_data(cdb->flags, st, did, name, len)) == NULL) {
+    if ((buf = make_cnid_data(cdb->cnid_db_flags, st, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "cnid_lookup: Pathname is too long");
         return 0;
     }
@@ -63,7 +63,7 @@ cnid_t cnid_cdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
         type_devino = ntohl(type_devino);
     }
 
-    buf = make_cnid_data(cdb->flags, st, did, name, len);
+    buf = make_cnid_data(cdb->cnid_db_flags, st, did, name, len);
     key.data = buf +CNID_DID_OFS;
     key.size = CNID_DID_LEN + len + 1;
     
index 9184264ae8ec40f420d7847328fac9050496cb30..f88a9e29c0e103416b9f7f96c21f5bdd70b9447e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_meta.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
index cf9d905a614cf9bb65293b363084ab6ec14c10cd..e8a4bdd050b2abe559e816d8928d21fa7a2e25f1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_meta.h,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #define CNID_META_CNID_LEN      4
index 60141c3bea5f7de7c1e688de9a983f00f21ab3b5..ba14bd8e46fa3d6a28f043d016477ba21307ae06 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_nextid.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 056420dad099f1123a6e514f98630055d285fa3a..0a8cfa39ed8fa4c9811747909e822c7f8d69c2d8 100644 (file)
@@ -38,6 +38,7 @@
 
 #ifdef CNID_BACKEND_CDB
 
+#include <atalk/volume.h>
 #include <atalk/cnid_private.h>
 #include "cnid_cdb_private.h"
 
@@ -109,7 +110,7 @@ static int my_open(DB * p, const char *f, const char *d, DBTYPE t, u_int32_t fla
 }
 
 /* --------------- */
-static struct _cnid_db *cnid_cdb_new(const char *volpath)
+static struct _cnid_db *cnid_cdb_new(struct vol *vol)
 {
     struct _cnid_db *cdb;
     int major, minor, patch;
@@ -127,13 +128,8 @@ static struct _cnid_db *cnid_cdb_new(const char *volpath)
     if ((cdb = (struct _cnid_db *) calloc(1, sizeof(struct _cnid_db))) == NULL)
         return NULL;
 
-    if ((cdb->volpath = strdup(volpath)) == NULL) {
-        free(cdb);
-        return NULL;
-    }
-
-    cdb->flags = CNID_FLAG_PERSISTENT;
-
+    cdb->cnid_db_vol = vol;
+    cdb->cnid_db_flags = CNID_FLAG_PERSISTENT;
     cdb->cnid_add = cnid_cdb_add;
     cdb->cnid_delete = cnid_cdb_delete;
     cdb->cnid_get = cnid_cdb_get;
@@ -144,7 +140,7 @@ static struct _cnid_db *cnid_cdb_new(const char *volpath)
     cdb->cnid_close = cnid_cdb_close;
     cdb->cnid_getstamp = cnid_cdb_getstamp;
     cdb->cnid_rebuild_add = cnid_cdb_rebuild_add;
-
+    cdb->cnid_wipe = NULL;
     return cdb;
 }
 
@@ -188,19 +184,16 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
     int open_flag, len;
     static int first = 0;
     int rc;
-
-    if (!args->dir || *args->dir == 0) {
-        return NULL;
-    }
+    struct vol *vol = args->cnid_args_vol;
 
     /* this checks .AppleDB.
        We need space for dir + '/' + DBHOMELEN + '/' + DBLEN */
-    if ((len = strlen(args->dir)) > (MAXPATHLEN - DBHOMELEN - DBLEN - 2)) {
-        LOG(log_error, logtype_default, "cnid_open: Pathname too large: %s", args->dir);
+    if ((len = strlen(vol->v_path)) > (MAXPATHLEN - DBHOMELEN - DBLEN - 2)) {
+        LOG(log_error, logtype_default, "cnid_open: Pathname too large: %s", vol->v_path);
         return NULL;
     }
 
-    if ((cdb = cnid_cdb_new(args->dir)) == NULL) {
+    if ((cdb = cnid_cdb_new(vol)) == NULL) {
         LOG(log_error, logtype_default, "cnid_open: Unable to allocate memory for database");
         return NULL;
     }
@@ -210,17 +203,16 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         goto fail_cdb;
     }
 
-    cdb->_private = (void *) db;
-    db->magic = CNID_DB_MAGIC;
+    cdb->cnid_db_private = (void *) db;
 
-    strcpy(path, args->dir);
+    strcpy(path, vol->v_path);
     if (path[len - 1] != '/') {
         strcat(path, "/");
         len++;
     }
 
     strcpy(path + len, DBHOME);
-    if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~args->mask) < 0)) {
+    if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~vol->v_umask) < 0)) {
         LOG(log_error, logtype_default, "cnid_open: DBHOME mkdir failed for %s", path);
         goto fail_adouble;
     }
@@ -247,7 +239,7 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
     }
 
     /* Open the database environment. */
-    if ((rc = db->dbenv->open(db->dbenv, path, DBOPTIONS, 0666 & ~args->mask)) != 0) {
+    if ((rc = db->dbenv->open(db->dbenv, path, DBOPTIONS, 0666 & ~vol->v_umask)) != 0) {
        LOG(log_error, logtype_default, "cnid_open: dbenv->open (rw) of %s failed: %s", path, db_strerror(rc));
        /* FIXME: This should probably go. Even if it worked, any use for a read-only DB? Didier? */
         if (rc == DB_RUNRECOVERY) {
@@ -260,10 +252,10 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         /* We can't get a full transactional environment, so multi-access
          * is out of the question.  Let's assume a read-only environment,
          * and try to at least get a shared memory pool. */
-        if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666 & ~args->mask)) != 0) {
+        if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666 & ~vol->v_umask)) != 0) {
             /* Nope, not a MPOOL, either.  Last-ditch effort: we'll try to
              * open the environment with no flags. */
-            if ((rc = db->dbenv->open(db->dbenv, path, 0, 0666 & ~args->mask)) != 0) {
+            if ((rc = db->dbenv->open(db->dbenv, path, 0, 0666 & ~vol->v_umask)) != 0) {
                 LOG(log_error, logtype_default, "cnid_open: dbenv->open of %s failed: %s", path, db_strerror(rc));
                 goto fail_lock;
             }
@@ -282,7 +274,7 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         goto fail_appinit;
     }
 
-    if ((rc = my_open(db->db_cnid, DBCNID, DBCNID, DB_BTREE, open_flag, 0666 & ~args->mask)) != 0) {
+    if ((rc = my_open(db->db_cnid, DBCNID, DBCNID, DB_BTREE, open_flag, 0666 & ~vol->v_umask)) != 0) {
         LOG(log_error, logtype_default, "cnid_open: Failed to open dev/ino database: %s",
             db_strerror(rc));
         goto fail_appinit;
@@ -297,7 +289,7 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         goto fail_appinit;
     }
 
-    if ((rc = my_open(db->db_didname, DBCNID, DBDIDNAME, DB_BTREE, open_flag, 0666 & ~args->mask))) {
+    if ((rc = my_open(db->db_didname, DBCNID, DBDIDNAME, DB_BTREE, open_flag, 0666 & ~vol->v_umask))) {
         LOG(log_error, logtype_default, "cnid_open: Failed to open did/name database: %s",
             db_strerror(rc));
         goto fail_appinit;
@@ -312,7 +304,7 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         goto fail_appinit;
     }
 
-    if ((rc = my_open(db->db_devino, DBCNID, DBDEVINO, DB_BTREE, open_flag, 0666 & ~args->mask)) != 0) {
+    if ((rc = my_open(db->db_devino, DBCNID, DBDEVINO, DB_BTREE, open_flag, 0666 & ~vol->v_umask)) != 0) {
         LOG(log_error, logtype_default, "cnid_open: Failed to open devino database: %s",
             db_strerror(rc));
         goto fail_appinit;
@@ -373,8 +365,6 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
     free(db);
 
   fail_cdb:
-    if (cdb->volpath != NULL)
-        free(cdb->volpath);
     free(cdb);
 
     return NULL;
index be2db1023692be7e7d966b76e23d1773b37ee1a0..951674656c85931f45edd6cdf7a4fa8c99fe8a4b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_rebuild_add.c,v 1.6 2009-11-20 17:22:11 didg Exp $
  *
  * All Rights Reserved. See COPYRIGHT.
  *
@@ -80,7 +79,7 @@ cnid_t cnid_cdb_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
     DBT key, data;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name || hint == CNID_INVALID || hint < CNID_START) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name || hint == CNID_INVALID || hint < CNID_START) {
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
     }
@@ -102,7 +101,7 @@ cnid_t cnid_cdb_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    if ((data.data = make_cnid_data(cdb->flags, st, did, name, len)) == NULL) {
+    if ((data.data = make_cnid_data(cdb->cnid_db_flags, st, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
         errno = CNID_ERR_PATH;
         return CNID_INVALID;
index 6c16ce7ec6a12b6341d5758075ba1325b7f09945..d3f6dab97a64099f763e9a62a93dc2439b7e329e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_resolve.c,v 1.5 2009-10-29 13:38:16 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
@@ -15,7 +14,7 @@ char *cnid_cdb_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le
     DBT key, data;
     int rc;
 
-    if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !(*id)) {
         return NULL;
     }
 
index f7676c840054516c10be062dae3e874719b7e23e..90c7d93ff54655b82812b1699732fab60b5c3250 100644 (file)
@@ -20,7 +20,7 @@ int cnid_cdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     int notfound = 0;
     char getbuf[CNID_HEADER_LEN + MAXPATHLEN +1];
 
-    if (!cdb || !(db = cdb->_private) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO)) {
         return -1;
     }
 
@@ -28,7 +28,7 @@ int cnid_cdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     memset(&pkey, 0, sizeof(pkey));
     memset(&data, 0, sizeof(data));
 
-    buf = make_cnid_data(cdb->flags, st, did, name, len);
+    buf = make_cnid_data(cdb->cnid_db_flags, st, did, name, len);
 
     key.data = buf +CNID_DEVINO_OFS;
     key.size = CNID_DEVINO_LEN;
@@ -54,7 +54,7 @@ int cnid_cdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     }
 
     memset(&pkey, 0, sizeof(pkey));
-    buf = make_cnid_data(cdb->flags, st, did, name, len);
+    buf = make_cnid_data(cdb->cnid_db_flags, st, did, name, len);
     key.data = buf + CNID_DID_OFS;
     key.size = CNID_DID_LEN + len + 1;
 
@@ -83,7 +83,7 @@ int cnid_cdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
 
     memset(&data, 0, sizeof(data));
     /* Make a new entry. */
-    buf = make_cnid_data(cdb->flags, st, did, name, len);
+    buf = make_cnid_data(cdb->cnid_db_flags, st, did, name, len);
     data.data = buf;
     memcpy(data.data, &id, sizeof(id));
     data.size = CNID_HEADER_LEN + len + 1;
index f0b8903d6ccf95782bbb7dcd4809d01239733402..24de358895f931b97cb94cb9ff7320bc3dcb5178 100644 (file)
@@ -30,6 +30,7 @@
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/compat.h>
+#include <atalk/volume.h>
 
 /* List of all registered modules. */
 static struct list_head modules = ATALK_LIST_HEAD_INIT(modules);
@@ -91,8 +92,7 @@ static int cnid_dir(const char *dir, mode_t mask)
 }
 
 /* Opens CNID database using particular back-end */
-struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int flags,
-                           const char *cnidsrv, const char *cnidport)
+struct _cnid_db *cnid_open(struct vol *vol, char *type, int flags)
 {
     struct _cnid_db *db;
     cnid_module *mod = NULL;
@@ -119,7 +119,7 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla
             LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
             return NULL;
         }
-        if (cnid_dir(volpath, mask) < 0) {
+        if (cnid_dir(vol->v_path, vol->v_umask) < 0) {
             if ( setegid(gid) < 0 || seteuid(uid) < 0) {
                 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
                 exit(EXITERR_SYS);
@@ -128,7 +128,11 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla
         }
     }
 
-    struct cnid_open_args args = {volpath, mask, flags, cnidsrv, cnidport};
+    struct cnid_open_args args =  {
+        .cnid_args_flags = flags,
+        .cnid_args_vol   = vol
+    };
+
     db = mod->cnid_open(&args);
 
     if ((mod->flags & CNID_FLAG_SETUID) && !(flags & CNID_FLAG_MEMORY)) {
@@ -140,16 +144,15 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla
     }
 
     if (NULL == db) {
-        LOG(log_error, logtype_afpd, "Cannot open CNID db at [%s].", volpath);
+        LOG(log_error, logtype_afpd, "Cannot open CNID db at [%s].", vol->v_path);
         return NULL;
     }
-    /* FIXME should module know about it ? */
-    if ((flags & CNID_FLAG_NODEV)) {
-        db->flags |= CNID_FLAG_NODEV;
-    }
-    db->flags |= mod->flags;
 
-    if ((db->flags & CNID_FLAG_BLOCK)) {
+    db->cnid_db_flags |= mod->flags;
+    if (flags & CNID_FLAG_NODEV)
+        db->cnid_db_flags |= CNID_FLAG_NODEV;
+
+    if (db->cnid_db_flags & CNID_FLAG_BLOCK) {
         sigemptyset(&sigblockset);
         sigaddset(&sigblockset, SIGTERM);
         sigaddset(&sigblockset, SIGHUP);
@@ -207,7 +210,7 @@ void cnid_close(struct _cnid_db *db)
         return;
     }
     /* cnid_close free db */
-    flags = db->flags;
+    flags = db->cnid_db_flags;
     block_signal(flags);
     db->cnid_close(db);
     unblock_signal(flags);
@@ -222,9 +225,9 @@ cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
     if (len == 0)
         return CNID_INVALID;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = valide(cdb->cnid_add(cdb, st, did, name, len, hint));
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -233,9 +236,9 @@ int cnid_delete(struct _cnid_db *cdb, cnid_t id)
 {
 int ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_delete(cdb, id);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -245,9 +248,9 @@ cnid_t cnid_get(struct _cnid_db *cdb, const cnid_t did, char *name,const size_t
 {
 cnid_t ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = valide(cdb->cnid_get(cdb, did, name, len));
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -266,9 +269,9 @@ time_t t;
        memcpy(buffer, &t, sizeof(time_t));
         return 0;
     }
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_getstamp(cdb, buffer, len);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -278,9 +281,9 @@ cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did
 {
     cnid_t ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = valide(cdb->cnid_lookup(cdb, st, did, name, len));
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -294,9 +297,9 @@ int cnid_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buff
         return -1;
     }
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_find(cdb, name, namelen, buffer, buflen);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
 
@@ -305,9 +308,9 @@ char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
 {
 char *ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_resolve(cdb, id, buffer, len);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     if (ret && !strcmp(ret, "..")) {
         LOG(log_error, logtype_afpd, "cnid_resolve: name is '..', corrupted db? ");
         ret = NULL;
@@ -321,9 +324,9 @@ int cnid_update   (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
 {
 int ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_update(cdb, id, st, did, name, len);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
                        
@@ -333,8 +336,20 @@ cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_
 {
 cnid_t ret;
 
-    block_signal(cdb->flags);
+    block_signal(cdb->cnid_db_flags);
     ret = cdb->cnid_rebuild_add(cdb, st, did, name, len, hint);
-    unblock_signal(cdb->flags);
+    unblock_signal(cdb->cnid_db_flags);
+    return ret;
+}
+
+/* --------------- */
+int cnid_wipe(struct _cnid_db *cdb)
+{
+    int ret = 0;
+
+    block_signal(cdb->cnid_db_flags);
+    if (cdb->cnid_wipe)
+        ret = cdb->cnid_wipe(cdb);
+    unblock_signal(cdb->cnid_db_flags);
     return ret;
 }
index 7cc108d3bcf5013dc6724ff96316034b36aad4c8..03cad3886846359872ca50d2a543f2358a705e46 100644 (file)
@@ -1,6 +1,5 @@
 
 /* 
- * $Id: cnid_init.c,v 1.3 2009-10-13 22:55:37 didg Exp $
  *
  * Copyright (c) 2003 the Netatalk Team
  * Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
@@ -57,6 +56,10 @@ extern struct _cnid_module cnid_dbd_module;
 extern struct _cnid_module cnid_tdb_module;
 #endif
 
+#ifdef CNID_BACKEND_MYSQL
+extern struct _cnid_module cnid_mysql_module;
+#endif
+
 void cnid_init(void)
 {
 #ifdef CNID_BACKEND_DB3
@@ -86,4 +89,8 @@ void cnid_init(void)
 #ifdef CNID_BACKEND_TDB
     cnid_register(&cnid_tdb_module);
 #endif
+
+#ifdef CNID_BACKEND_MYSQL
+    cnid_register(&cnid_mysql_module);
+#endif
 }
index c96a22596163659fc87be4a6b3a60f3eafc6e321..09cf9ead2ed4f4cc10cf6f4458b8e85545afb7b8 100644 (file)
@@ -31,8 +31,9 @@
 #include <atalk/logger.h>
 #include <atalk/adouble.h>
 #include <atalk/cnid.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
 #include <atalk/util.h>
+#include <atalk/volume.h>
 
 #include "cnid_dbd.h"
 
@@ -224,27 +225,49 @@ static int write_vec(int fd, struct iovec *iov, ssize_t towrite, int vecs)
 }
 
 /* --------------------- */
-static int init_tsock(CNID_private *db)
+static int init_tsock(CNID_bdb_private *db)
 {
     int fd;
-    int len;
-    struct iovec iov[2];
+    int len[DBD_NUM_OPEN_ARGS];
+    int iovecs;
+    struct iovec iov[DBD_NUM_OPEN_ARGS + 1] = {{0}};
+    struct vol *vol = db->vol;
+    ssize_t iovlen;
 
-    LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
-        db->db_dir, db->cnidserver, db->cnidport);
+    LOG(log_debug, logtype_cnid, "connecting to CNID server: %s:%s",
+        vol->v_cnidserver, vol->v_cnidport);
 
-    if ((fd = tsock_getfd(db->cnidserver, db->cnidport)) < 0)
+    if ((fd = tsock_getfd(vol->v_cnidserver, vol->v_cnidport)) < 0)
         return -1;
 
-    len = strlen(db->db_dir);
+    LOG(log_debug, logtype_cnid, "connecting volume '%s', path: %s, user: %s",
+        vol->v_configname, vol->v_path, vol->v_obj->username[0] ? vol->v_obj->username : "-");
+
+    iovecs = 1 + DBD_NUM_OPEN_ARGS - 1;
+
+    len[0] = strlen(vol->v_configname) + 1;
+    len[1] = strlen(vol->v_path) + 1;
+    len[2] = strlen(vol->v_obj->username);
+
+    iov[0].iov_base = &len[0];
+    iov[0].iov_len  = DBD_NUM_OPEN_ARGS * sizeof(int);
 
-    iov[0].iov_base = &len;
-    iov[0].iov_len  = sizeof(int);
+    iov[1].iov_base = vol->v_configname;
+    iov[1].iov_len  = len[0];
 
-    iov[1].iov_base = db->db_dir;
-    iov[1].iov_len  = len;
+    iov[2].iov_base = vol->v_path;
+    iov[2].iov_len  = len[1];
 
-    if (write_vec(fd, iov, len + sizeof(int), 2) != len + sizeof(int)) {
+    if (len[2] > 0) {
+        len[2] += 1;
+        iovecs++;
+        iov[3].iov_base = vol->v_obj->username;
+        iov[3].iov_len  = len[2];
+    }
+
+    iovlen = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len + iov[3].iov_len;
+
+    if (write_vec(fd, iov, iovlen, iovecs) != iovlen) {
         LOG(log_error, logtype_cnid, "init_tsock: Error/short write: %s", strerror(errno));
         close(fd);
         return -1;
@@ -256,7 +279,7 @@ static int init_tsock(CNID_private *db)
 }
 
 /* --------------------- */
-static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
+static int send_packet(CNID_bdb_private *db, struct cnid_dbd_rqst *rqst)
 {
     struct iovec iov[2];
     size_t towrite;
@@ -275,8 +298,8 @@ static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
     }
 
     if (write_vec(db->fd, iov, towrite, vecs) != towrite) {
-        LOG(log_warning, logtype_cnid, "send_packet: Error writev rqst (db_dir %s): %s",
-            db->db_dir, strerror(errno));
+        LOG(log_warning, logtype_cnid, "send_packet: Error writev rqst (volume %s): %s",
+            db->vol->v_localname, strerror(errno));
         return -1;
     }
 
@@ -312,7 +335,7 @@ static int dbd_reply_stamp(struct cnid_dbd_rply *rply)
  * assume send is non blocking
  * if no answer after sometime (at least MAX_DELAY secondes) return an error
  */
-static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+static int dbd_rpc(CNID_bdb_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     ssize_t ret;
     char *nametmp;
@@ -327,21 +350,21 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
     ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
 
     if (ret != sizeof(struct cnid_dbd_rply)) {
-        LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
-            db->db_dir, ret == -1 ? strerror(errno) : "closed");
+        LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (volume %s): %s",
+            db->vol->v_localname, ret == -1 ? strerror(errno) : "closed");
         rply->name = nametmp;
         return -1;
     }
     rply->name = nametmp;
     if (rply->namelen && rply->namelen > len) {
         LOG(log_error, logtype_cnid,
-            "dbd_rpc: Error reading name (db_dir %s): %s name too long: %d. only wanted %d, garbage?",
-            db->db_dir, rply->name, rply->namelen, len);
+            "dbd_rpc: Error reading name (volume %s): %s name too long: %d. only wanted %d, garbage?",
+            db->vol->v_localname, rply->name, rply->namelen, len);
         return -1;
     }
     if (rply->namelen && (ret = readt(db->fd, rply->name, rply->namelen, 0, ONE_DELAY)) != (ssize_t)rply->namelen) {
-        LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (db_dir %s): %s",
-            db->db_dir, ret == -1?strerror(errno):"closed");
+        LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (volume %s): %s",
+            db->vol->v_localname, ret == -1?strerror(errno):"closed");
         return -1;
     }
 
@@ -351,52 +374,23 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
 }
 
 /* -------------------- */
-static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+static int transmit(CNID_bdb_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     time_t orig, t;
     int clean = 1; /* no errors so far - to prevent sleep on first try */
 
-    if (db->changed) {
-        /* volume and db don't have the same timestamp
-         */
-        return -1;
-    }
     while (1) {
         if (db->fd == -1) {
-            struct cnid_dbd_rqst rqst_stamp;
-            struct cnid_dbd_rply rply_stamp;
-            char  stamp[ADEDLEN_PRIVSYN];
-
             LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
             if ((db->fd = init_tsock(db)) < 0) {
                 goto transmit_fail;
             }
-            dbd_initstamp(&rqst_stamp);
-            memset(stamp, 0, ADEDLEN_PRIVSYN);
-            rply_stamp.name = stamp;
-            rply_stamp.namelen = ADEDLEN_PRIVSYN;
-
-            if (dbd_rpc(db, &rqst_stamp, &rply_stamp) < 0)
-                goto transmit_fail;
-            if (dbd_reply_stamp(&rply_stamp ) < 0)
-                goto transmit_fail;
-
             if (db->notfirst) {
-                LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd, comparing database stamps...");
-                if (memcmp(stamp, db->stamp, ADEDLEN_PRIVSYN)) {
-                    LOG(log_error, logtype_cnid, "transmit: ... not the same db!");
-                    db->changed = 1;
-                    return -1;
-                }
-                LOG(log_debug7, logtype_cnid, "transmit: ... OK.");
+                LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd");
             } else { /* db->notfirst == 0 */
                 db->notfirst = 1;
-                if (db->client_stamp)
-                    memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
-                memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
             }
-            LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
-                db->db_dir, *(uint64_t *)stamp);
+            LOG(log_debug, logtype_cnid, "transmit: attached to '%s'", db->vol->v_localname);
         }
         if (!dbd_rpc(db, rqst, rply)) {
             LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
@@ -410,14 +404,14 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
 
         if (errno == ECONNREFUSED) { /* errno carefully injected in tsock_getfd */
             /* give up */
-            LOG(log_error, logtype_cnid, "transmit: connection refused (db_dir %s)", db->db_dir);
+            LOG(log_error, logtype_cnid, "transmit: connection refused (volume %s)", db->vol->v_localname);
             return -1;
         }
 
         if (!clean) { /* don't sleep if just got disconnected by cnid server */
             time(&t);
             if (t - orig > MAX_DELAY) {
-                LOG(log_error, logtype_cnid, "transmit: Request to dbd daemon (db_dir %s) timed out.", db->db_dir);
+                LOG(log_error, logtype_cnid, "transmit: Request to dbd daemon (volume %s) timed out.", db->vol->v_localname);
                 return -1;
             }
             /* sleep a little before retry */
@@ -431,20 +425,15 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
 }
 
 /* ---------------------- */
-static struct _cnid_db *cnid_dbd_new(const char *volpath)
+static struct _cnid_db *cnid_dbd_new(struct vol *vol)
 {
     struct _cnid_db *cdb;
 
     if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
         return NULL;
 
-    if ((cdb->volpath = strdup(volpath)) == NULL) {
-        free(cdb);
-        return NULL;
-    }
-
-    cdb->flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
-
+    cdb->cnid_db_vol = vol;
+    cdb->cnid_db_flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
     cdb->cnid_add = cnid_dbd_add;
     cdb->cnid_delete = cnid_dbd_delete;
     cdb->cnid_get = cnid_dbd_get;
@@ -456,51 +445,39 @@ static struct _cnid_db *cnid_dbd_new(const char *volpath)
     cdb->cnid_update = cnid_dbd_update;
     cdb->cnid_rebuild_add = cnid_dbd_rebuild_add;
     cdb->cnid_close = cnid_dbd_close;
-
+    cdb->cnid_wipe = cnid_dbd_wipe;
     return cdb;
 }
 
 /* ---------------------- */
 struct _cnid_db *cnid_dbd_open(struct cnid_open_args *args)
 {
-    CNID_private *db = NULL;
+    CNID_bdb_private *db = NULL;
     struct _cnid_db *cdb = NULL;
 
-    if (!args->dir) {
-        return NULL;
-    }
-
-    if ((cdb = cnid_dbd_new(args->dir)) == NULL) {
+    if ((cdb = cnid_dbd_new(args->cnid_args_vol)) == NULL) {
         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
         return NULL;
     }
 
-    if ((db = (CNID_private *)calloc(1, sizeof(CNID_private))) == NULL) {
+    if ((db = (CNID_bdb_private *)calloc(1, sizeof(CNID_bdb_private))) == NULL) {
         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
         goto cnid_dbd_open_fail;
     }
 
-    cdb->_private = db;
+    cdb->cnid_db_private = db;
 
-    /* We keep a copy of the directory in the db structure so that we can
-       transparently reconnect later. */
-    strcpy(db->db_dir, args->dir);
-    db->magic = CNID_DB_MAGIC;
     db->fd = -1;
-    db->cnidserver = strdup(args->cnidserver);
-    db->cnidport = strdup(args->cnidport);
+    db->vol = args->cnid_args_vol;
 
-    LOG(log_debug, logtype_cnid, "cnid_dbd_open: Finished initializing cnid dbd module for volume '%s'", db->db_dir);
+    LOG(log_debug, logtype_cnid, "Finished initializing CNID dbd module for volume '%s'",
+        args->cnid_args_vol->v_localname);
 
     return cdb;
 
 cnid_dbd_open_fail:
-    if (cdb != NULL) {
-        if (cdb->volpath != NULL) {
-            free(cdb->volpath);
-        }
+    if (cdb != NULL)
         free(cdb);
-    }
     if (db != NULL)
         free(db);
 
@@ -510,37 +487,62 @@ cnid_dbd_open_fail:
 /* ---------------------- */
 void cnid_dbd_close(struct _cnid_db *cdb)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
 
     if (!cdb) {
         LOG(log_error, logtype_cnid, "cnid_close called with NULL argument!");
         return;
     }
 
-    if ((db = cdb->_private) != NULL) {
-        LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'", db->db_dir);
+    if ((db = cdb->cnid_db_private) != NULL) {
+        LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'", db->vol->v_localname);
 
         if (db->fd >= 0)
             close(db->fd);
         free(db);
     }
 
-    free(cdb->volpath);
     free(cdb);
 
     return;
 }
 
+/**
+ * Get the db stamp
+ **/
+static int cnid_dbd_stamp(CNID_bdb_private *db)
+{
+    struct cnid_dbd_rqst rqst_stamp;
+    struct cnid_dbd_rply rply_stamp;
+    char  stamp[ADEDLEN_PRIVSYN];
+
+    dbd_initstamp(&rqst_stamp);
+    memset(stamp, 0, ADEDLEN_PRIVSYN);
+    rply_stamp.name = stamp;
+    rply_stamp.namelen = ADEDLEN_PRIVSYN;
+
+    if (transmit(db, &rqst_stamp, &rply_stamp) < 0)
+        return -1;
+    if (dbd_reply_stamp(&rply_stamp ) < 0)
+        return -1;
+
+    if (db->client_stamp)
+        memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
+    memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
+
+    return 0;
+}
+
 /* ---------------------- */
 cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
                     cnid_t did, const char *name, size_t len, cnid_t hint)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     cnid_t id;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
         LOG(log_error, logtype_cnid, "cnid_add: Parameter error");
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
@@ -555,7 +557,7 @@ cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
     RQST_RESET(&rqst);
     rqst.op = CNID_DBD_OP_ADD;
 
-    if (!(cdb->flags & CNID_FLAG_NODEV)) {
+    if (!(cdb->cnid_db_flags & CNID_FLAG_NODEV)) {
         rqst.dev = st->st_dev;
     }
 
@@ -566,8 +568,8 @@ cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
     rqst.name = name;
     rqst.namelen = len;
 
-    LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
-        ntohl(did), name, (long long)st->st_ino, rqst.type);
+    LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', dev: 0x%llx, inode: 0x%llx, type: %s",
+        ntohl(did), name, (long long)rqst.dev, (long long)st->st_ino, rqst.type ? "dir" : "file");
 
     rply.namelen = 0;
     if (transmit(db, &rqst, &rply) < 0) {
@@ -599,12 +601,12 @@ cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
 /* ---------------------- */
 cnid_t cnid_dbd_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     cnid_t id;
 
-    if (!cdb || !(db = cdb->_private) || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !name) {
         LOG(log_error, logtype_cnid, "cnid_dbd_get: Parameter error");
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
@@ -652,12 +654,12 @@ cnid_t cnid_dbd_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t l
 /* ---------------------- */
 char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     char *name;
 
-    if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !(*id)) {
         LOG(log_error, logtype_cnid, "cnid_resolve: Parameter error");
         errno = CNID_ERR_PARAM;
         return NULL;
@@ -706,32 +708,34 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le
     return name;
 }
 
-/* ---------------------- */
+/**
+ * Caller passes buffer where we will store the db stamp
+ **/
 int cnid_dbd_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
 
-    if (!cdb || !(db = cdb->_private) || len != ADEDLEN_PRIVSYN) {
+    if (!cdb || !(db = cdb->cnid_db_private) || len != ADEDLEN_PRIVSYN) {
         LOG(log_error, logtype_cnid, "cnid_getstamp: Parameter error");
         errno = CNID_ERR_PARAM;
         return -1;
     }
     db->client_stamp = buffer;
     db->stamp_size = len;
-    memset(buffer,0, len);
-    return 0;
+
+    return cnid_dbd_stamp(db);
 }
 
 /* ---------------------- */
 cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
                        const char *name, size_t len)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     cnid_t id;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
         LOG(log_error, logtype_cnid, "cnid_lookup: Parameter error");
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
@@ -746,7 +750,7 @@ cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     RQST_RESET(&rqst);
     rqst.op = CNID_DBD_OP_LOOKUP;
 
-    if (!(cdb->flags & CNID_FLAG_NODEV)) {
+    if (!(cdb->cnid_db_flags & CNID_FLAG_NODEV)) {
         rqst.dev = st->st_dev;
     }
 
@@ -787,12 +791,12 @@ cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
 /* ---------------------- */
 int cnid_dbd_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     int count;
 
-    if (!cdb || !(db = cdb->_private) || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !name) {
         LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
@@ -843,11 +847,11 @@ int cnid_dbd_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *
 int cnid_dbd_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
                     cnid_t did, const char *name, size_t len)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
 
-    if (!cdb || !(db = cdb->_private) || !id || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !st || !name) {
         LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
         errno = CNID_ERR_PARAM;
         return -1;
@@ -862,7 +866,7 @@ int cnid_dbd_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     RQST_RESET(&rqst);
     rqst.op = CNID_DBD_OP_UPDATE;
     rqst.cnid = id;
-    if (!(cdb->flags & CNID_FLAG_NODEV)) {
+    if (!(cdb->cnid_db_flags & CNID_FLAG_NODEV)) {
         rqst.dev = st->st_dev;
     }
     rqst.ino = st->st_ino;
@@ -897,12 +901,12 @@ int cnid_dbd_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
 cnid_t cnid_dbd_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
                             cnid_t did, const char *name, size_t len, cnid_t hint)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
     cnid_t id;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name || hint == CNID_INVALID) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name || hint == CNID_INVALID) {
         LOG(log_error, logtype_cnid, "cnid_rebuild_add: Parameter error");
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
@@ -917,7 +921,7 @@ cnid_t cnid_dbd_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
     RQST_RESET(&rqst);
     rqst.op = CNID_DBD_OP_REBUILD_ADD;
 
-    if (!(cdb->flags & CNID_FLAG_NODEV)) {
+    if (!(cdb->cnid_db_flags & CNID_FLAG_NODEV)) {
         rqst.dev = st->st_dev;
     }
 
@@ -959,11 +963,11 @@ cnid_t cnid_dbd_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
 /* ---------------------- */
 int cnid_dbd_delete(struct _cnid_db *cdb, const cnid_t id)
 {
-    CNID_private *db;
+    CNID_bdb_private *db;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
 
-    if (!cdb || !(db = cdb->_private) || !id) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id) {
         LOG(log_error, logtype_cnid, "cnid_delete: Parameter error");
         errno = CNID_ERR_PARAM;
         return -1;
@@ -994,6 +998,39 @@ int cnid_dbd_delete(struct _cnid_db *cdb, const cnid_t id)
     }
 }
 
+int cnid_dbd_wipe(struct _cnid_db *cdb)
+{
+    CNID_bdb_private *db;
+    struct cnid_dbd_rqst rqst;
+    struct cnid_dbd_rply rply;
+
+    if (!cdb || !(db = cdb->cnid_db_private)) {
+        LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
+        errno = CNID_ERR_PARAM;
+        return -1;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
+
+    RQST_RESET(&rqst);
+    rqst.op = CNID_DBD_OP_WIPE;
+    rqst.cnid = 0;
+
+    rply.namelen = 0;
+    if (transmit(db, &rqst, &rply) < 0) {
+        errno = CNID_ERR_DB;
+        return -1;
+    }
+
+    if (rply.result != CNID_DBD_RES_OK) {
+        errno = CNID_ERR_DB;
+        return -1;
+    }
+    LOG(log_debug, logtype_cnid, "cnid_dbd_wipe: ok");
+
+    return cnid_dbd_stamp(db);
+}
+
 
 struct _cnid_module cnid_dbd_module = {
     "dbd",
index fdba4c042a62591e3e771ed449ad7de6588986ca..69ff5a64fb252a0e8424a76ebb9e688eded6174b 100644 (file)
@@ -32,7 +32,7 @@ extern int    cnid_dbd_update     (struct _cnid_db *, cnid_t, const struct stat
 extern int    cnid_dbd_delete     (struct _cnid_db *, const cnid_t);
 extern cnid_t cnid_dbd_rebuild_add(struct _cnid_db *, const struct stat *,
                                    cnid_t, const char *, size_t, cnid_t);
-
+extern int    cnid_dbd_wipe       (struct _cnid_db *cdb);
 /* FIXME: These functions could be static in cnid_dbd.c */
 
 #endif /* include/atalk/cnid_dbd.h */
index 5903d03fe449da281238818e9501c57e812cf9f1..2314217cd50b1eb4c3109d98f3ccab85bfc1bfe4 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: cnid_last.c,v 1.5 2010-03-31 09:47:32 franklahm Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
@@ -59,10 +58,10 @@ cnid_t cnid_last_add(struct _cnid_db *cdb, const struct stat *st,
 
     struct _cnid_last_private *priv;
 
-    if (!cdb || !(cdb->_private))
+    if (!cdb || !(cdb->cnid_db_private))
         return CNID_INVALID;
 
-    priv = (struct _cnid_last_private *) (cdb->_private);
+    priv = (struct _cnid_last_private *) (cdb->cnid_db_private);
 
     if (S_ISDIR(st->st_mode))
         return htonl(priv->last_did++);
@@ -74,8 +73,7 @@ cnid_t cnid_last_add(struct _cnid_db *cdb, const struct stat *st,
 
 void cnid_last_close(struct _cnid_db *cdb)
 {
-    free(cdb->volpath);
-    free(cdb->_private);
+    free(cdb->cnid_db_private);
     free(cdb);
 }
 
@@ -106,7 +104,7 @@ cnid_t cnid_last_lookup(struct _cnid_db *cdb _U_, const struct stat *st _U_, cni
 }
 
 
-static struct _cnid_db *cnid_last_new(const char *volpath)
+static struct _cnid_db *cnid_last_new(struct vol *vol)
 {
     struct _cnid_db *cdb;
     struct _cnid_last_private *priv;
@@ -114,23 +112,17 @@ static struct _cnid_db *cnid_last_new(const char *volpath)
     if ((cdb = (struct _cnid_db *) calloc(1, sizeof(struct _cnid_db))) == NULL)
         return NULL;
 
-    if ((cdb->volpath = strdup(volpath)) == NULL) {
-        free(cdb);
-        return NULL;
-    }
-
-    if ((cdb->_private = calloc(1, sizeof(struct _cnid_last_private))) == NULL) {
-        free(cdb->volpath);
+    if ((cdb->cnid_db_private = calloc(1, sizeof(struct _cnid_last_private))) == NULL) {
         free(cdb);
         return NULL;
     }
 
     /* Set up private state */
-    priv = (struct _cnid_last_private *) (cdb->_private);
+    priv = (struct _cnid_last_private *) (cdb->cnid_db_private);
     priv->last_did = 17;
 
     /* Set up standard fields */
-    cdb->flags = 0;
+    cdb->cnid_db_flags = 0;
     cdb->cnid_add = cnid_last_add;
     cdb->cnid_delete = cnid_last_delete;
     cdb->cnid_get = cnid_last_get;
@@ -139,7 +131,8 @@ static struct _cnid_db *cnid_last_new(const char *volpath)
     cdb->cnid_resolve = cnid_last_resolve;
     cdb->cnid_update = cnid_last_update;
     cdb->cnid_close = cnid_last_close;
-    
+    cdb->cnid_wipe = NULL;
+
     return cdb;
 }
 
@@ -147,11 +140,7 @@ struct _cnid_db *cnid_last_open(struct cnid_open_args *args)
 {
     struct _cnid_db *cdb;
 
-    if (!args->dir) {
-        return NULL;
-    }
-
-    if ((cdb = cnid_last_new(args->dir)) == NULL) {
+    if ((cdb = cnid_last_new(args->cnid_args_vol)) == NULL) {
         LOG(log_error, logtype_default, "cnid_open: Unable to allocate memory for database");
         return NULL;
     }
diff --git a/libatalk/cnid/mysql/Makefile.am b/libatalk/cnid/mysql/Makefile.am
new file mode 100644 (file)
index 0000000..df688b2
--- /dev/null
@@ -0,0 +1,6 @@
+# Makefile.am for libatalk/cnid/mysql/
+
+noinst_LTLIBRARIES = libcnid_mysql.la
+libcnid_mysql_la_SOURCES = cnid_mysql.c
+libcnid_mysql_la_CFLAGS = @MYSQL_CFLAGS@
+libcnid_mysql_la_LIBADD = @MYSQL_LIBS@
diff --git a/libatalk/cnid/mysql/cnid_mysql.c b/libatalk/cnid/mysql/cnid_mysql.c
new file mode 100644 (file)
index 0000000..aa04bf8
--- /dev/null
@@ -0,0 +1,953 @@
+/*
+ * Copyright (C) Ralph Boehme 2013
+ * All Rights Reserved.  See COPYING.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#undef _FORTIFY_SOURCE
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <errmsg.h>
+
+#include <atalk/logger.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/cnid_mysql_private.h>
+#include <atalk/cnid_bdb_private.h>
+#include <atalk/errchk.h>
+#include <atalk/globals.h>
+#include <atalk/volume.h>
+
+
+static MYSQL_BIND lookup_param[4], lookup_result[5];
+static MYSQL_BIND add_param[4], put_param[5];
+
+/*
+ * Prepared statement parameters
+ */
+static char               stmt_param_name[MAXPATHLEN];
+static unsigned long      stmt_param_name_len;
+static unsigned long long stmt_param_id;
+static unsigned long long stmt_param_did;
+static unsigned long long stmt_param_dev;
+static unsigned long long stmt_param_ino;
+
+/*
+ * lookup result parameters
+ */
+static unsigned long long lookup_result_id;
+static unsigned long long lookup_result_did;
+static char               lookup_result_name[MAXPATHLEN];
+static unsigned long      lookup_result_name_len;
+static unsigned long long lookup_result_dev;
+static unsigned long long lookup_result_ino;
+
+static int init_prepared_stmt_lookup(CNID_mysql_private *db)
+{
+    EC_INIT;
+    char *sql = NULL;
+
+    lookup_param[0].buffer_type    = MYSQL_TYPE_STRING;
+    lookup_param[0].buffer         = &stmt_param_name;
+    lookup_param[0].buffer_length  = sizeof(stmt_param_name);
+    lookup_param[0].length         = &stmt_param_name_len;
+
+    lookup_param[1].buffer_type    = MYSQL_TYPE_LONGLONG;
+    lookup_param[1].buffer         = &stmt_param_did;
+    lookup_param[1].is_unsigned    = true;
+
+    lookup_param[2].buffer_type    = MYSQL_TYPE_LONGLONG;
+    lookup_param[2].buffer         = &stmt_param_dev;
+    lookup_param[2].is_unsigned    = true;
+
+    lookup_param[3].buffer_type    = MYSQL_TYPE_LONGLONG;
+    lookup_param[3].buffer         = &stmt_param_ino;
+    lookup_param[3].is_unsigned    = true;
+
+    lookup_result[0].buffer_type   = MYSQL_TYPE_LONGLONG;
+    lookup_result[0].buffer        = &lookup_result_id;
+    lookup_result[0].is_unsigned   = true;
+
+    lookup_result[1].buffer_type   = MYSQL_TYPE_LONGLONG;
+    lookup_result[1].buffer        = &lookup_result_did;
+    lookup_result[1].is_unsigned   = true;
+
+    lookup_result[2].buffer_type   = MYSQL_TYPE_STRING;
+    lookup_result[2].buffer        = &lookup_result_name;
+    lookup_result[2].buffer_length = sizeof(lookup_result_name);
+    lookup_result[2].length        = &lookup_result_name_len;
+
+    lookup_result[3].buffer_type   = MYSQL_TYPE_LONGLONG;
+    lookup_result[3].buffer        = &lookup_result_dev;
+    lookup_result[3].is_unsigned   = true;
+
+    lookup_result[4].buffer_type   = MYSQL_TYPE_LONGLONG;
+    lookup_result[4].buffer        = &lookup_result_ino;
+    lookup_result[4].is_unsigned   = true;
+
+    EC_NULL( db->cnid_lookup_stmt = mysql_stmt_init(db->cnid_mysql_con) );
+    EC_NEG1( asprintf(&sql,
+                      "SELECT Id,Did,Name,DevNo,InodeNo FROM %s "
+                      "WHERE (Name=? AND Did=?) OR (DevNo=? AND InodeNo=?)",
+                      db->cnid_mysql_voluuid_str) );
+    EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_lookup_stmt, sql, strlen(sql)) );
+    EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_lookup_stmt, lookup_param) );
+
+EC_CLEANUP:
+    if (sql)
+        free(sql);
+    EC_EXIT;
+}
+
+static int init_prepared_stmt_add(CNID_mysql_private *db)
+{
+    EC_INIT;
+    char *sql = NULL;
+
+    EC_NULL( db->cnid_add_stmt = mysql_stmt_init(db->cnid_mysql_con) );
+    EC_NEG1( asprintf(&sql,
+                      "INSERT INTO %s (Name,Did,DevNo,InodeNo) VALUES(?,?,?,?)",
+                      db->cnid_mysql_voluuid_str) );
+
+    add_param[0].buffer_type    = MYSQL_TYPE_STRING;
+    add_param[0].buffer         = &stmt_param_name;
+    add_param[0].buffer_length  = sizeof(stmt_param_name);
+    add_param[0].length         = &stmt_param_name_len;
+
+    add_param[1].buffer_type    = MYSQL_TYPE_LONGLONG;
+    add_param[1].buffer         = &stmt_param_did;
+    add_param[1].is_unsigned    = true;
+
+    add_param[2].buffer_type    = MYSQL_TYPE_LONGLONG;
+    add_param[2].buffer         = &stmt_param_dev;
+    add_param[2].is_unsigned    = true;
+
+    add_param[3].buffer_type    = MYSQL_TYPE_LONGLONG;
+    add_param[3].buffer         = &stmt_param_ino;
+    add_param[3].is_unsigned    = true;
+
+    EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_add_stmt, sql, strlen(sql)) );
+    EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_add_stmt, add_param) );
+
+EC_CLEANUP:
+    if (sql)
+        free(sql);
+    EC_EXIT;
+}
+
+static int init_prepared_stmt_put(CNID_mysql_private *db)
+{
+    EC_INIT;
+    char *sql = NULL;
+
+    EC_NULL( db->cnid_put_stmt = mysql_stmt_init(db->cnid_mysql_con) );
+    EC_NEG1( asprintf(&sql,
+                      "INSERT INTO %s (Id,Name,Did,DevNo,InodeNo) VALUES(?,?,?,?,?)",
+                      db->cnid_mysql_voluuid_str) );
+
+    put_param[0].buffer_type    = MYSQL_TYPE_LONGLONG;
+    put_param[0].buffer         = &stmt_param_id;
+    put_param[0].is_unsigned    = true;
+
+    put_param[1].buffer_type    = MYSQL_TYPE_STRING;
+    put_param[1].buffer         = &stmt_param_name;
+    put_param[1].buffer_length  = sizeof(stmt_param_name);
+    put_param[1].length         = &stmt_param_name_len;
+
+    put_param[2].buffer_type    = MYSQL_TYPE_LONGLONG;
+    put_param[2].buffer         = &stmt_param_did;
+    put_param[2].is_unsigned    = true;
+
+    put_param[3].buffer_type    = MYSQL_TYPE_LONGLONG;
+    put_param[3].buffer         = &stmt_param_dev;
+    put_param[3].is_unsigned    = true;
+
+    put_param[4].buffer_type    = MYSQL_TYPE_LONGLONG;
+    put_param[4].buffer         = &stmt_param_ino;
+    put_param[4].is_unsigned    = true;
+
+    EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_put_stmt, sql, strlen(sql)) );
+    EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_put_stmt, put_param) );
+
+EC_CLEANUP:
+    if (sql)
+        free(sql);
+    EC_EXIT;
+}
+
+static int init_prepared_stmt(CNID_mysql_private *db)
+{
+    EC_INIT;
+
+    EC_ZERO( init_prepared_stmt_lookup(db) );
+    EC_ZERO( init_prepared_stmt_add(db) );
+    EC_ZERO( init_prepared_stmt_put(db) );
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static void close_prepared_stmt(CNID_mysql_private *db)
+{
+    mysql_stmt_close(db->cnid_lookup_stmt);
+    mysql_stmt_close(db->cnid_add_stmt);
+    mysql_stmt_close(db->cnid_put_stmt);
+}
+
+static int cnid_mysql_execute(MYSQL *con, char *fmt, ...)
+{
+    char *sql = NULL;
+    va_list ap;
+    int rv;
+
+    va_start(ap, fmt);
+    if (vasprintf(&sql, fmt, ap) == -1)
+        return -1;
+    va_end(ap);
+
+    LOG(log_maxdebug, logtype_cnid, "SQL: %s", sql);
+
+    rv = mysql_query(con, sql);
+
+    if (rv) {
+        LOG(log_info, logtype_cnid, "MySQL query \"%s\", error: %s", sql, mysql_error(con));
+        errno = CNID_ERR_DB;
+    }
+    free(sql);
+    return rv;
+}
+
+int cnid_mysql_delete(struct _cnid_db *cdb, const cnid_t id)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+
+    if (!cdb || !(db = cdb->cnid_db_private) || !id) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_delete: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): BEGIN", ntohl(id));
+    
+    EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                "DELETE FROM %s WHERE Id=%" PRIu32,
+                                db->cnid_mysql_voluuid_str,
+                                ntohl(id)) );
+
+    LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): END", ntohl(id));
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+void cnid_mysql_close(struct _cnid_db *cdb)
+{
+    CNID_mysql_private *db;
+
+    if (!cdb) {
+        LOG(log_error, logtype_cnid, "cnid_close called with NULL argument !");
+        return;
+    }
+
+    if ((db = cdb->cnid_db_private) != NULL) {
+        LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'",
+            cdb->cnid_db_vol->v_localname);
+
+        free(db->cnid_mysql_voluuid_str);
+
+        close_prepared_stmt(db);
+
+        if (db->cnid_mysql_con)
+            mysql_close(db->cnid_mysql_con);
+        free(db);
+    }
+
+    free(cdb);
+
+    return;
+}
+
+int cnid_mysql_update(struct _cnid_db *cdb,
+                      cnid_t id,
+                      const struct stat *st,
+                      cnid_t did,
+                      const char *name,
+                      size_t len)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    cnid_t update_id;
+
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !st || !name) {
+        LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    if (len > MAXPATHLEN) {
+        LOG(log_error, logtype_cnid, "cnid_update: Path name is too long");
+        errno = CNID_ERR_PATH;
+        EC_FAIL;
+    }
+
+    uint64_t dev = st->st_dev;
+    uint64_t ino = st->st_ino;
+
+    do {
+        EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                    "DELETE FROM %s WHERE Id=%" PRIu32,
+                                    db->cnid_mysql_voluuid_str,
+                                    ntohl(id)) );
+        EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                    "DELETE FROM %s WHERE Did=%" PRIu32 " AND Name='%s'",
+                                    db->cnid_mysql_voluuid_str, ntohl(did), name) );
+        EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                    "DELETE FROM %s WHERE DevNo=%" PRIu64 " AND InodeNo=%" PRIu64,
+                                    db->cnid_mysql_voluuid_str, dev, ino) );
+
+        stmt_param_id = ntohl(id);
+        strncpy(stmt_param_name, name, sizeof(stmt_param_name));
+        stmt_param_name_len = len;
+        stmt_param_did = ntohl(did);
+        stmt_param_dev = dev;
+        stmt_param_ino = ino;
+
+        if (mysql_stmt_execute(db->cnid_put_stmt)) {
+            switch (mysql_stmt_errno(db->cnid_put_stmt)) {
+            case ER_DUP_ENTRY:
+                /*
+                 * Race condition:
+                 * between deletion and insert another process may have inserted
+                 * this entry.
+                 */
+                continue;
+            case CR_SERVER_LOST:
+                close_prepared_stmt(db);
+                EC_ZERO( init_prepared_stmt(db) );
+                continue;
+            default:
+                EC_FAIL;
+            }
+        }
+        update_id = mysql_stmt_insert_id(db->cnid_put_stmt);
+    } while (update_id != ntohl(id));
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+cnid_t cnid_mysql_lookup(struct _cnid_db *cdb,
+                         const struct stat *st,
+                         cnid_t did,
+                         const char *name,
+                         size_t len)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    cnid_t id = CNID_INVALID;
+    bool have_result = false;
+
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    if (len > MAXPATHLEN) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Path name is too long");
+        errno = CNID_ERR_PATH;
+        EC_FAIL;
+    }
+
+    uint64_t dev = st->st_dev;
+    uint64_t ino = st->st_ino;
+    cnid_t hint = db->cnid_mysql_hint;
+
+    LOG(log_maxdebug, logtype_cnid, "cnid_mysql_lookup(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
+        ntohl(did), name, ntohl(hint));
+
+    strncpy(stmt_param_name, name, sizeof(stmt_param_name));
+    stmt_param_name_len = len;
+    stmt_param_did = ntohl(did);
+    stmt_param_dev = dev;
+    stmt_param_ino = ino;
+
+exec_stmt:
+    if (mysql_stmt_execute(db->cnid_lookup_stmt)) {
+        switch (mysql_stmt_errno(db->cnid_lookup_stmt)) {
+        case CR_SERVER_LOST:
+            close_prepared_stmt(db);
+            EC_ZERO( init_prepared_stmt(db) );
+            goto exec_stmt;
+        default:
+            EC_FAIL;
+        }
+    }
+    EC_ZERO_LOG( mysql_stmt_store_result(db->cnid_lookup_stmt) );
+    have_result = true;
+    EC_ZERO_LOG( mysql_stmt_bind_result(db->cnid_lookup_stmt, lookup_result) );
+
+    uint64_t retdev, retino;
+    cnid_t retid, retdid;
+    char *retname;
+
+    switch (mysql_stmt_num_rows(db->cnid_lookup_stmt)) {
+
+    case 0:
+        /* not found */
+        LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: name: '%s', did: %u is not in the CNID database", 
+            name, ntohl(did));
+        errno = CNID_DBD_RES_NOTFOUND;
+        EC_FAIL;
+
+    case 1:
+        /* either both OR clauses matched the same id or only one matched, handled below */
+        EC_ZERO( mysql_stmt_fetch(db->cnid_lookup_stmt) );
+        break;
+
+    case 2:
+        /* a mismatch, delete both and return not found */
+        while (mysql_stmt_fetch(db->cnid_lookup_stmt) == 0) {
+            if (cnid_mysql_delete(cdb, htonl((cnid_t)lookup_result_id))) {
+                LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+                errno = CNID_ERR_DB;
+                EC_FAIL;
+            }
+        }
+        errno = CNID_DBD_RES_NOTFOUND;
+        EC_FAIL;
+
+    default:
+        errno = CNID_ERR_DB;
+        EC_FAIL;
+    }
+
+    retid = htonl(lookup_result_id);
+    retdid = htonl(lookup_result_did);
+    retname = lookup_result_name;
+    retdev = lookup_result_dev;
+    retino = lookup_result_ino;
+
+    if (retdid != did || STRCMP(retname, !=, name)) {
+        LOG(log_debug, logtype_cnid,
+            "cnid_mysql_lookup(CNID hint: %" PRIu32 ", DID: %" PRIu32 ", name: \"%s\"): server side mv oder reused inode",
+            ntohl(hint), ntohl(did), name);
+        if (hint != retid) {
+            if (cnid_mysql_delete(cdb, retid) != 0) {
+                LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+                errno = CNID_ERR_DB;
+                EC_FAIL;
+            }
+            errno = CNID_DBD_RES_NOTFOUND;
+            EC_FAIL;
+        }
+        LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: server side mv, got hint, updating");
+        if (cnid_mysql_update(cdb, retid, st, did, name, len) != 0) {
+            LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+            errno = CNID_ERR_DB;
+            EC_FAIL;
+        }
+        id = retid;
+    } else if (retdev != dev || retino != ino) {
+        LOG(log_debug, logtype_cnid, "cnid_mysql_lookup(DID:%u, name: \"%s\"): changed dev/ino",
+            ntohl(did), name);
+        if (cnid_mysql_delete(cdb, retid) != 0) {
+            LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+            errno = CNID_ERR_DB;
+            EC_FAIL;
+        }
+        errno = CNID_DBD_RES_NOTFOUND;
+        EC_FAIL;
+    } else {
+        /* everythings good */
+        id = retid;
+    }
+
+EC_CLEANUP:
+    LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: id: %" PRIu32, ntohl(id));
+    if (have_result)
+        mysql_stmt_free_result(db->cnid_lookup_stmt);
+    if (ret != 0)
+        id = CNID_INVALID;
+    return id;
+}
+
+cnid_t cnid_mysql_add(struct _cnid_db *cdb,
+                      const struct stat *st,
+                      cnid_t did,
+                      const char *name,
+                      size_t len,
+                      cnid_t hint)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    cnid_t id = CNID_INVALID;
+    MYSQL_RES *result = NULL;
+    MYSQL_STMT *stmt;
+    my_ulonglong lastid;
+
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_add: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    if (len > MAXPATHLEN) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_add: Path name is too long");
+        errno = CNID_ERR_PATH;
+        EC_FAIL;
+    }
+
+    uint64_t dev = st->st_dev;
+    uint64_t ino = st->st_ino;
+    db->cnid_mysql_hint = hint;
+
+    LOG(log_maxdebug, logtype_cnid, "cnid_mysql_add(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
+        ntohl(did), name, ntohl(hint));
+
+    do {
+        if ((id = cnid_mysql_lookup(cdb, st, did, name, len)) == CNID_INVALID) {
+            if (errno == CNID_ERR_DB)
+                EC_FAIL;
+            /*
+             * If the CNID set overflowed before (CNID_MYSQL_FLAG_DEPLETED)
+             * ignore the CNID "hint" taken from the AppleDouble file
+             */
+            if (!db->cnid_mysql_hint || (db->cnid_mysql_flags & CNID_MYSQL_FLAG_DEPLETED)) {
+                stmt = db->cnid_add_stmt;
+            } else {
+                stmt = db->cnid_put_stmt;
+                stmt_param_id = ntohl(db->cnid_mysql_hint);
+            }
+            strncpy(stmt_param_name, name, sizeof(stmt_param_name));
+            stmt_param_name_len = len;
+            stmt_param_did = ntohl(did);
+            stmt_param_dev = dev;
+            stmt_param_ino = ino;
+
+            if (mysql_stmt_execute(stmt)) {
+                switch (mysql_stmt_errno(stmt)) {
+                case ER_DUP_ENTRY:
+                    break;
+                case CR_SERVER_LOST:
+                    close_prepared_stmt(db);
+                    EC_ZERO( init_prepared_stmt(db) );
+                    continue;
+                default:
+                    EC_FAIL;
+                }
+                /*
+                 * Race condition:
+                 * between lookup and insert another process may have inserted
+                 * this entry.
+                 */
+                if (db->cnid_mysql_hint)
+                    db->cnid_mysql_hint = CNID_INVALID;
+                continue;
+            }
+
+            lastid = mysql_stmt_insert_id(stmt);
+
+            if (lastid > 0xffffffff) {
+                /* CNID set ist depleted, restart from scratch */
+                EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                            "START TRANSACTION;"
+                                            "UPDATE volumes SET Depleted=1 WHERE VolUUID='%s';"
+                                            "TRUNCATE TABLE %s;"
+                                            "ALTER TABLE %s AUTO_INCREMENT = 17;" 
+                                            "COMMIT;",
+                                            db->cnid_mysql_voluuid_str,
+                                            db->cnid_mysql_voluuid_str,
+                                            db->cnid_mysql_voluuid_str) );
+                db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
+                hint = CNID_INVALID;
+                do {
+                    result = mysql_store_result(db->cnid_mysql_con);
+                    if (result)
+                        mysql_free_result(result);
+                } while (mysql_next_result(db->cnid_mysql_con) == 0);
+                continue;
+            }
+
+            /* Finally assign our result */
+            id = htonl((uint32_t)lastid);
+        }
+    } while (id == CNID_INVALID);
+
+EC_CLEANUP:
+    LOG(log_debug, logtype_cnid, "cnid_mysql_add: id: %" PRIu32, ntohl(id));
+
+    if (result)
+        mysql_free_result(result);
+    return id;
+}
+
+cnid_t cnid_mysql_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    cnid_t id = CNID_INVALID;
+    MYSQL_RES *result = NULL;
+    MYSQL_ROW row;
+
+    if (!cdb || !(db = cdb->cnid_db_private) || !name) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    if (len > MAXPATHLEN) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_get: name is too long");
+        errno = CNID_ERR_PATH;
+        return CNID_INVALID;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_mysql_get(did: %" PRIu32 ", name: \"%s\"): START",
+        ntohl(did),name);
+
+    EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
+                                "SELECT Id FROM %s "
+                                "WHERE Name='%s' AND Did=%" PRIu32,
+                                db->cnid_mysql_voluuid_str,
+                                name,
+                                ntohl(did)) );
+
+    if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        errno = CNID_ERR_DB;
+        EC_FAIL;
+    }
+
+    if (mysql_num_rows(result)) {
+        row = mysql_fetch_row(result);
+        id = htonl(atoi(row[0]));
+    }
+
+EC_CLEANUP:
+    LOG(log_debug, logtype_cnid, "cnid_mysql_get: id: %" PRIu32, ntohl(id));
+
+    if (result)
+        mysql_free_result(result);
+
+    return id;
+}
+
+char *cnid_mysql_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    MYSQL_RES *result = NULL;
+    MYSQL_ROW row;
+
+    if (!cdb || !(db = cdb->cnid_db_private)) {
+        LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
+        errno = CNID_ERR_PARAM;
+        EC_FAIL;
+    }
+
+    EC_NEG1( cnid_mysql_execute(
+                 db->cnid_mysql_con,
+                 "SELECT Did,Name FROM %s WHERE Id=%" PRIu32,
+                 db->cnid_mysql_voluuid_str, ntohl(*id)) );
+
+    EC_NULL( result = mysql_store_result(db->cnid_mysql_con) );
+
+    if (mysql_num_rows(result) != 1)
+        EC_FAIL;
+
+    row = mysql_fetch_row(result);
+
+    *id = htonl(atoi(row[0]));
+    strncpy(buffer, row[1], len);
+
+EC_CLEANUP:             
+    if (result)
+        mysql_free_result(result);
+
+    if (ret != 0) {
+        *id = CNID_INVALID;
+        return NULL;
+    }
+    return buffer;
+}
+
+/**
+ * Caller passes buffer where we will store the db stamp
+ **/
+int cnid_mysql_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    MYSQL_RES *result = NULL;
+    MYSQL_ROW row;
+
+    if (!cdb || !(db = cdb->cnid_db_private)) {
+        LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
+        errno = CNID_ERR_PARAM;
+        return CNID_INVALID;
+    }
+
+    if (!buffer)
+        EC_EXIT_STATUS(0);
+
+    if (cnid_mysql_execute(db->cnid_mysql_con,
+                           "SELECT Stamp FROM volumes WHERE VolPath='%s'",
+                           cdb->cnid_db_vol->v_path)) {
+        if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
+            LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+            EC_FAIL;
+        }
+    }
+
+    if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        errno = CNID_ERR_DB;
+        EC_FAIL;
+    }
+    if (!mysql_num_rows(result)) {
+        LOG(log_error, logtype_cnid, "Can't get DB stamp for volumes \"%s\"", cdb->cnid_db_vol->v_path);
+        EC_FAIL;
+    }
+    row = mysql_fetch_row(result);
+    memcpy(buffer, row[0], len);
+
+EC_CLEANUP:
+    if (result)
+        mysql_free_result(result);
+    EC_EXIT;
+}
+
+
+int cnid_mysql_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
+{
+    LOG(log_error, logtype_cnid,
+        "cnid_mysql_find(\"%s\"): not supported with MySQL CNID backend", name);
+    return -1;
+}
+
+cnid_t cnid_mysql_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
+                              cnid_t did, const char *name, size_t len, cnid_t hint)
+{
+    LOG(log_error, logtype_cnid,
+        "cnid_mysql_rebuild_add(\"%s\"): not supported with MySQL CNID backend", name);
+    return CNID_INVALID;
+}
+
+int cnid_mysql_wipe(struct _cnid_db *cdb)
+{
+    EC_INIT;
+    CNID_mysql_private *db;
+    MYSQL_RES *result = NULL;
+
+    if (!cdb || !(db = cdb->cnid_db_private)) {
+        LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
+        errno = CNID_ERR_PARAM;
+        return -1;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
+
+    EC_NEG1( cnid_mysql_execute(
+                 db->cnid_mysql_con,
+                 "START TRANSACTION;"
+                 "UPDATE volumes SET Depleted=0 WHERE VolUUID='%s';"
+                 "TRUNCATE TABLE %s;"
+                 "ALTER TABLE %s AUTO_INCREMENT = 17;"
+                 "COMMIT;",
+                 db->cnid_mysql_voluuid_str,
+                 db->cnid_mysql_voluuid_str,
+                 db->cnid_mysql_voluuid_str) );
+
+    do {
+        result = mysql_store_result(db->cnid_mysql_con);
+        if (result)
+            mysql_free_result(result);
+    } while (mysql_next_result(db->cnid_mysql_con) == 0);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+static struct _cnid_db *cnid_mysql_new(struct vol *vol)
+{
+    struct _cnid_db *cdb;
+
+    if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
+        return NULL;
+
+    cdb->cnid_db_vol = vol;
+    cdb->cnid_db_flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
+    cdb->cnid_add = cnid_mysql_add;
+    cdb->cnid_delete = cnid_mysql_delete;
+    cdb->cnid_get = cnid_mysql_get;
+    cdb->cnid_lookup = cnid_mysql_lookup;
+    cdb->cnid_find = cnid_mysql_find;
+    cdb->cnid_nextid = NULL;
+    cdb->cnid_resolve = cnid_mysql_resolve;
+    cdb->cnid_getstamp = cnid_mysql_getstamp;
+    cdb->cnid_update = cnid_mysql_update;
+    cdb->cnid_rebuild_add = cnid_mysql_rebuild_add;
+    cdb->cnid_close = cnid_mysql_close;
+    cdb->cnid_wipe = cnid_mysql_wipe;
+    return cdb;
+}
+
+static const char *printuuid(const unsigned char *uuid) {
+    static char uuidstring[64];
+    int i = 0;
+    unsigned char c;
+
+    while ((c = *uuid)) {
+        uuid++;
+        sprintf(uuidstring + i, "%02X", c);
+        i += 2;
+    }
+    uuidstring[i] = 0;
+    return uuidstring;
+}
+
+/* ---------------------- */
+struct _cnid_db *cnid_mysql_open(struct cnid_open_args *args)
+{
+    EC_INIT;
+    CNID_mysql_private *db = NULL;
+    struct _cnid_db *cdb = NULL;
+    MYSQL_RES *result = NULL;
+    MYSQL_ROW row;
+    struct vol *vol = args->cnid_args_vol;
+
+    EC_NULL( cdb = cnid_mysql_new(vol) );
+    EC_NULL( db = (CNID_mysql_private *)calloc(1, sizeof(CNID_mysql_private)) );
+    cdb->cnid_db_private = db;
+
+    db->cnid_mysql_voluuid_str = strdup(printuuid(vol->v_uuid));
+
+    /* Initialize and connect to MySQL server */
+    EC_NULL( db->cnid_mysql_con = mysql_init(NULL) );
+    my_bool my_recon = true;
+    EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_RECONNECT, &my_recon) );
+    int my_timeout = 600;
+    EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_CONNECT_TIMEOUT, &my_timeout) );
+
+    const AFPObj *obj = vol->v_obj;
+
+    EC_NULL( mysql_real_connect(db->cnid_mysql_con,
+                                obj->options.cnid_mysql_host,
+                                obj->options.cnid_mysql_user,
+                                obj->options.cnid_mysql_pw,
+                                obj->options.cnid_mysql_db,
+                                0, NULL, CLIENT_MULTI_STATEMENTS));
+
+    /* Add volume to volume table */
+    if (cnid_mysql_execute(db->cnid_mysql_con,
+                           "CREATE TABLE IF NOT EXISTS volumes"
+                           "(VolUUID CHAR(32) PRIMARY KEY,VolPath TEXT(4096),Stamp BINARY(8),Depleted INT, INDEX(VolPath(64)))")) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        EC_FAIL;
+    }
+    time_t now = time(NULL);
+    char stamp[8];
+    memset(stamp, 0, 8);
+    memcpy(stamp, &now, sizeof(time_t));
+    char blob[16+1];
+    mysql_real_escape_string(db->cnid_mysql_con, blob, stamp, 8);
+
+    if (cnid_mysql_execute(db->cnid_mysql_con,
+                           "INSERT INTO volumes (VolUUID,Volpath,Stamp,Depleted) "
+                           "VALUES('%s','%s','%s',0)",
+                           db->cnid_mysql_voluuid_str,
+                           vol->v_path,
+                           blob)) {
+        if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
+            LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+            EC_FAIL;
+        }
+    }
+
+    /*
+     * Check whether CNID set overflowed before.
+     * If that's the case, in cnid_mysql_add() we'll ignore the CNID "hint" taken from the
+     * AppleDouble file.
+     */
+    if (cnid_mysql_execute(db->cnid_mysql_con,
+                           "SELECT Depleted FROM volumes WHERE VolUUID='%s'",
+                           db->cnid_mysql_voluuid_str)) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        EC_FAIL;
+    }
+    if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        errno = CNID_ERR_DB;
+        EC_FAIL;
+    }
+    if (mysql_num_rows(result)) {
+        row = mysql_fetch_row(result);
+        int depleted = atoi(row[0]);
+        if (depleted)
+            db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
+    }
+    mysql_free_result(result);
+    result = NULL;
+
+    /* Create volume table */
+    if (cnid_mysql_execute(db->cnid_mysql_con,
+                           "CREATE TABLE IF NOT EXISTS %s"
+                           "(Id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,"
+                           "Name VARCHAR(255) NOT NULL,"
+                           "Did INT UNSIGNED NOT NULL,"
+                           "DevNo BIGINT UNSIGNED NOT NULL,"
+                           "InodeNo BIGINT UNSIGNED NOT NULL,"
+                           "UNIQUE DidName(Did, Name), UNIQUE DevIno(DevNo, InodeNo)) "
+                           "AUTO_INCREMENT=17",
+                           db->cnid_mysql_voluuid_str)) {
+        LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
+        EC_FAIL;
+    }
+
+    EC_ZERO( init_prepared_stmt(db) );
+
+    LOG(log_debug, logtype_cnid, "Finished initializing MySQL CNID module for volume '%s'",
+        vol->v_path);
+
+EC_CLEANUP:
+    if (result)
+        mysql_free_result(result);
+    if (ret != 0) {
+        if (cdb)
+            free(cdb);
+        cdb = NULL;
+        if (db)
+            free(db);
+    }
+    return cdb;
+}
+
+struct _cnid_module cnid_mysql_module = {
+    "mysql",
+    {NULL, NULL},
+    cnid_mysql_open,
+    0
+};
index 58a4f7225215aa33208a19eeeeabd2d2b9172b72..93c289aa5faa53a633f09916f1d4807f592bd42c 100644 (file)
@@ -14,4 +14,7 @@ libcnid_tdb_la_SOURCES = cnid_tdb_add.c \
                         cnid_tdb_update.c \
                         cnid_tdb.h
 
+libcnid_tdb_la_CFLAGS = @TDB_CFLAGS@
+libcnid_tdb_la_LIBADD = @TDB_LIBS@
+
 EXTRA_DIST = README cnid_tdb_nextid.c
index fcb43fa2f6488d5d725a79ee43cec230571e96a3..b3babcd35f2b87f58d9f08474dbf9bea15099ccd 100644 (file)
@@ -93,7 +93,7 @@ static int add_cnid (struct _cnid_tdb_private *db, TDB_DATA *key, TDB_DATA *data
     }
 
     /* did/name database */
-    altkey.dptr = (char *) data->dptr +CNID_DID_OFS;
+    altkey.dptr = data->dptr +CNID_DID_OFS;
     altkey.dsize = data->dsize -CNID_DID_OFS;
     if (tdb_store(db->tdb_didname, altkey, altdata, TDB_REPLACE)) {
         goto abort;
@@ -112,7 +112,7 @@ static cnid_t get_cnid(struct _cnid_tdb_private *db)
     
     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
     memset(&data, 0, sizeof(data));
-    rootinfo_key.dptr = ROOTINFO_KEY;
+    rootinfo_key.dptr = (unsigned char *)ROOTINFO_KEY;
     rootinfo_key.dsize = ROOTINFO_KEYLEN;
     
     tdb_chainlock(db->tdb_didname, rootinfo_key);  
@@ -136,7 +136,7 @@ static cnid_t get_cnid(struct _cnid_tdb_private *db)
     }
     
     memset(&data, 0, sizeof(data));
-    data.dptr = (char *)&hint;
+    data.dptr = (unsigned char *)&hint;
     data.dsize = sizeof(hint);
     if (tdb_store(db->tdb_didname, rootinfo_key, data, TDB_REPLACE)) {
         goto cleanup;
@@ -160,7 +160,7 @@ cnid_t cnid_tdb_add(struct _cnid_db *cdb, const struct stat *st,
     TDB_DATA key, data; 
     int rc;      
     
-    if (!cdb || !(priv = cdb->_private) || !st || !name) {
+    if (!cdb || !(priv = cdb->cnid_db_private) || !st || !name) {
         errno = CNID_ERR_PARAM;
         return CNID_INVALID;
     }
@@ -181,9 +181,9 @@ cnid_t cnid_tdb_add(struct _cnid_db *cdb, const struct stat *st,
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    key.dptr = (char *)&hint;
+    key.dptr = (unsigned char *)&hint;
     key.dsize = sizeof(cnid_t);
-    if ((data.dptr = make_tdb_data(cdb->flags, lstp, did, name, len)) == NULL) {
+    if ((data.dptr = make_tdb_data(cdb->cnid_db_flags, lstp, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "tdb_add: Path name is too long");
         errno = CNID_ERR_PATH;
         return CNID_INVALID;
index cbe89a6e8a8dcd415b3f63fafb198c808057c419..4879e10bba415ef75a4f1bcc201653430df19d1d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_close.c,v 1.3 2009-11-21 13:38:11 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
@@ -14,10 +13,9 @@ void cnid_tdb_close(struct _cnid_db *cdb)
 {
     struct _cnid_tdb_private *db;
 
-    free(cdb->volpath);
-    db = (struct _cnid_tdb_private *)cdb->_private;
+    db = (struct _cnid_tdb_private *)cdb->cnid_db_private;
     tdb_close(db->tdb_cnid);
-    free(cdb->_private);
+    free(cdb->cnid_db_private);
     free(cdb);
 }
 
index aa801e584ac29c43a16be416e2a881771fb52d2b..95441098bf807938476cd60e6ed1c8500e0ec7de 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_delete.c,v 1.4 2009-11-20 19:25:05 didg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
@@ -20,13 +19,13 @@ int cnid_tdb_delete(struct _cnid_db *cdb, const cnid_t id)
     struct _cnid_tdb_private *db;
     TDB_DATA key, data;
 
-    if (!cdb || !(db = cdb->_private) || !id) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id) {
         return -1;
     }
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    key.dptr  = (char *)&id;
+    key.dptr  = (unsigned char *)&id;
     key.dsize = sizeof(cnid_t);
     data = tdb_fetch(db->tdb_cnid, key);
     if (!data.dptr)
@@ -40,7 +39,7 @@ int cnid_tdb_delete(struct _cnid_db *cdb, const cnid_t id)
     key.dsize = CNID_DEVINO_LEN;
     tdb_delete(db->tdb_devino, key); 
 
-    key.dptr = (char *)data.dptr +CNID_DID_OFS;
+    key.dptr = data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize -CNID_DID_OFS;
     tdb_delete(db->tdb_didname, key); 
 
index 8321d04e868eb20469c061bd08c0057c7dacc7ae..5c7536777eca193a7c80da3411e3172a3ceaf91c 100644 (file)
@@ -14,7 +14,7 @@ cnid_t cnid_tdb_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t l
     TDB_DATA key, data;
     cnid_t id;
 
-    if (!cdb || !(db = cdb->_private) || (len > MAXPATHLEN)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || (len > MAXPATHLEN)) {
         return 0;
     }
 
@@ -26,7 +26,7 @@ cnid_t cnid_tdb_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t l
     buf += sizeof(did);
     memcpy(buf, name, len);
     *(buf + len) = '\0'; /* Make it a C-string. */
-    key.dptr = start;
+    key.dptr = (unsigned char *)start;
     key.dsize = CNID_DID_LEN + len + 1;
     data = tdb_fetch(db->tdb_didname, key);
     if (!data.dptr)
index f095cd54b9c989e186e66360177b0c4bc4f8110a..7c7bdff14c2fdfd3b5dcc9dc01420a5bc2dc1faa 100644 (file)
@@ -21,11 +21,11 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     int update = 0;
     cnid_t id_devino = 0, id_didname = 0,id = 0;
 
-    if (!cdb || !(db = cdb->_private) || !st || !name) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
         return 0;
     }
 
-    if ((buf = make_tdb_data(cdb->flags, st, did, name, len)) == NULL) {
+    if ((buf = (char *)make_tdb_data(cdb->cnid_db_flags, st, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "tdb_lookup: Pathname is too long");
         return 0;
     }
@@ -42,7 +42,7 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN);
     memcpy(ino, buf + CNID_INO_OFS, CNID_INO_LEN);
 
-    key.dptr = buf +CNID_DEVINO_OFS;
+    key.dptr = (unsigned char *)buf + CNID_DEVINO_OFS;
     key.dsize  = CNID_DEVINO_LEN;
     cniddata = tdb_fetch(db->tdb_devino, key);
     if (!cniddata.dptr) {
@@ -66,7 +66,7 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     }
 
     /* did/name now */
-    key.dptr = buf + CNID_DID_OFS;
+    key.dptr = (unsigned char *)buf + CNID_DID_OFS;
     key.dsize = CNID_DID_LEN + len + 1;
     cniddata = tdb_fetch(db->tdb_didname, key);
     if (!cniddata.dptr) {
index 8aefa42b2278c6f34696d095451ff7e375a2b62e..8e7b12616f0a9f3020e03f743bef034fdd6ce543 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_nextid.c,v 1.2 2005-04-28 20:50:02 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index d02ef0bcc054caa1da65ab3ccfa5f6c5885a8b5a..7e2558fe3c34fc13592f24e40df6ccd0a78c31e6 100644 (file)
@@ -12,6 +12,8 @@
 
 #include "cnid_tdb.h"
 #include <atalk/logger.h>
+#include <atalk/volume.h>
+
 #include <stdlib.h>
 #define DBHOME       ".AppleDB"
 #define DBHOMELEN    9                  /* strlen(DBHOME) +1 for / */
 #define DBVERSION2       0x00000002U
 #define DBVERSION        DBVERSION2
 
-static struct _cnid_db *cnid_tdb_new(const char *volpath)
+static struct _cnid_db *cnid_tdb_new(struct vol *vol)
 {
     struct _cnid_db *cdb;
-    struct _cnid_tdb_private *priv;
 
     if ((cdb = (struct _cnid_db *) calloc(1, sizeof(struct _cnid_db))) == NULL)
         return NULL;
 
-    if ((cdb->volpath = strdup(volpath)) == NULL) {
-        free(cdb);
-        return NULL;
-    }
+    cdb->cnid_db_vol = vol;
 
-    if ((cdb->_private = calloc(1, sizeof(struct _cnid_tdb_private))) == NULL) {
-        free(cdb->volpath);
+    if ((cdb->cnid_db_private = calloc(1, sizeof(struct _cnid_tdb_private))) == NULL) {
         free(cdb);
         return NULL;
     }
 
-    /* Set up private state */
-    priv = (struct _cnid_tdb_private *) (cdb->_private);
-
     /* Set up standard fields */
-    cdb->flags = CNID_FLAG_PERSISTENT;
+    cdb->cnid_db_flags = CNID_FLAG_PERSISTENT;
 
     cdb->cnid_add = cnid_tdb_add;
     cdb->cnid_delete = cnid_tdb_delete;
@@ -56,7 +50,8 @@ static struct _cnid_db *cnid_tdb_new(const char *volpath)
     cdb->cnid_resolve = cnid_tdb_resolve;
     cdb->cnid_update = cnid_tdb_update;
     cdb->cnid_close = cnid_tdb_close;
-    
+    cdb->cnid_wipe = NULL;
+
     return cdb;
 }
 
@@ -71,31 +66,27 @@ struct _cnid_db *cnid_tdb_open(struct cnid_open_args *args)
     TDB_DATA                  key, data;
     int                      hash_size = 131071;
     int                       tdb_flags = 0;
+    struct vol               *vol = args->cnid_args_vol;
 
-    if (!args->dir) {
-        /* note: dir and path are not used for in memory db */
+    if ((cdb = cnid_tdb_new(vol)) == NULL) {
+        LOG(log_error, logtype_default, "tdb_open: Unable to allocate memory for tdb");
         return NULL;
     }
 
-    if ((len = strlen(args->dir)) > (MAXPATHLEN - DBLEN - 1)) {
-        LOG(log_error, logtype_default, "tdb_open: Pathname too large: %s", args->dir);
-        return NULL;
-    }
-    
-    if ((cdb = cnid_tdb_new(args->dir)) == NULL) {
-        LOG(log_error, logtype_default, "tdb_open: Unable to allocate memory for tdb");
+    if ((len = strlen(vol->v_path)) > (MAXPATHLEN - DBLEN - 1)) {
+        LOG(log_error, logtype_default, "tdb_open: Pathname too large: %s", vol->v_path);
         return NULL;
     }
     
-    strcpy(path, args->dir);
+    strcpy(path, vol->v_path);
     if (path[len - 1] != '/') {
         strcat(path, "/");
         len++;
     }
  
     strcpy(path + len, DBHOME);
-    if (!(args->flags & CNID_FLAG_MEMORY)) {
-        if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~args->mask) < 0)) {
+    if (!(args->cnid_args_flags & CNID_FLAG_MEMORY)) {
+        if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~vol->v_umask) < 0)) {
             LOG(log_error, logtype_default, "tdb_open: DBHOME mkdir failed for %s", path);
             goto fail;
         }
@@ -106,12 +97,12 @@ struct _cnid_db *cnid_tdb_open(struct cnid_open_args *args)
     }
     strcat(path, "/");
  
-    db = (struct _cnid_tdb_private *)cdb->_private;
+    db = (struct _cnid_tdb_private *)cdb->cnid_db_private;
 
     path[len + DBHOMELEN] = '\0';
     strcat(path, DBCNID);
 
-    db->tdb_cnid = tdb_open(path, hash_size, tdb_flags , O_RDWR | O_CREAT, 0666 & ~args->mask);
+    db->tdb_cnid = tdb_open(path, hash_size, tdb_flags , O_RDWR | O_CREAT, 0666 & ~vol->v_umask);
     if (!db->tdb_cnid) {
         LOG(log_error, logtype_default, "tdb_open: unable to open tdb", path);
         goto fail;
@@ -124,14 +115,14 @@ struct _cnid_db *cnid_tdb_open(struct cnid_open_args *args)
      * to change the format in any way. */
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
-    key.dptr = DBVERSION_KEY;
+    key.dptr = (unsigned char *)DBVERSION_KEY;
     key.dsize = DBVERSION_KEYLEN;
 
     data = tdb_fetch(db->tdb_didname, key);
     if (!data.dptr) {
         uint32_t version = htonl(DBVERSION);
 
-        data.dptr = (char *)&version;
+        data.dptr = (unsigned char *)&version;
         data.dsize = sizeof(version);
         if (tdb_store(db->tdb_didname, key, data, TDB_REPLACE)) {
             LOG(log_error, logtype_default, "tdb_open: Error putting new version");
@@ -145,8 +136,7 @@ struct _cnid_db *cnid_tdb_open(struct cnid_open_args *args)
     return cdb;
 
 fail:
-    free(cdb->_private);
-    free(cdb->volpath);
+    free(cdb->cnid_db_private);
     free(cdb);
     
     return NULL;
index 5e8312222ca377b7e8e25bcf27d53b9017e6afb3..b999ca3e001d73b6d5271149b52c3a3e814dc773 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_resolve.c,v 1.4 2009-11-22 14:14:05 franklahm Exp $
  */
 
 #ifdef HAVE_CONFIG_H
@@ -16,10 +15,10 @@ char *cnid_tdb_resolve(struct _cnid_db *cdb, cnid_t * id, void *buffer, size_t l
     struct _cnid_tdb_private *db;
     TDB_DATA key, data;      
 
-    if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !(*id)) {
         return NULL;
     }
-    key.dptr  = (char *)id;
+    key.dptr  = (unsigned char *)id;
     key.dsize = sizeof(cnid_t);
     data = tdb_fetch(db->tdb_cnid, key);
     if (data.dptr) 
index d367334e06126c8f903fe8bea20d1a0d7efea0b1..9688d866c5c5490a16751de56d89ea144298e9b5 100644 (file)
@@ -13,7 +13,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     struct _cnid_tdb_private *db;
     TDB_DATA key, data, altdata;
 
-    if (!cdb || !(db = cdb->_private) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO)) {
+    if (!cdb || !(db = cdb->cnid_db_private) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO)) {
         return -1;
     }
 
@@ -22,7 +22,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
 
 
     /* Get the old info. search by dev/ino */
-    data.dptr = make_tdb_data(cdb->flags, st, did, name, len);
+    data.dptr = make_tdb_data(cdb->cnid_db_flags, st, did, name, len);
     data.dsize = CNID_HEADER_LEN + len + 1;
     key.dptr = data.dptr +CNID_DEVINO_OFS;
     key.dsize = CNID_DEVINO_LEN;
@@ -38,7 +38,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
         free(altdata.dptr);
 
         if (data.dptr) {
-            key.dptr = (char *)data.dptr +CNID_DID_OFS;
+            key.dptr = (unsigned char *)data.dptr +CNID_DID_OFS;
             key.dsize = data.dsize - CNID_DID_OFS;
             tdb_delete(db->tdb_didname, key); 
         
@@ -47,9 +47,9 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     }
 
     /* search by did/name */
-    data.dptr = make_tdb_data(cdb->flags, st, did, name, len);
+    data.dptr = make_tdb_data(cdb->cnid_db_flags, st, did, name, len);
     data.dsize = CNID_HEADER_LEN + len + 1;
-    key.dptr = (char *)data.dptr +CNID_DID_OFS;
+    key.dptr = (unsigned char *)data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize - CNID_DID_OFS;
     altdata = tdb_fetch(db->tdb_didname, key);
     if (altdata.dptr) {
@@ -71,12 +71,12 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     
 
     /* Make a new entry. */
-    data.dptr = make_tdb_data(cdb->flags, st, did, name, len);
+    data.dptr = make_tdb_data(cdb->cnid_db_flags, st, did, name, len);
     data.dsize = CNID_HEADER_LEN + len + 1;
     memcpy(data.dptr, &id, sizeof(id));
 
     /* Update the old CNID with the new info. */
-    key.dptr = (char *) &id;
+    key.dptr = (unsigned char *) &id;
     key.dsize = sizeof(id);
     if (tdb_store(db->tdb_cnid, key, data, TDB_REPLACE)) {
         goto update_err;
@@ -85,13 +85,13 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     /* Put in a new dev/ino mapping. */
     key.dptr = data.dptr +CNID_DEVINO_OFS;
     key.dsize = CNID_DEVINO_LEN;
-    altdata.dptr  = (char *) &id;
+    altdata.dptr  = (unsigned char *) &id;
     altdata.dsize = sizeof(id);
     if (tdb_store(db->tdb_devino, key, altdata, TDB_REPLACE)) {
         goto update_err;
     }
     /* put in a new did/name mapping. */
-    key.dptr = (char *) data.dptr +CNID_DID_OFS;
+    key.dptr = (unsigned char *) data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize -CNID_DID_OFS;
 
     if (tdb_store(db->tdb_didname, key, altdata, TDB_REPLACE)) {
index 2716bb8f955ca79c002dde8237f8107f4141f7e5..481978b9f42f67f8f5897623c9bd62ba66810196 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: getusershell.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * Copyright (c) 1985 Regents of the University of California.
  * All rights reserved.
index 5811531520ddcf860da59a6d76dae346d0e8a191..614c6f2d155ab6b04b2184262b7bea2a7b622649 100644 (file)
@@ -2,6 +2,9 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include <stdio.h>
+#include <stdarg.h>
+
 #include <atalk/compat.h>
 
 #if !defined HAVE_DIRFD && defined SOLARIS
@@ -25,3 +28,50 @@ size_t strnlen(const char *s, size_t max)
     return len;  
 }
 #endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **ret, const char *fmt, va_list ap)
+{
+    int n, size = 64;
+    char *p, *np;
+
+    if ((p = malloc(size)) == NULL)
+        return NULL;
+
+    while (1) {
+        /* Try to print in the allocated space. */
+        n = vsnprintf(p, size, fmt, ap);
+        /* If that worked, return the string. */
+        if (n > -1 && n < size) {
+            *ret = p;
+            return n;
+        }
+        /* Else try again with more space. */
+        if (n > -1)    /* glibc 2.1 */
+            size = n+1; /* precisely what is needed */
+        else           /* glibc 2.0 */
+            size *= 2;  /* twice the old size */
+        if ((np = realloc (p, size)) == NULL) {
+            free(p);
+            *ret = NULL;
+            return -1;
+        } else {
+            p = np;
+        }
+    }
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...)
+{
+    va_list ap;
+    int len;
+
+    va_start(ap, fmt);
+    len = vasprintf(strp, fmt, ap);
+    va_end(ap);
+
+    return len;
+}
+#endif
index ddbfe2d5f88a338e4722db6cc5b42a79a7a8353e..8faae19336149d309234b182002bdead6c3d9cdd 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: mktemp.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * Copyright (c) 1987 Regents of the University of California.
  * All rights reserved.
index 03dd3bb601f1d6cb849d43736c5017aa461a649d..eb73c9754e88c4a5c4c29778c8ddb32c1304f28f 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: rquota_xdr.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * taken from the quota-1.55 used on linux. here's the bsd copyright:
  *
index 5a60c24d5c25d30d223b820b3edf496c89009740..80757712192a8240421d71e8f10cce7588310d1a 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_attn.c,v 1.8 2009-10-25 06:13:11 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 80bee3fe702a79cfbd18bd7a7ac4bd8539295aaf..199954fce69ccfdeca888cf4ab3277ccb0ca4d01 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_close.c,v 1.4 2003-03-12 15:07:06 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
@@ -22,7 +21,7 @@ void dsi_close(DSI *dsi)
       dsi->header.dsi_flags = DSIFL_REQUEST;
       dsi->header.dsi_command = DSIFUNC_CLOSE;
       dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
-      dsi->header.dsi_code = dsi->header.dsi_reserved = htonl(0);
+      dsi->header.dsi_data.dsi_code = dsi->header.dsi_reserved = htonl(0);
       dsi->cmdlen = 0; 
       dsi_send(dsi);
       dsi->proto_close(dsi);
index 5b0d627de2c788e7356ffeb50219163d6007cb3c..04d17083351ea715cf89809c0cd3eb8189d4fef7 100644 (file)
@@ -25,7 +25,7 @@ int dsi_cmdreply(DSI *dsi, const int err)
 
     dsi->header.dsi_flags = DSIFL_REPLY;
     dsi->header.dsi_len = htonl(dsi->datalen);
-    dsi->header.dsi_code = htonl(err);
+    dsi->header.dsi_data.dsi_code = htonl(err);
 
     ret = dsi_stream_send(dsi, dsi->data, dsi->datalen);
 
index 001279cd74ae6ad717eef7fe01a7880a77b3dfab..89af152b76aa6e312de2d0096002ea8283fec737 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_getsess.c,v 1.7 2005-04-28 20:50:02 bfernhomberg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  * @param childp    (w) after fork: parent return pointer to child, child returns NULL
  * @returns             0 on sucess, any other value denotes failure
  */
-int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_child_t **childp)
+int dsi_getsession(DSI *dsi, server_child_t *serv_children, int tickleval, afp_child_t **childp)
 {
   pid_t pid;
-  unsigned int ipc_fds[2];  
+  int ipc_fds[2];  
   afp_child_t *child;
 
   if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
@@ -62,13 +61,13 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
     /* using SIGKILL is hokey, but the child might not have
      * re-established its signal handler for SIGTERM yet. */
     close(ipc_fds[1]);
-    if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds[0])) ==  NULL) {
+    if ((child = server_child_add(serv_children, pid, ipc_fds[0])) ==  NULL) {
       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
       close(ipc_fds[0]);
       dsi->header.dsi_flags = DSIFL_REPLY;
-      dsi->header.dsi_code = DSIERR_SERVBUSY;
+      dsi->header.dsi_data.dsi_code = htonl(DSIERR_SERVBUSY);
       dsi_send(dsi);
-      dsi->header.dsi_code = DSIERR_OK;
+      dsi->header.dsi_data.dsi_code = DSIERR_OK;
       kill(pid, SIGKILL);
     }
     dsi->proto_close(dsi);
@@ -76,16 +75,9 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
     return 0;
   }
   
-  /* child: check number of open connections. this is one off the
-   * actual count. */
-  if ((serv_children->count >= serv_children->nsessions) &&
-      (dsi->header.dsi_command == DSIFUNC_OPEN)) {
-    LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
-    dsi->header.dsi_flags = DSIFL_REPLY;
-    dsi->header.dsi_code = DSIERR_TOOMANY;
-    dsi_send(dsi);
-    exit(EXITERR_CLNT);
-  }
+  /* Save number of existing and maximum connections */
+  dsi->AFPobj->cnx_cnt = serv_children->servch_count;
+  dsi->AFPobj->cnx_max = serv_children->servch_nsessions;
 
   /* get rid of some stuff */
   dsi->AFPobj->ipc_fd = ipc_fds[1];
@@ -121,7 +113,6 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
     /* set up the tickle timer */
     dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval;
     dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
-    signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
     dsi_opensession(dsi);
     *childp = NULL;
     return 0;
index aec872ab0c15f1aaea351ddeb9febe44860e9f9a..4be9b802175fc218f3867a6655eb84a602783290 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_getstat.c,v 1.4 2005-09-07 15:27:29 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
@@ -21,7 +20,7 @@ void dsi_getstatus(DSI *dsi)
 {
   dsi->header.dsi_flags = DSIFL_REPLY;
   /*dsi->header.dsi_command = DSIFUNC_STAT;*/
-  dsi->header.dsi_code = dsi->header.dsi_reserved = 0;
+  dsi->header.dsi_data.dsi_code = dsi->header.dsi_reserved = 0;
   
   memcpy(dsi->commands, dsi->status, dsi->statuslen);
   dsi->cmdlen = dsi->statuslen; 
index a2dcb4c9b0d6c11177eca4cd0eea24344a63163f..2945f9b185f242de3a6a767f1c8d3a5e281ee38e 100644 (file)
@@ -44,7 +44,7 @@ void dsi_opensession(DSI *dsi)
   /* let the client know the server quantum. we don't use the
    * max server quantum due to a bug in appleshare client 3.8.6. */
   dsi->header.dsi_flags = DSIFL_REPLY;
-  dsi->header.dsi_code = 0;
+  dsi->header.dsi_data.dsi_code = 0;
   /* dsi->header.dsi_command = DSIFUNC_OPEN;*/
 
   dsi->cmdlen = 2 * (2 + sizeof(i)); /* length of data. dsi_send uses it. */
index a0cbd872c2851a7ef2ba14021a52138f4a03d7ad..31ee3bdff4a9191327b808061c6979c8ae4a0dcf 100644 (file)
@@ -31,7 +31,7 @@ ssize_t dsi_readinit(DSI *dsi, void *buf, const size_t buflen, const size_t size
     dsi->flags |= DSI_NOREPLY; /* we will handle our own replies */
     dsi->header.dsi_flags = DSIFL_REPLY;
     dsi->header.dsi_len = htonl(size);
-    dsi->header.dsi_code = htonl(err);
+    dsi->header.dsi_data.dsi_code = htonl(err);
 
     dsi->in_write++;
     if (dsi_stream_send(dsi, buf, buflen)) {
index 711a037b5098925c59c77198bb6350d390d9b5a3..c8f859ce1ca4ff8264f59dadddbbb8ceaf190fb7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1998 Adrian Sun (asun@zoology.washington.edu)
  * Copyright (c) 2010,2011,2012 Frank Lahm <franklahm@googlemail.com>
- * All rights reserved. See COPYRIGHT.
+> * All rights reserved. See COPYRIGHT.
  *
  * this file provides the following functions:
  * dsi_stream_write:    just write a bunch of bytes.
@@ -45,7 +45,7 @@ static void dsi_header_pack_reply(const DSI *dsi, char *buf)
     buf[0] = dsi->header.dsi_flags;
     buf[1] = dsi->header.dsi_command;
     memcpy(buf + 2, &dsi->header.dsi_requestID, sizeof(dsi->header.dsi_requestID));           
-    memcpy(buf + 4, &dsi->header.dsi_code, sizeof(dsi->header.dsi_code));
+    memcpy(buf + 4, &dsi->header.dsi_data.dsi_code, sizeof(dsi->header.dsi_data.dsi_code));
     memcpy(buf + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len));
     memcpy(buf + 12, &dsi->header.dsi_reserved, sizeof(dsi->header.dsi_reserved));
 }
@@ -115,7 +115,7 @@ static int dsi_peek(DSI *dsi)
         if (FD_ISSET(dsi->socket, &readfds)) {
             len = dsi->end - dsi->eof; /* it's ensured above that there's space */
 
-            if ((len = read(dsi->socket, dsi->eof, len)) <= 0) {
+            if ((len = recv(dsi->socket, dsi->eof, len, 0)) <= 0) {
                 if (len == 0) {
                     LOG(log_error, logtype_dsi, "dsi_peek: EOF");
                     return -1;
@@ -185,7 +185,7 @@ static ssize_t buf_read(DSI *dsi, uint8_t *buf, size_t count)
     if (len)
         return len;             /* 2. */
   
-    len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
+    len = readt(dsi->socket, buf, count, 0, 0); /* 3. */
 
     LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
 
@@ -213,7 +213,7 @@ static size_t dsi_buffered_stream_read(DSI *dsi, uint8_t *data, const size_t len
   buflen = MIN(8192, dsi->end - dsi->eof);
   if (buflen > 0) {
       ssize_t ret;
-      ret = read(dsi->socket, dsi->eof, buflen);
+      ret = recv(dsi->socket, dsi->eof, buflen, 0);
       if (ret > 0)
           dsi->eof += ret;
   }
@@ -344,6 +344,16 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
     int sfvcnt;
     struct sendfilevec vec[2];
     ssize_t nwritten;
+#elif defined(FREEBSD)
+    ssize_t nwritten;
+    void *hdrp;
+    struct sf_hdtr hdr;
+    struct iovec iovec;
+    hdr.headers = &iovec;
+    hdr.hdr_cnt = 1;
+    hdr.trailers = NULL;
+    hdr.trl_cnt = 0;
+    hdrp = &hdr;
 #endif
 
     LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length);
@@ -356,7 +366,7 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
     dsi->flags |= DSI_NOREPLY;
     dsi->header.dsi_flags = DSIFL_REPLY;
     dsi->header.dsi_len = htonl(length);
-    dsi->header.dsi_code = htonl(err);
+    dsi->header.dsi_data.dsi_code = htonl(err);
     dsi_header_pack_reply(dsi, block);
 
 #ifdef HAVE_SENDFILEV
@@ -372,6 +382,9 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
     vec[1].sfv_flag = 0;
     vec[1].sfv_off = offset;
     vec[1].sfv_len = length;
+#elif defined(FREEBSD)
+    iovec.iov_base = block;
+    iovec.iov_len = DSI_BLOCKSIZ;
 #else
     dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE);
 #endif
@@ -380,6 +393,10 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
 #ifdef HAVE_SENDFILEV
         nwritten = 0;
         len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten);
+#elif defined(FREEBSD)
+        len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0);
+        if (len == 0)
+            len = nwritten;
 #else
         len = sys_sendfile(dsi->socket, fromfd, &pos, total - written);
 #endif
@@ -388,16 +405,14 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
             case EINTR:
             case EAGAIN:
                 len = 0;
-#ifdef HAVE_SENDFILEV
+#if defined(HAVE_SENDFILEV) || defined(FREEBSD)
                 len = (size_t)nwritten;
-#else
-#if defined(SOLARIS) || defined(FREEBSD)
+#elif defined(SOLARIS)
                 if (pos > offset) {
                     /* we actually have sent sth., adjust counters and keep trying */
                     len = pos - offset;
                     offset = pos;
                 }
-#endif /* defined(SOLARIS) || defined(FREEBSD) */
 #endif /* HAVE_SENDFILEV */
 
                 if (dsi_peek(dsi) != 0) {
@@ -426,6 +441,18 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
             vec[0].sfv_off += len;
             vec[0].sfv_len -= len;
         }
+#elif defined(FREEBSD)
+        if (hdrp) {
+            if (len >= iovec.iov_len) {
+                hdrp = NULL;
+                len -= iovec.iov_len;   /* len now contains how much sendfile() actually sent from the file */
+            } else {
+                iovec.iov_len -= len;
+                iovec.iov_base += len;
+                len = 0;
+            }
+        }
+        pos += len;
 #endif  /* HAVE_SENDFILEV */
         LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len);
         written += len;
@@ -471,10 +498,6 @@ size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
           stored += len;
       } else { /* eof or error */
           /* don't log EOF error if it's just after connect (OSX 10.3 probe) */
-#if 0
-          if (errno == ECONNRESET)
-              dsi->flags |= DSI_GOT_ECONNRESET;
-#endif
           if (len || stored || dsi->read_count) {
               if (! (dsi->flags & DSI_DISCONNECTED)) {
                   LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
@@ -500,6 +523,7 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
 {
   char block[DSI_BLOCKSIZ];
   struct iovec iov[2];
+  int iovecs = 2;
   size_t towrite;
   ssize_t len;
 
@@ -526,7 +550,7 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
   towrite = sizeof(block) + length;
   dsi->write_count += towrite;
   while (towrite > 0) {
-      if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || (len == 0))
+      if (((len = writev(dsi->socket, iov, iovecs)) == -1 && errno == EINTR) || (len == 0))
           continue;
     
       if ((size_t)len == towrite) /* wrote everything out */
@@ -547,12 +571,13 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
           iov[0].iov_base = (char *) iov[0].iov_base + len;
           iov[0].iov_len -= len;
       } else { /* skip to data */
-          if (iov[0].iov_len) {
+          if (iovecs == 2) {
+              iovecs = 1;
               len -= iov[0].iov_len;
-              iov[0].iov_len = 0;
+              iov[0] = iov[1];
           }
-          iov[1].iov_base = (char *) iov[1].iov_base + len;
-          iov[1].iov_len -= len;
+          iov[0].iov_base = (char *) iov[0].iov_base + len;
+          iov[0].iov_len -= len;
       }
   }
 
@@ -590,14 +615,23 @@ int dsi_stream_receive(DSI *dsi)
       return 0;
 
   memcpy(&dsi->header.dsi_requestID, block + 2, sizeof(dsi->header.dsi_requestID));
-  memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+  memcpy(&dsi->header.dsi_data.dsi_doff, block + 4, sizeof(dsi->header.dsi_data.dsi_doff));
+  dsi->header.dsi_data.dsi_doff = htonl(dsi->header.dsi_data.dsi_doff);
   memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
+
   memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved));
   dsi->clientID = ntohs(dsi->header.dsi_requestID);
   
   /* make sure we don't over-write our buffers. */
   dsi->cmdlen = MIN(ntohl(dsi->header.dsi_len), dsi->server_quantum);
-  if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen) 
+
+  /* Receiving DSIWrite data is done in AFP function, not here */
+  if (dsi->header.dsi_data.dsi_doff) {
+      LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: write request");
+      dsi->cmdlen = dsi->header.dsi_data.dsi_doff;
+  }
+
+  if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen)
     return 0;
 
   LOG(log_debug, logtype_dsi, "dsi_stream_receive: DSI cmdlen: %zd", dsi->cmdlen);
index e06e87d92420187948611b9d044995ab158065c6..24fde971fbd2aca085456c8494e7dd3bbeccfb79 100644 (file)
@@ -103,9 +103,29 @@ static void dsi_init_buffer(DSI *dsi)
     dsi->end = dsi->buffer + (dsi->dsireadbuf * dsi->server_quantum);
 }
 
+/*!
+ * Free any allocated ressources of the master afpd DSI objects and close server socket
+ */
+void dsi_free(DSI *dsi)
+{
+    close(dsi->serversock);
+    dsi->serversock = -1;
+
+    free(dsi->commands);
+    dsi->commands = NULL;
+
+    free(dsi->buffer);
+    dsi->buffer = NULL;
+
+#ifdef USE_ZEROCONF
+    free(dsi->bonjourname);
+    dsi->bonjourname = NULL;
+#endif
+}
+
 static struct itimerval itimer;
 /* accept the socket and do a little sanity checking */
-static int dsi_tcp_open(DSI *dsi)
+static pid_t dsi_tcp_open(DSI *dsi)
 {
     pid_t pid;
     SOCKLEN_T len;
@@ -166,7 +186,7 @@ static int dsi_tcp_open(DSI *dsi)
         len = dsi_stream_read(dsi, block, 2);
         if (!len ) {
             /* connection already closed, don't log it (normal OSX 10.3 behaviour) */
-            exit(EXITERR_CLNT);
+            exit(EXITERR_CLOSED);
         }
         if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
             LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header");
@@ -189,7 +209,7 @@ static int dsi_tcp_open(DSI *dsi)
         dsi->header.dsi_command = block[1];
         memcpy(&dsi->header.dsi_requestID, block + 2,
                sizeof(dsi->header.dsi_requestID));
-        memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+        memcpy(&dsi->header.dsi_data.dsi_code, block + 4, sizeof(dsi->header.dsi_data.dsi_code));
         memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
         memcpy(&dsi->header.dsi_reserved, block + 12,
                sizeof(dsi->header.dsi_reserved));
@@ -284,25 +304,45 @@ iflist_done:
 #define AI_NUMERICSERV 0
 #endif
 
-/* this needs to accept passed in addresses */
+/*!
+ * Initialize DSI over TCP
+ *
+ * @param dsi        (rw) DSI handle
+ * @param hostname   (r)  pointer to hostname string
+ * @param inaddress  (r)  Optional IPv4 or IPv6 address with an optional port, may be NULL
+ * @param inport     (r)  pointer to port string
+ *
+ * Creates listening AFP/DSI socket. If the parameter inaddress is NULL, then we listen
+ * on the wildcard address, ie on all interfaces. That should mean listening on the IPv6
+ * address "::" on IPv4/IPv6 dual stack kernels, accepting both v4 and v6 requests.
+ *
+ * If the parameter inaddress is not NULL, then we only listen on the given address.
+ * The parameter may contain a port number using the URL format for address and port:
+ *
+ *   IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port
+ *
+ * Parameter inport must be a valid pointer to a port string and is used if the inaddress
+ * parameter doesn't contain a port.
+ *
+ * @returns 0 on success, -1 on failure
+ */
 int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
 {
     EC_INIT;
     int                flag, err;
-    char               *a = NULL, *b;
-    const char         *address;
-    const char         *port;
+    char              *address = NULL, *port = NULL;
     struct addrinfo    hints, *servinfo, *p;
 
-    /* Check whether address is of the from IP:PORT and split */
-    address = inaddress;
-    port = inport;
-    if (address && strchr(address, ':')) {
-        EC_NULL_LOG( address = a = strdup(address) );
-        b = strchr(a, ':');
-        *b = 0;
-        port = b + 1;
-    }
+    /* inaddress may be NULL */
+    AFP_ASSERT(dsi && hostname && inport);
+
+    if (inaddress)
+        /* Check whether address is of the from IP:PORT and split */
+        EC_ZERO( tokenize_ip_port(inaddress, &address, &port) );
+
+    if (port == NULL)
+        /* inport is supposed to always contain a valid port string */
+        EC_NULL( port = strdup(inport) );
 
     /* Prepare hint for getaddrinfo */
     memset(&hints, 0, sizeof hints);
@@ -323,8 +363,8 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const ch
         hints.ai_family = AF_UNSPEC;
 #endif
     }
-    if ((ret = getaddrinfo(address ? address : NULL, port, &hints, &servinfo)) != 0) {
-        LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
+    if ((ret = getaddrinfo(address, port, &hints, &servinfo)) != 0) {
+        LOG(log_error, logtype_dsi, "dsi_tcp_init(%s): getaddrinfo: %s\n", address ? address : "*", gai_strerror(ret));
         EC_FAIL;
     }
 
@@ -427,8 +467,10 @@ interfaces:
     guess_interface(dsi, hostname, port ? port : "548");
 
 EC_CLEANUP:
-    if (a)
-        free(a);
+    if (address)
+        free(address);
+    if (port)
+        free(port);
     EC_EXIT;
 }
 
index 40d0c0ad4cb43376dc0421c4b8fd504708fc15e2..a283d40b82670fbb1b3172788fca1e8bfa62a248 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_tickle.c,v 1.8 2009-10-25 06:13:11 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 1cf25e64c6516ea65e4144f63eafcd625fdb72d2..fd8fe2c52da48c2c5f1a71db6cf75acc1c54237d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_write.c,v 1.5 2009-10-20 04:31:41 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
 #include <atalk/util.h>
 #include <atalk/logger.h>
 
-/* initialize relevant things for dsi_write. this returns the amount
- * of data in the data buffer. the interface has been reworked to allow
- * for arbitrary buffers. */
 size_t dsi_writeinit(DSI *dsi, void *buf, const size_t buflen _U_)
 {
-  size_t len, header;
+    size_t bytes = 0;
+    dsi->datasize = ntohl(dsi->header.dsi_len) - dsi->header.dsi_data.dsi_doff;
 
-  /* figure out how much data we have. do a couple checks for 0 
-   * data */
-  header = ntohl(dsi->header.dsi_doff);
-  dsi->datasize = header ? ntohl(dsi->header.dsi_len) - header : 0;
+    if (dsi->eof > dsi->start) {
+        /* We have data in the buffer */
+        bytes = MIN(dsi->eof - dsi->start, dsi->datasize);
+        memmove(buf, dsi->start, bytes);
+        dsi->start += bytes;
+        dsi->datasize -= bytes;
+        if (dsi->start >= dsi->eof)
+            dsi->start = dsi->eof = dsi->buffer;
+    }
 
-  if (dsi->datasize > 0) {
-      len = MIN(dsi->server_quantum - header, dsi->datasize);
+    LOG(log_maxdebug, logtype_dsi, "dsi_writeinit: remaining DSI datasize: %jd", (intmax_t)dsi->datasize);
 
-      /* write last part of command buffer into buf */
-      memmove(buf, dsi->commands + header, len);
-
-      /* recalculate remaining data */
-      dsi->datasize -= len;
-  } else
-    len = 0;
-
-  LOG(log_maxdebug, logtype_dsi, "dsi_writeinit: len: %ju, remaining DSI datasize: %jd",
-      (intmax_t)len, (intmax_t)dsi->datasize);
-
-  return len;
+    return bytes;
 }
 
+
 /* fill up buf and then return. this should be called repeatedly
  * until all the data has been read. i block alarm processing 
  * during the transfer to avoid sending unnecessary tickles. */
index b44796bb24e30394d70cfd84c81784255b9068e5..191db576d52e95ece30eb1de5312dd71442073bc 100644 (file)
@@ -3,7 +3,6 @@
    @file       dictionary.c
    @author     N. Devillard
    @date       Sep 2007
-   @version    $Revision: 1.27 $
    @brief      Implements a dictionary for string variables.
 
    This module implements a simple dictionary object, i.e. a list
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $
-       $Revision: 1.27 $
 */
 /*---------------------------------------------------------------------------
                                                                Includes
  ---------------------------------------------------------------------------*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <atalk/dictionary.h>
 #include <atalk/compat.h>
 
@@ -106,7 +107,7 @@ static char * xstrdup(char * s)
   by comparing the key itself in last resort.
  */
 /*--------------------------------------------------------------------------*/
-unsigned dictionary_hash(char * key)
+unsigned atalkdict_hash(char * key)
 {
        int                     len ;
        unsigned        hash ;
@@ -135,7 +136,7 @@ unsigned dictionary_hash(char * key)
   dictionary, give size=0.
  */
 /*--------------------------------------------------------------------------*/
-dictionary * dictionary_new(int size)
+dictionary * atalkdict_new(int size)
 {
        dictionary      *       d ;
 
@@ -161,7 +162,7 @@ dictionary * dictionary_new(int size)
   Deallocate a dictionary object and all memory associated to it.
  */
 /*--------------------------------------------------------------------------*/
-void dictionary_del(dictionary * d)
+void atalkdict_del(dictionary * d)
 {
        int             i ;
 
@@ -193,12 +194,12 @@ void dictionary_del(dictionary * d)
   dictionary object, you should not try to free it or modify it.
  */
 /*--------------------------------------------------------------------------*/
-const char * dictionary_get(const dictionary * d, const char *section, const char * key, const char * def)
+const char * atalkdict_get(const dictionary * d, const char *section, const char * key, const char * def)
 {
        unsigned        hash ;
        int                     i ;
 
-       hash = dictionary_hash(makekey(section, key));
+       hash = atalkdict_hash(makekey(section, key));
        for (i=0 ; i<d->size ; i++) {
         if (d->key[i]==NULL)
             continue ;
@@ -229,8 +230,8 @@ const char * dictionary_get(const dictionary * d, const char *section, const cha
   or the key are considered as errors: the function will return immediately
   in such a case.
 
-  Notice that if you dictionary_set a variable to NULL, a call to
-  dictionary_get will return a NULL value: the variable will be found, and
+  Notice that if you atalkdict_set a variable to NULL, a call to
+  atalkdict_get will return a NULL value: the variable will be found, and
   its value (NULL) is returned. In other words, setting the variable
   content to NULL is equivalent to deleting the variable from the
   dictionary. It is not possible (in this implementation) to have a key in
@@ -239,7 +240,7 @@ const char * dictionary_get(const dictionary * d, const char *section, const cha
   This function returns non-zero in case of failure.
  */
 /*--------------------------------------------------------------------------*/
-int dictionary_set(dictionary * d, char *section, char * key, char * val)
+int atalkdict_set(dictionary * d, char *section, char * key, char * val)
 {
        int                     i ;
        unsigned        hash ;
@@ -247,7 +248,7 @@ int dictionary_set(dictionary * d, char *section, char * key, char * val)
        if (d==NULL || section==NULL) return -1 ;
        
        /* Compute hash for this key */
-       hash = dictionary_hash(makekey(section, key));
+       hash = atalkdict_hash(makekey(section, key));
        /* Find if value is already in dictionary */
        if (d->n>0) {
                for (i=0 ; i<d->size ; i++) {
@@ -307,7 +308,7 @@ int dictionary_set(dictionary * d, char *section, char * key, char * val)
   key cannot be found.
  */
 /*--------------------------------------------------------------------------*/
-void dictionary_unset(dictionary * d, char *section, char * key)
+void atalkdict_unset(dictionary * d, char *section, char * key)
 {
        unsigned        hash ;
        int                     i ;
@@ -316,7 +317,7 @@ void dictionary_unset(dictionary * d, char *section, char * key)
                return;
        }
 
-       hash = dictionary_hash(makekey(section, key));
+       hash = atalkdict_hash(makekey(section, key));
        for (i=0 ; i<d->size ; i++) {
         if (d->key[i]==NULL)
             continue ;
@@ -356,7 +357,7 @@ void dictionary_unset(dictionary * d, char *section, char * key)
   output file pointers.
  */
 /*--------------------------------------------------------------------------*/
-void dictionary_dump(dictionary * d, FILE * out)
+void atalkdict_dump(dictionary * d, FILE * out)
 {
        int             i ;
 
index 6e89472b34be54aff750f48a3c53b073fedd04b4..53f6a108382513b51495251dec09fa8e2e14dcf4 100644 (file)
@@ -8,6 +8,10 @@
 */
 
 /*---------------------------- Includes ------------------------------------*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <ctype.h>
 
 #include <atalk/iniparser.h>
@@ -84,7 +88,7 @@ static char * strstrip(char * s)
   This function returns -1 in case of error.
  */
 /*--------------------------------------------------------------------------*/
-int iniparser_getnsec(const dictionary * d)
+int atalk_iniparser_getnsec(const dictionary * d)
 {
     int i ;
     int nsec ;
@@ -115,7 +119,7 @@ int iniparser_getnsec(const dictionary * d)
   This function returns NULL in case of error.
  */
 /*--------------------------------------------------------------------------*/
-const char * iniparser_getsecname(const dictionary * d, int n)
+const char * atalk_iniparser_getsecname(const dictionary * d, int n)
 {
     int i ;
     int foundsec ;
@@ -150,7 +154,7 @@ const char * iniparser_getsecname(const dictionary * d, int n)
   purposes mostly.
  */
 /*--------------------------------------------------------------------------*/
-void iniparser_dump(const dictionary * d, FILE * f)
+void atalk_iniparser_dump(const dictionary * d, FILE * f)
 {
     int     i ;
 
@@ -178,7 +182,7 @@ void iniparser_dump(const dictionary * d, FILE * f)
   It is Ok to specify @c stderr or @c stdout as output files.
  */
 /*--------------------------------------------------------------------------*/
-void iniparser_dump_ini(const dictionary * d, FILE * f)
+void atalk_iniparser_dump_ini(const dictionary * d, FILE * f)
 {
     int     i, j ;
     char    keym[ASCIILINESZ+1];
@@ -188,7 +192,7 @@ void iniparser_dump_ini(const dictionary * d, FILE * f)
 
     if (d==NULL || f==NULL) return ;
 
-    nsec = iniparser_getnsec(d);
+    nsec = atalk_iniparser_getnsec(d);
     if (nsec<1) {
         /* No section in file: dump all keys as they are */
         for (i=0 ; i<d->size ; i++) {
@@ -199,7 +203,7 @@ void iniparser_dump_ini(const dictionary * d, FILE * f)
         return ;
     }
     for (i=0 ; i<nsec ; i++) {
-        secname = iniparser_getsecname(d, i) ;
+        secname = atalk_iniparser_getsecname(d, i) ;
         seclen  = (int)strlen(secname);
         fprintf(f, "\n[%s]\n", secname);
         sprintf(keym, "%s:", secname);
@@ -234,14 +238,14 @@ void iniparser_dump_ini(const dictionary * d, FILE * f)
   the dictionary, do not free or modify it.
  */
 /*--------------------------------------------------------------------------*/
-const char * iniparser_getstring(const dictionary * d, const char *section, const char * key, const char * def)
+const char * atalk_iniparser_getstring(const dictionary * d, const char *section, const char * key, const char * def)
 {
     const char * sval ;
 
     if (d==NULL || key==NULL)
         return def ;
 
-    sval = dictionary_get(d, section, key, def);
+    sval = atalkdict_get(d, section, key, def);
     return sval ;
 }
 
@@ -260,14 +264,14 @@ const char * iniparser_getstring(const dictionary * d, const char *section, cons
   The returned char pointer a strdup'ed allocated string, so the caller must free it.
  */
 /*--------------------------------------------------------------------------*/
-char * iniparser_getstrdup(const dictionary * d, const char *section, const char * key, const char * def)
+char * atalk_iniparser_getstrdup(const dictionary * d, const char *section, const char * key, const char * def)
 {
     const char * sval ;
 
     if (d==NULL || key==NULL)
         return NULL;
 
-    if ((sval = dictionary_get(d, section, key, def)))
+    if ((sval = atalkdict_get(d, section, key, def)))
         return strdup(sval);
     return NULL;
 }
@@ -300,11 +304,11 @@ char * iniparser_getstrdup(const dictionary * d, const char *section, const char
   Credits: Thanks to A. Becker for suggesting strtol()
  */
 /*--------------------------------------------------------------------------*/
-int iniparser_getint(const dictionary * d, const char *section, const char * key, int notfound)
+int atalk_iniparser_getint(const dictionary * d, const char *section, const char * key, int notfound)
 {
     const char    *   str ;
 
-    str = iniparser_getstring(d, section, key, INI_INVALID_KEY);
+    str = atalk_iniparser_getstring(d, section, key, INI_INVALID_KEY);
     if (str==INI_INVALID_KEY) return notfound ;
     return (int)strtol(str, NULL, 0);
 }
@@ -323,11 +327,11 @@ int iniparser_getint(const dictionary * d, const char *section, const char * key
   the notfound value is returned.
  */
 /*--------------------------------------------------------------------------*/
-double iniparser_getdouble(const dictionary * d, const char *section, const char * key, double notfound)
+double atalk_iniparser_getdouble(const dictionary * d, const char *section, const char * key, double notfound)
 {
     const char    *   str ;
 
-    str = iniparser_getstring(d, section, key, INI_INVALID_KEY);
+    str = atalk_iniparser_getstring(d, section, key, INI_INVALID_KEY);
     if (str==INI_INVALID_KEY) return notfound ;
     return atof(str);
 }
@@ -365,12 +369,12 @@ double iniparser_getdouble(const dictionary * d, const char *section, const char
   necessarily have to be 0 or 1.
  */
 /*--------------------------------------------------------------------------*/
-int iniparser_getboolean(const dictionary * d, const char *section, const char * key, int notfound)
+int atalk_iniparser_getboolean(const dictionary * d, const char *section, const char * key, int notfound)
 {
     const char    *   c ;
     int         ret ;
 
-    c = iniparser_getstring(d, section, key, INI_INVALID_KEY);
+    c = atalk_iniparser_getstring(d, section, key, INI_INVALID_KEY);
     if (c==INI_INVALID_KEY) return notfound ;
     if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
         ret = 1 ;
@@ -394,10 +398,10 @@ int iniparser_getboolean(const dictionary * d, const char *section, const char *
   of querying for the presence of sections in a dictionary.
  */
 /*--------------------------------------------------------------------------*/
-int iniparser_find_entry(const dictionary *ini, const char *entry)
+int atalk_iniparser_find_entry(const dictionary *ini, const char *entry)
 {
     int found=0 ;
-    if (iniparser_getstring(ini, entry, NULL, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+    if (atalk_iniparser_getstring(ini, entry, NULL, INI_INVALID_KEY)!=INI_INVALID_KEY) {
         found = 1 ;
     }
     return found ;
@@ -417,9 +421,9 @@ int iniparser_find_entry(const dictionary *ini, const char *entry)
   It is Ok to set val to NULL.
  */
 /*--------------------------------------------------------------------------*/
-int iniparser_set(dictionary * ini, char *section, char * key, char * val)
+int atalk_iniparser_set(dictionary * ini, char *section, char * key, char * val)
 {
-    return dictionary_set(ini, section, key, val) ;
+    return atalkdict_set(ini, section, key, val) ;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -433,9 +437,9 @@ int iniparser_set(dictionary * ini, char *section, char * key, char * val)
   If the given entry can be found, it is deleted from the dictionary.
  */
 /*--------------------------------------------------------------------------*/
-void iniparser_unset(dictionary * ini, char *section, char * key)
+void atalk_iniparser_unset(dictionary * ini, char *section, char * key)
 {
-    dictionary_unset(ini, section, key);
+    atalkdict_unset(ini, section, key);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -448,7 +452,7 @@ void iniparser_unset(dictionary * ini, char *section, char * key)
   @return   line_status value
  */
 /*--------------------------------------------------------------------------*/
-static line_status iniparser_line(
+static line_status atalk_iniparser_line(
     char * input_line,
     char * section,
     char * key,
@@ -474,8 +478,7 @@ static line_status iniparser_line(
         strcpy(section, strstrip(section));
         strcpy(section, section);
         sta = LINE_SECTION ;
-    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
-           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
+    } else if (sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
            ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
         /* Usual key=value, with or without comments */
         strcpy(key, strstrip(key));
@@ -519,17 +522,16 @@ static line_status iniparser_line(
   should not be accessed directly, but through accessor functions
   instead.
 
-  The returned dictionary must be freed using iniparser_freedict().
+  The returned dictionary must be freed using atalk_iniparser_freedict().
  */
 /*--------------------------------------------------------------------------*/
-dictionary * iniparser_load(const char * ininame)
+dictionary * atalk_iniparser_load(const char * ininame)
 {
     FILE *in, *include = NULL, *inifile;
 
     char line    [ASCIILINESZ+1] ;
     char section [ASCIILINESZ+1] ;
     char key     [ASCIILINESZ+1] ;
-    char tmp     [ASCIILINESZ+1] ;
     char val     [ASCIILINESZ+1] ;
 
     int  last=0 ;
@@ -544,7 +546,7 @@ dictionary * iniparser_load(const char * ininame)
         return NULL ;
     }
 
-    dict = dictionary_new(0) ;
+    dict = atalkdict_new(0) ;
     if (!dict) {
         fclose(inifile);
         return NULL ;
@@ -571,14 +573,6 @@ dictionary * iniparser_load(const char * ininame)
         len = (int)strlen(line)-1;
         if (len==0)
             continue;
-        /* Safety check against buffer overflows */
-        if (line[len]!='\n') {
-            LOG(log_error, logtype_default, "iniparser: input line too long in \"%s\" (lineno: %d)",
-                ininame, lineno);
-            dictionary_del(dict);
-            fclose(in);
-            return NULL ;
-        }
         /* Get rid of \n and spaces at end of line */
         while ((len>=0) &&
                 ((line[len]=='\n') || (isspace(line[len])))) {
@@ -593,14 +587,14 @@ dictionary * iniparser_load(const char * ininame)
         } else {
             last=0 ;
         }
-        switch (iniparser_line(line, section, key, val)) {
+        switch (atalk_iniparser_line(line, section, key, val)) {
         case LINE_EMPTY:
         case LINE_COMMENT:
             break ;
         case LINE_SECTION:
             if (strchr(section, ':') != NULL)
                 LOG(log_error, logtype_default, "iniparser: syntax error \"%s\" section name must not contain \":\".", section);
-            errs = dictionary_set(dict, section, NULL, NULL);
+            errs = atalkdict_set(dict, section, NULL, NULL);
             break ;
         case LINE_VALUE:
             if (strcmp(key, "include") == 0) {
@@ -611,7 +605,7 @@ dictionary * iniparser_load(const char * ininame)
                 in = include;
                 continue;
             }
-            errs = dictionary_set(dict, section, key, val) ;
+            errs = atalkdict_set(dict, section, key, val) ;
             break ;
         case LINE_ERROR:
             LOG(log_error, logtype_default, "iniparser: syntax error in %s (lineno: %d): %s",
@@ -629,7 +623,7 @@ dictionary * iniparser_load(const char * ininame)
         }
     }
     if (errs) {
-        dictionary_del(dict);
+        atalkdict_del(dict);
         dict = NULL ;
     }
     fclose(in);
@@ -647,8 +641,8 @@ dictionary * iniparser_load(const char * ininame)
   gets out of the current context.
  */
 /*--------------------------------------------------------------------------*/
-void iniparser_freedict(dictionary * d)
+void atalk_iniparser_freedict(dictionary * d)
 {
-    dictionary_del(d);
+    atalkdict_del(d);
 }
 
diff --git a/libatalk/libatalk-3.0.1dev.abi b/libatalk/libatalk-3.0.1dev.abi
new file mode 100644 (file)
index 0000000..f671fca
--- /dev/null
@@ -0,0 +1,554 @@
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_getsession: int (DSI *, server_child *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_client_uds: int (const char *)
+ipc_server_read: int (server_child *, int)
+ipc_server_uds: int (const char *)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+lchdir: int (const char *)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = -1}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *, void (*)(const AFPObj *, struct vol *))
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+lstatat: int (int, const char *, struct stat *)
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+reconnect_ipc: int (AFPObj *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child *, int, pid_t, int)
+server_child_alloc: server_child *(const int, const int)
+server_child_free: void (server_child *)
+server_child_kill: void (server_child *, int, int)
+server_child_kill_one_by_id: void (server_child *, int, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child *, const int, pid_t)
+server_child_setup: void (server_child *, const int, void (*)(const pid_t))
+server_child_transfer_session: int (server_child *, int, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const char *, mode_t, struct stat *, mode_t)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.2.abi b/libatalk/libatalk-3.0.2.abi
new file mode 100644 (file)
index 0000000..8e73229
--- /dev/null
@@ -0,0 +1,565 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_client_uds: int (const char *)
+ipc_server_read: int (server_child *, int)
+ipc_server_uds: int (const char *)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+reconnect_ipc: int (AFPObj *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child *, int, pid_t, int)
+server_child_alloc: server_child *(const int, const int)
+server_child_free: void (server_child *)
+server_child_kill: void (server_child *, int, int)
+server_child_kill_one_by_id: void (server_child *, int, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child *, const int, pid_t)
+server_child_setup: void (server_child *, const int, void (*)(const pid_t))
+server_child_transfer_session: int (server_child *, int, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.3.abi b/libatalk/libatalk-3.0.3.abi
new file mode 100644 (file)
index 0000000..17e9539
--- /dev/null
@@ -0,0 +1,566 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.4.abi b/libatalk/libatalk-3.0.4.abi
new file mode 100644 (file)
index 0000000..33e4f92
--- /dev/null
@@ -0,0 +1,566 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.5.abi b/libatalk/libatalk-3.0.5.abi
new file mode 100644 (file)
index 0000000..2425dc6
--- /dev/null
@@ -0,0 +1,568 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.6.abi b/libatalk/libatalk-3.0.6.abi
new file mode 100644 (file)
index 0000000..57fa789
--- /dev/null
@@ -0,0 +1,570 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupfilter: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap user filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap group filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userfilter: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.1.0.abi b/libatalk/libatalk-3.1.0.abi
new file mode 100644 (file)
index 0000000..ebbfd6c
--- /dev/null
@@ -0,0 +1,651 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_recvfile: ssize_t (struct adouble *, int, int, off_t, size_t, int)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = <cnid_dbd_open>, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_for_path: cnid_t (struct _cnid_db *, const char *, const char *, cnid_t *)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = <cnid_last_open>, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_mysql_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_mysql_close: void (struct _cnid_db *)
+cnid_mysql_delete: int (struct _cnid_db *, const cnid_t)
+cnid_mysql_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_mysql_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_mysql_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_mysql_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_mysql_module: {name = "mysql", db_list = {next = 0x0, prev = 0x0}, cnid_open = <cnid_mysql_open>, flags = 0}
+cnid_mysql_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_mysql_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_mysql_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_mysql_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_mysql_wipe: int (struct _cnid_db *)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *, const void *, char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = <cnid_tdb_open>, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+dalloc_add_talloc_chunk: int (DALLOC_CTX *, void *, void *, size_t)
+dalloc_get: void *(const DALLOC_CTX *, ...)
+dalloc_size: int (DALLOC_CTX *)
+dalloc_strdup: char *(const void *, const char *)
+dalloc_strndup: char *(const void *, const char *, size_t)
+dalloc_value_for_key: void *(const DALLOC_CTX *, ...)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupfilter: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap user filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap group filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userfilter: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\000' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+_talloc: void *(const void *, size_t)
+_talloc_array: void *(const void *, size_t, unsigned int, const char *)
+talloc_asprintf: char *(const void *, const char *, ...)
+talloc_asprintf_append: char *(char *, const char *, ...)
+talloc_asprintf_append_buffer: char *(char *, const char *, ...)
+talloc_autofree_context: void *(void)
+talloc_check_name: void *(const void *, const char *)
+talloc_disable_null_tracking: void (void)
+talloc_enable_leak_report: void (void)
+talloc_enable_leak_report_full: void (void)
+talloc_enable_null_tracking: void (void)
+talloc_enable_null_tracking_no_autofree: void (void)
+talloc_find_parent_byname: void *(const void *, const char *)
+_talloc_free: int (void *, const char *)
+talloc_free_children: void (void *)
+talloc_get_name: const char *(const void *)
+talloc_get_size: size_t (const void *)
+_talloc_get_type_abort: void *(const void *, const char *, const char *)
+talloc_increase_ref_count: int (const void *)
+talloc_init: void *(const char *, ...)
+talloc_is_parent: int (const void *, const void *)
+_talloc_memdup: void *(const void *, const void *, size_t, const char *)
+_talloc_move: void *(const void *, const void *)
+talloc_named: void *(const void *, size_t, const char *, ...)
+talloc_named_const: void *(const void *, size_t, const char *)
+talloc_parent: void *(const void *)
+talloc_parent_name: const char *(const void *)
+talloc_pool: void *(const void *, size_t)
+_talloc_realloc: void *(const void *, void *, size_t, const char *)
+_talloc_realloc_array: void *(const void *, void *, size_t, unsigned int, const char *)
+talloc_realloc_fn: void *(const void *, void *, size_t)
+talloc_reference_count: size_t (const void *)
+_talloc_reference_loc: void *(const void *, const void *, const char *)
+talloc_reparent: void *(const void *, const void *, const void *)
+talloc_report: void (const void *, FILE *)
+talloc_report_depth_cb: void (const void *, int, int, void (*)(const void *, int, int, int, void *), void *)
+talloc_report_depth_file: void (const void *, int, int, FILE *)
+talloc_report_full: void (const void *, FILE *)
+talloc_set_abort_fn: void (void (*)(const char *))
+_talloc_set_destructor: void (const void *, int (*)(void *))
+talloc_set_log_fn: void (void (*)(const char *))
+talloc_set_log_stderr: void (void)
+talloc_set_name: const char *(const void *, const char *, ...)
+talloc_set_name_const: void (const void *, const char *)
+talloc_show_parents: void (const void *, FILE *)
+_talloc_steal_loc: void *(const void *, const void *, const char *)
+talloc_strdup: char *(const void *, const char *)
+talloc_strdup_append: char *(char *, const char *)
+talloc_strdup_append_buffer: char *(char *, const char *)
+talloc_strndup: char *(const void *, const char *, size_t)
+talloc_strndup_append: char *(char *, const char *, size_t)
+talloc_strndup_append_buffer: char *(char *, const char *, size_t)
+talloc_total_blocks: size_t (const void *)
+talloc_total_size: size_t (const void *)
+talloc_unlink: int (const void *, void *)
+talloc_vasprintf: char *(const void *, const char *, struct __va_list_tag *)
+talloc_vasprintf_append: char *(char *, const char *, struct __va_list_tag *)
+talloc_vasprintf_append_buffer: char *(char *, const char *, struct __va_list_tag *)
+talloc_version_major: int (void)
+talloc_version_minor: int (void)
+_talloc_zero: void *(const void *, size_t, const char *)
+_talloc_zero_array: void *(const void *, size_t, unsigned int, const char *)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.1.1.abi b/libatalk/libatalk-3.1.1.abi
new file mode 100644 (file)
index 0000000..11fb887
--- /dev/null
@@ -0,0 +1,639 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_osx: int (struct adouble *, char *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_recvfile: ssize_t (struct adouble *, int, int, off_t, size_t, int)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, va_list)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_for_path: cnid_t (struct _cnid_db *, const char *, const char *, cnid_t *)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *, const void *, char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+dalloc_add_talloc_chunk: int (DALLOC_CTX *, void *, void *, size_t)
+dalloc_get: void *(const DALLOC_CTX *, ...)
+dalloc_size: int (DALLOC_CTX *)
+dalloc_strdup: char *(const void *, const char *)
+dalloc_strndup: char *(const void *, const char *, size_t)
+dalloc_value_for_key: void *(const DALLOC_CTX *, ...)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupfilter: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap user filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap group filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userfilter: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *, lv_flags_t)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\000' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+_talloc: void *(const void *, size_t)
+_talloc_array: void *(const void *, size_t, unsigned int, const char *)
+talloc_asprintf: char *(const void *, const char *, ...)
+talloc_asprintf_append: char *(char *, const char *, ...)
+talloc_asprintf_append_buffer: char *(char *, const char *, ...)
+talloc_autofree_context: void *(void)
+talloc_check_name: void *(const void *, const char *)
+talloc_disable_null_tracking: void (void)
+talloc_enable_leak_report: void (void)
+talloc_enable_leak_report_full: void (void)
+talloc_enable_null_tracking: void (void)
+talloc_enable_null_tracking_no_autofree: void (void)
+talloc_find_parent_byname: void *(const void *, const char *)
+_talloc_free: int (void *, const char *)
+talloc_free_children: void (void *)
+talloc_get_name: const char *(const void *)
+talloc_get_size: size_t (const void *)
+_talloc_get_type_abort: void *(const void *, const char *, const char *)
+talloc_increase_ref_count: int (const void *)
+talloc_init: void *(const char *, ...)
+talloc_is_parent: int (const void *, const void *)
+_talloc_memdup: void *(const void *, const void *, size_t, const char *)
+_talloc_move: void *(const void *, const void *)
+talloc_named: void *(const void *, size_t, const char *, ...)
+talloc_named_const: void *(const void *, size_t, const char *)
+talloc_parent: void *(const void *)
+talloc_parent_name: const char *(const void *)
+talloc_pool: void *(const void *, size_t)
+_talloc_realloc: void *(const void *, void *, size_t, const char *)
+_talloc_realloc_array: void *(const void *, void *, size_t, unsigned int, const char *)
+talloc_realloc_fn: void *(const void *, void *, size_t)
+talloc_reference_count: size_t (const void *)
+_talloc_reference_loc: void *(const void *, const void *, const char *)
+talloc_reparent: void *(const void *, const void *, const void *)
+talloc_report: void (const void *, FILE *)
+talloc_report_depth_cb: void (const void *, int, int, void (*)(const void *, int, int, int, void *), void *)
+talloc_report_depth_file: void (const void *, int, int, FILE *)
+talloc_report_full: void (const void *, FILE *)
+talloc_set_abort_fn: void (void (*)(const char *))
+_talloc_set_destructor: void (const void *, int (*)(void *))
+talloc_set_log_fn: void (void (*)(const char *))
+talloc_set_log_stderr: void (void)
+talloc_set_name: const char *(const void *, const char *, ...)
+talloc_set_name_const: void (const void *, const char *)
+talloc_show_parents: void (const void *, FILE *)
+_talloc_steal_loc: void *(const void *, const void *, const char *)
+talloc_strdup: char *(const void *, const char *)
+talloc_strdup_append: char *(char *, const char *)
+talloc_strdup_append_buffer: char *(char *, const char *)
+talloc_strndup: char *(const void *, const char *, size_t)
+talloc_strndup_append: char *(char *, const char *, size_t)
+talloc_strndup_append_buffer: char *(char *, const char *, size_t)
+talloc_total_blocks: size_t (const void *)
+talloc_total_size: size_t (const void *)
+talloc_unlink: int (const void *, void *)
+talloc_vasprintf: char *(const void *, const char *, va_list)
+talloc_vasprintf_append: char *(char *, const char *, va_list)
+talloc_vasprintf_append_buffer: char *(char *, const char *, va_list)
+talloc_version_major: int (void)
+talloc_version_minor: int (void)
+_talloc_zero: void *(const void *, size_t, const char *)
+_talloc_zero_array: void *(const void *, size_t, unsigned int, const char *)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.1.2.abi b/libatalk/libatalk-3.1.2.abi
new file mode 100644 (file)
index 0000000..4535bbc
--- /dev/null
@@ -0,0 +1,639 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_osx: int (struct adouble *, char *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_recvfile: ssize_t (struct adouble *, int, int, off_t, size_t, int)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, va_list)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_for_path: cnid_t (struct _cnid_db *, const char *, const char *, cnid_t *)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(struct vol *, char *, int)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+dalloc_add_talloc_chunk: int (DALLOC_CTX *, void *, void *, size_t)
+dalloc_get: void *(const DALLOC_CTX *, ...)
+dalloc_size: int (DALLOC_CTX *)
+dalloc_strdup: char *(const void *, const char *)
+dalloc_strndup: char *(const void *, const char *, size_t)
+dalloc_value_for_key: void *(const DALLOC_CTX *, ...)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupfilter: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap user filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap group filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userfilter: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *, lv_flags_t)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\000' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+_talloc: void *(const void *, size_t)
+_talloc_array: void *(const void *, size_t, unsigned int, const char *)
+talloc_asprintf: char *(const void *, const char *, ...)
+talloc_asprintf_append: char *(char *, const char *, ...)
+talloc_asprintf_append_buffer: char *(char *, const char *, ...)
+talloc_autofree_context: void *(void)
+talloc_check_name: void *(const void *, const char *)
+talloc_disable_null_tracking: void (void)
+talloc_enable_leak_report: void (void)
+talloc_enable_leak_report_full: void (void)
+talloc_enable_null_tracking: void (void)
+talloc_enable_null_tracking_no_autofree: void (void)
+talloc_find_parent_byname: void *(const void *, const char *)
+_talloc_free: int (void *, const char *)
+talloc_free_children: void (void *)
+talloc_get_name: const char *(const void *)
+talloc_get_size: size_t (const void *)
+_talloc_get_type_abort: void *(const void *, const char *, const char *)
+talloc_increase_ref_count: int (const void *)
+talloc_init: void *(const char *, ...)
+talloc_is_parent: int (const void *, const void *)
+_talloc_memdup: void *(const void *, const void *, size_t, const char *)
+_talloc_move: void *(const void *, const void *)
+talloc_named: void *(const void *, size_t, const char *, ...)
+talloc_named_const: void *(const void *, size_t, const char *)
+talloc_parent: void *(const void *)
+talloc_parent_name: const char *(const void *)
+talloc_pool: void *(const void *, size_t)
+_talloc_realloc: void *(const void *, void *, size_t, const char *)
+_talloc_realloc_array: void *(const void *, void *, size_t, unsigned int, const char *)
+talloc_realloc_fn: void *(const void *, void *, size_t)
+talloc_reference_count: size_t (const void *)
+_talloc_reference_loc: void *(const void *, const void *, const char *)
+talloc_reparent: void *(const void *, const void *, const void *)
+talloc_report: void (const void *, FILE *)
+talloc_report_depth_cb: void (const void *, int, int, void (*)(const void *, int, int, int, void *), void *)
+talloc_report_depth_file: void (const void *, int, int, FILE *)
+talloc_report_full: void (const void *, FILE *)
+talloc_set_abort_fn: void (void (*)(const char *))
+_talloc_set_destructor: void (const void *, int (*)(void *))
+talloc_set_log_fn: void (void (*)(const char *))
+talloc_set_log_stderr: void (void)
+talloc_set_name: const char *(const void *, const char *, ...)
+talloc_set_name_const: void (const void *, const char *)
+talloc_show_parents: void (const void *, FILE *)
+_talloc_steal_loc: void *(const void *, const void *, const char *)
+talloc_strdup: char *(const void *, const char *)
+talloc_strdup_append: char *(char *, const char *)
+talloc_strdup_append_buffer: char *(char *, const char *)
+talloc_strndup: char *(const void *, const char *, size_t)
+talloc_strndup_append: char *(char *, const char *, size_t)
+talloc_strndup_append_buffer: char *(char *, const char *, size_t)
+talloc_total_blocks: size_t (const void *)
+talloc_total_size: size_t (const void *)
+talloc_unlink: int (const void *, void *)
+talloc_vasprintf: char *(const void *, const char *, va_list)
+talloc_vasprintf_append: char *(char *, const char *, va_list)
+talloc_vasprintf_append_buffer: char *(char *, const char *, va_list)
+talloc_version_major: int (void)
+talloc_version_minor: int (void)
+_talloc_zero: void *(const void *, size_t, const char *)
+_talloc_zero_array: void *(const void *, size_t, unsigned int, const char *)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.1.3.abi b/libatalk/libatalk-3.1.3.abi
new file mode 100644 (file)
index 0000000..9cc45db
--- /dev/null
@@ -0,0 +1,634 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const unsigned long)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const unsigned long)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_offsets: int (struct adouble *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_osx: int (struct adouble *, char *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_recvfile: ssize_t (struct adouble *, int, int, off_t, size_t, int)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+apply_ip_mask: void (struct sockaddr *, int)
+atalkdict_del: void (dictionary *)
+atalkdict_dump: void (dictionary *, FILE *)
+atalkdict_get: const char *(const dictionary *, const char *, const char *, const char *)
+atalkdict_hash: unsigned int (char *)
+atalkdict_new: dictionary *(int)
+atalkdict_set: int (dictionary *, char *, char *, char *)
+atalkdict_unset: void (dictionary *, char *, char *)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_iniparser_dump: void (const dictionary *, FILE *)
+atalk_iniparser_dump_ini: void (const dictionary *, FILE *)
+atalk_iniparser_find_entry: int (const dictionary *, const char *)
+atalk_iniparser_freedict: void (dictionary *)
+atalk_iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+atalk_iniparser_getint: int (const dictionary *, const char *, const char *, int)
+atalk_iniparser_getnsec: int (const dictionary *)
+atalk_iniparser_getsecname: const char *(const dictionary *, int)
+atalk_iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+atalk_iniparser_load: dictionary *(const char *)
+atalk_iniparser_set: int (dictionary *, char *, char *, char *)
+atalk_iniparser_unset: void (dictionary *, char *, char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0x0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_for_path: cnid_t (struct _cnid_db *, const char *, const char *, cnid_t *)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0x0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_mysql_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_mysql_close: void (struct _cnid_db *)
+cnid_mysql_delete: int (struct _cnid_db *, const cnid_t)
+cnid_mysql_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_mysql_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_mysql_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_mysql_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_mysql_module: {name = "mysql", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0x0, flags = 0}
+cnid_mysql_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_mysql_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_mysql_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_mysql_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_mysql_wipe: int (struct _cnid_db *)
+cnid_open: struct _cnid_db *(struct vol *, char *, int)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0x0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+dalloc_add_talloc_chunk: int (DALLOC_CTX *, void *, void *, size_t)
+dalloc_get: void *(const DALLOC_CTX *, ...)
+dalloc_size: int (DALLOC_CTX *)
+dalloc_strdup: char *(const void *, const char *)
+dalloc_strndup: char *(const void *, const char *, size_t)
+dalloc_value_for_key: void *(const DALLOC_CTX *, ...)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+dequeue: void *(q_t *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupfilter: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap user filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap group filter", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userfilter: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *, lv_flags_t)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\000' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+talloc_asprintf: char *(const void *, const char *, ...)
+talloc_asprintf_append: char *(char *, const char *, ...)
+talloc_asprintf_append_buffer: char *(char *, const char *, ...)
+talloc_autofree_context: void *(void)
+talloc_check_name: void *(const void *, const char *)
+talloc_disable_null_tracking: void (void)
+talloc_enable_leak_report: void (void)
+talloc_enable_leak_report_full: void (void)
+talloc_enable_null_tracking: void (void)
+talloc_enable_null_tracking_no_autofree: void (void)
+talloc_find_parent_byname: void *(const void *, const char *)
+talloc_free_children: void (void *)
+talloc_get_name: const char *(const void *)
+talloc_get_size: size_t (const void *)
+talloc_increase_ref_count: int (const void *)
+talloc_init: void *(const char *, ...)
+talloc_is_parent: int (const void *, const void *)
+talloc_named: void *(const void *, size_t, const char *, ...)
+talloc_named_const: void *(const void *, size_t, const char *)
+talloc_parent: void *(const void *)
+talloc_parent_name: const char *(const void *)
+talloc_pool: void *(const void *, size_t)
+talloc_realloc_fn: void *(const void *, void *, size_t)
+talloc_reference_count: size_t (const void *)
+talloc_reparent: void *(const void *, const void *, const void *)
+talloc_report: void (const void *, FILE *)
+talloc_report_depth_cb: void (const void *, int, int, void (*)(const void *, int, int, int, void *), void *)
+talloc_report_depth_file: void (const void *, int, int, FILE *)
+talloc_report_full: void (const void *, FILE *)
+talloc_set_abort_fn: void (void (*)(const char *))
+talloc_set_log_fn: void (void (*)(const char *))
+talloc_set_log_stderr: void (void)
+talloc_set_name: const char *(const void *, const char *, ...)
+talloc_set_name_const: void (const void *, const char *)
+talloc_show_parents: void (const void *, FILE *)
+talloc_strdup: char *(const void *, const char *)
+talloc_strdup_append: char *(char *, const char *)
+talloc_strdup_append_buffer: char *(char *, const char *)
+talloc_strndup: char *(const void *, const char *, size_t)
+talloc_strndup_append: char *(char *, const char *, size_t)
+talloc_strndup_append_buffer: char *(char *, const char *, size_t)
+talloc_total_blocks: size_t (const void *)
+talloc_total_size: size_t (const void *)
+talloc_unlink: int (const void *, void *)
+talloc_vasprintf: char *(const void *, const char *, struct __va_list_tag *)
+talloc_vasprintf_append: char *(char *, const char *, struct __va_list_tag *)
+talloc_vasprintf_append_buffer: char *(char *, const char *, struct __va_list_tag *)
+talloc_version_major: int (void)
+talloc_version_minor: int (void)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tokenize_ip_port: int (const char *, char **, char **)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/talloc/Makefile.am b/libatalk/talloc/Makefile.am
new file mode 100644 (file)
index 0000000..8616eac
--- /dev/null
@@ -0,0 +1,4 @@
+# Makefile.am for libatalk/talloc/
+
+noinst_LTLIBRARIES = libtalloc.la
+libtalloc_la_SOURCES = talloc.c dalloc.c
diff --git a/libatalk/talloc/dalloc.c b/libatalk/talloc/dalloc.c
new file mode 100644 (file)
index 0000000..99dd7ec
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+  Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+  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
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+/*!
+  @file
+  Typesafe, dynamic object store based on talloc
+  Usage:
+
+  //
+  // Define some terminal types:
+  //
+
+  // A key/value store aka dictionary that supports retrieving elements by key
+  typedef dict_t DALLOC_CTX;
+
+  // An ordered set that can store different objects which can be retrieved by number
+  typedef set_t DALLOC_CTX;
+
+  //
+  // Create an dalloc object and add elementes of different type
+  //
+
+  // Allocate a new talloc context
+  TALLOC_CTX *mem_ctx = talloc_new(NULL);
+  // Create a new dalloc object
+  DALLOC_CTX *d = talloc_zero(mem_ctx, DALLOC_CTX);
+  // Store an int value in the object
+  uint64_t i = 1;
+  dalloc_add_copy(d, &i, uint64_t);
+  // Store a string
+  char *str = dalloc_strdup(d, "hello world");
+  dalloc_add(d, str, char *);
+  // Add a nested object, you later can't fetch this directly
+  DALLOC_CTX *nested = talloc_zero(d, DALLOC_CTX);
+  dalloc_add(d, nested, DALLOC_CTX);
+
+  // Add an int value to the nested object, this can be fetched
+  i = 2;
+  dalloc_add_copy(nested, &i, uint64_t);
+
+  // Add a nested set
+  set_t *set = talloc_zero(nested, set_t);
+  dalloc_add(nested, set, set_t);
+  // Add an int value to the set
+  i = 3;
+  dalloc_add_copy(set, &i, uint64_t);
+
+  // Add a dictionary (key/value store)
+  dict_t *dict = talloc_zero(nested, dict_t);
+  dalloc_add(nested, dict, dict_t);
+
+  // Store a string as key in the dict
+  str = dalloc_strdup(d, "key");
+  dalloc_add(dict, str, char *);
+
+  // Add a value for the key
+  i = 4;
+  dalloc_add_copy(dict, &i, uint64_t);
+
+  //
+  // Fetching value references
+  // You can fetch anything that is not a DALLOC_CTXs, because passing
+  // "DALLOC_CTXs" as type to the functions dalloc_get() and dalloc_value_for_key()
+  // tells the function to step into that object and expect more arguments that specify
+  // which element to fetch.
+  //
+
+  // Get reference to an objects element by position
+  uint64_t *p = dalloc_get(d, "uint64_t", 0);
+  // p now points to the first int with a value of 1
+
+  // Get reference to the "hello world" string
+  str = dalloc_get(d, "char *", 1);
+
+  // You can't fetch a pure DALLOC_CTX
+  nested = dalloc_get(d, "DALLOC_CTX", 2);
+  // But you can do this
+  p = dalloc_get(d, "DALLOC_CTX", 2, "uint64_t", 0);
+  // p now points to the value 2
+
+  // You can fetch types that are typedefd DALLOC_CTXs
+  set = dalloc_get(d, "DALLOC_CTX", 2, "set_t", 1);
+
+  // Fetch int from set, note that you must use DALLOC_CTX as type for the set
+  p = dalloc_get(d, "DALLOC_CTX", 2, "DALLOC_CTX", 1, "uint64_t", 0);
+  // p points to 3
+
+  // Fetch value by key from dictionary
+  p = dalloc_value_for_key(d, "DALLOC_CTX", 2, "DALLOC_CTX", 2, "key");
+  // p now point to 4
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/talloc.h>
+#include <atalk/bstrlib.h>
+#include <atalk/dalloc.h>
+
+/* Use dalloc_add_copy() macro, not this function */
+int dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *talloc_chunk, void *obj, size_t size)
+{
+    if (talloc_chunk) {
+        /* Called from dalloc_add_copy() macro */
+        dd->dd_talloc_array = talloc_realloc(dd,
+                                             dd->dd_talloc_array,
+                                             void *,
+                                             talloc_array_length(dd->dd_talloc_array) + 1);
+        memcpy(talloc_chunk, obj, size);
+        dd->dd_talloc_array[talloc_array_length(dd->dd_talloc_array) - 1] = talloc_chunk;
+    } else {
+        /* Called from dalloc_add() macro */
+        dd->dd_talloc_array = talloc_realloc(dd,
+                                             dd->dd_talloc_array,
+                                             void *,
+                                             talloc_array_length(dd->dd_talloc_array) + 1);
+        dd->dd_talloc_array[talloc_array_length(dd->dd_talloc_array) - 1] = obj;
+
+    }
+    return 0;
+}
+
+/* Get number of elements, returns 0 if the structure is empty or not initialized */
+int dalloc_size(DALLOC_CTX *d)
+{
+    if (!d || !d->dd_talloc_array)
+        return 0;
+    return talloc_array_length(d->dd_talloc_array);
+}
+
+/*
+ * Get pointer to value from a DALLOC object
+ *
+ * Returns pointer to object from a DALLOC object. Nested object interation
+ * is supported by using the type string "DALLOC_CTX". Any other type string
+ * designates the requested objects type.
+ */
+void *dalloc_get(const DALLOC_CTX *d, ...)
+{
+    EC_INIT;
+    void *p = NULL;
+    va_list args;
+    const char *type;
+    int elem;
+    const char *elemtype;
+
+    va_start(args, d);
+    type = va_arg(args, const char *);
+
+    while (STRCMP(type, ==, "DALLOC_CTX")) {
+        elem = va_arg(args, int);
+        if (elem >= talloc_array_length(d->dd_talloc_array)) {
+            LOG(log_error, logtype_sl, "dalloc_get(%s): bound check error: %d >= %d",
+                type, elem >= talloc_array_length(d->dd_talloc_array));
+            EC_FAIL;
+        }
+        d = d->dd_talloc_array[elem];
+        type = va_arg(args, const char *);
+    }
+
+    elem = va_arg(args, int);
+    if (elem >= talloc_array_length(d->dd_talloc_array)) {
+        LOG(log_error, logtype_sl, "dalloc_get(%s): bound check error: %d >= %d",
+            type, elem,  talloc_array_length(d->dd_talloc_array));
+        EC_FAIL;
+    }
+
+    if (!(p = talloc_check_name(d->dd_talloc_array[elem], type))) {
+        LOG(log_error, logtype_sl, "dalloc_get(%d/%s): type mismatch: %s",
+            type, elem, talloc_get_name(d->dd_talloc_array[elem]));
+    }
+
+    va_end(args);
+
+EC_CLEANUP:
+    if (ret != 0)
+        p = NULL;
+    return p;
+}
+
+void *dalloc_value_for_key(const DALLOC_CTX *d, ...)
+{
+    EC_INIT;
+    void *p = NULL;
+    va_list args;
+    const char *type;
+    int elem;
+    const char *elemtype;
+    char *s;
+
+    va_start(args, d);
+    type = va_arg(args, const char *);
+
+    while (STRCMP(type, ==, "DALLOC_CTX")) {
+        elem = va_arg(args, int);
+        AFP_ASSERT(elem < talloc_array_length(d->dd_talloc_array));
+        d = d->dd_talloc_array[elem];
+        type = va_arg(args, const char *);
+    }
+
+    for (elem = 0; elem + 1 < talloc_array_length(d->dd_talloc_array); elem += 2) {
+        if (STRCMP(talloc_get_name(d->dd_talloc_array[elem]), !=, "char *")) {
+            LOG(log_error, logtype_default, "dalloc_value_for_key: key not a string: %s",
+                talloc_get_name(d->dd_talloc_array[elem]));
+            EC_FAIL;
+        }
+        if (STRCMP((char *)d->dd_talloc_array[elem], ==, type)) {
+            p = d->dd_talloc_array[elem + 1];
+            break;
+        }            
+    }
+    va_end(args);
+
+EC_CLEANUP:
+    if (ret != 0)
+        p = NULL;
+    return p;
+}
+
+char *dalloc_strdup(const void *ctx, const char *string)
+{
+    EC_INIT;
+    char *p;
+
+    EC_NULL( p = talloc_strdup(ctx, string) );
+    talloc_set_name(p, "char *");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (p)
+            talloc_free(p);
+        p = NULL;
+    }
+    return p;
+}
+
+char *dalloc_strndup(const void *ctx, const char *string, size_t n)
+{
+    EC_INIT;
+    char *p;
+
+    EC_NULL( p = talloc_strndup(ctx, string, n) );
+    talloc_set_name(p, "char *");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (p)
+            talloc_free(p);
+        p = NULL;
+    }
+    return p;
+}
diff --git a/libatalk/talloc/talloc.c b/libatalk/talloc/talloc.c
new file mode 100644 (file)
index 0000000..78dffa8
--- /dev/null
@@ -0,0 +1,2379 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+
+   Samba trivial allocation library - new interface
+
+   NOTE: Please read talloc_guide.txt for full documentation
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  inspired by http://swapped.cc/halloc/
+*/
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <atalk/talloc.h>
+
+#define _PUBLIC_ extern
+
+/** 
+ * pointer difference macro 
+ */
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+
+
+#ifdef TALLOC_BUILD_VERSION_MAJOR
+#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
+#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR"
+#endif
+#endif
+
+#ifdef TALLOC_BUILD_VERSION_MINOR
+#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR)
+#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR"
+#endif
+#endif
+
+/* Special macros that are no-ops except when run under Valgrind on
+ * x86.  They've moved a little bit from valgrind 1.0.4 to 1.9.4 */
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+        /* memcheck.h includes valgrind.h */
+#include <valgrind/memcheck.h>
+#elif defined(HAVE_VALGRIND_H)
+#include <valgrind.h>
+#endif
+
+/* use this to force every realloc to change the pointer, to stress test
+   code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC_BASE 0xe814ec70
+#define TALLOC_MAGIC ( \
+       TALLOC_MAGIC_BASE + \
+       (TALLOC_VERSION_MAJOR << 12) + \
+       (TALLOC_VERSION_MINOR << 4) \
+)
+
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04          /* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08       /* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called 
+   on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+   as its first argument */
+#ifndef likely
+#define likely(x)   __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+   talloc_enable_leak_report_full() is called, otherwise it remains
+   NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+/* used to enable fill of memory on free, which can be useful for
+ * catching use after free errors when valgrind is too slow
+ */
+static struct {
+       bool initialised;
+       bool enabled;
+       uint8_t fill_value;
+} talloc_fill;
+
+#define TALLOC_FILL_ENV "TALLOC_FREE_FILL"
+
+/*
+ * do not wipe the header, to allow the
+ * double-free logic to still work
+ */
+#define TC_INVALIDATE_FULL_FILL_CHUNK(_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size; \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole chunk as not accessable */
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { \
+       size_t _flen = TC_HDR_SIZE + (_tc)->size; \
+       char *_fptr = (char *)(_tc); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_FULL_CHUNK(_tc) do { \
+       TC_INVALIDATE_FULL_FILL_CHUNK(_tc); \
+       TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc); \
+} while (0)
+
+#define TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the unused bytes not accessable */
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while (0)
+#else
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#define TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the unused bytes as undefined */
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the new bytes as undefined */
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _old_used = TC_HDR_SIZE + (_tc)->size; \
+       size_t _new_used = TC_HDR_SIZE + (_new_size); \
+       size_t _flen = _new_used - _old_used; \
+       char *_fptr = _old_used + (char *)(_tc); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_GROW_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+struct talloc_reference_handle {
+       struct talloc_reference_handle *next, *prev;
+       void *ptr;
+       const char *location;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+       struct talloc_chunk *next, *prev;
+       struct talloc_chunk *parent, *child;
+       struct talloc_reference_handle *refs;
+       talloc_destructor_t destructor;
+       const char *name;
+       size_t size;
+       unsigned flags;
+
+       /*
+        * "pool" has dual use:
+        *
+        * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+        * marks the end of the currently allocated area.
+        *
+        * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+        * is a pointer to the struct talloc_chunk of the pool that it was
+        * allocated from. This way children can quickly find the pool to chew
+        * from.
+        */
+       void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_ALIGN16(s) (((s)+15)&~15)
+#define TC_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_chunk))
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+_PUBLIC_ int talloc_version_major(void)
+{
+       return TALLOC_VERSION_MAJOR;
+}
+
+_PUBLIC_ int talloc_version_minor(void)
+{
+       return TALLOC_VERSION_MINOR;
+}
+
+static void (*talloc_log_fn)(const char *message);
+
+_PUBLIC_ void talloc_set_log_fn(void (*log_fn)(const char *message))
+{
+       talloc_log_fn = log_fn;
+}
+
+static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+static void talloc_log(const char *fmt, ...)
+{
+       va_list ap;
+       char *message;
+
+       if (!talloc_log_fn) {
+               return;
+       }
+
+       va_start(ap, fmt);
+       message = talloc_vasprintf(NULL, fmt, ap);
+       va_end(ap);
+
+       talloc_log_fn(message);
+       talloc_free(message);
+}
+
+static void talloc_log_stderr(const char *message)
+{
+       fprintf(stderr, "%s", message);
+}
+
+_PUBLIC_ void talloc_set_log_stderr(void)
+{
+       talloc_set_log_fn(talloc_log_stderr);
+}
+
+static void (*talloc_abort_fn)(const char *reason);
+
+_PUBLIC_ void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
+{
+       talloc_abort_fn = abort_fn;
+}
+
+static void talloc_abort(const char *reason)
+{
+       talloc_log("%s\n", reason);
+
+       if (!talloc_abort_fn) {
+               TALLOC_ABORT(reason);
+       }
+
+       talloc_abort_fn(reason);
+}
+
+static void talloc_abort_magic(unsigned magic)
+{
+       unsigned striped = magic - TALLOC_MAGIC_BASE;
+       unsigned major = (striped & 0xFFFFF000) >> 12;
+       unsigned minor = (striped & 0x00000FF0) >> 4;
+       talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n",
+                  magic, major, minor,
+                  TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR);
+       talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
+}
+
+static void talloc_abort_access_after_free(void)
+{
+       talloc_abort("Bad talloc magic value - access after free");
+}
+
+static void talloc_abort_unknown_value(void)
+{
+       talloc_abort("Bad talloc magic value - unknown value");
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+       const char *pp = (const char *)ptr;
+       struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+       if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
+               if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) {
+                       talloc_abort_magic(tc->flags & (~0xF));
+                       return NULL;
+               }
+
+               if (tc->flags & TALLOC_FLAG_FREE) {
+                       talloc_log("talloc: access after free error - first free may be at %s\n", tc->name);
+                       talloc_abort_access_after_free();
+                       return NULL;
+               } else {
+                       talloc_abort_unknown_value();
+                       return NULL;
+               }
+       }
+       return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (list) = (p); \
+               (p)->next = (p)->prev = NULL; \
+       } else { \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (p)->prev = NULL; \
+               (list) = (p); \
+       }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               (list) = (p)->next; \
+               if (list) (list)->prev = NULL; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+  return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+       while (tc->prev) tc=tc->prev;
+
+       return tc->parent;
+}
+
+_PUBLIC_ void *talloc_parent(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+       return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+  find parents name
+*/
+_PUBLIC_ const char *talloc_parent_name(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+       return tc? tc->name : NULL;
+}
+
+/*
+  A pool carries an in-pool object count count in the first 16 bytes.
+  bytes. This is done to support talloc_steal() to a parent outside of the
+  pool. The count includes the pool itself, so a talloc_free() on a pool will
+  only destroy the pool if the count has dropped to zero. A talloc_free() of a
+  pool member will reduce the count, and eventually also call free(3) on the
+  pool memory.
+
+  The object count is not put into "struct talloc_chunk" because it is only
+  relevant for talloc pools and the alignment to 16 bytes would increase the
+  memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+#define TC_POOL_SPACE_LEFT(_pool_tc) \
+       PTR_DIFF(TC_HDR_SIZE + (_pool_tc)->size + (char *)(_pool_tc), \
+                (_pool_tc)->pool)
+
+#define TC_POOL_FIRST_CHUNK(_pool_tc) \
+       ((void *)(TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE + (char *)(_pool_tc)))
+
+#define TC_POOLMEM_CHUNK_SIZE(_tc) \
+       TC_ALIGN16(TC_HDR_SIZE + (_tc)->size)
+
+#define TC_POOLMEM_NEXT_CHUNK(_tc) \
+       ((void *)(TC_POOLMEM_CHUNK_SIZE(tc) + (char*)(_tc)))
+
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_FILL_POOL(_pool_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+               char *_fptr = (char *)(_pool_tc)->pool; \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while(0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { \
+       size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+       char *_fptr = (char *)(_pool_tc)->pool; \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_POOL(_pool_tc) do { \
+       TC_INVALIDATE_FILL_POOL(_pool_tc); \
+       TC_INVALIDATE_VALGRIND_POOL(_pool_tc); \
+} while (0)
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+       return (unsigned int *)((char *)tc + TC_HDR_SIZE);
+}
+
+/*
+  Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+                                             size_t size)
+{
+       struct talloc_chunk *pool_ctx = NULL;
+       size_t space_left;
+       struct talloc_chunk *result;
+       size_t chunk_size;
+
+       if (parent == NULL) {
+               return NULL;
+       }
+
+       if (parent->flags & TALLOC_FLAG_POOL) {
+               pool_ctx = parent;
+       }
+       else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+               pool_ctx = (struct talloc_chunk *)parent->pool;
+       }
+
+       if (pool_ctx == NULL) {
+               return NULL;
+       }
+
+       space_left = TC_POOL_SPACE_LEFT(pool_ctx);
+
+       /*
+        * Align size to 16 bytes
+        */
+       chunk_size = TC_ALIGN16(size);
+
+       if (space_left < chunk_size) {
+               return NULL;
+       }
+
+       result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+       VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+       pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+       result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+       result->pool = pool_ctx;
+
+       *talloc_pool_objectcount(pool_ctx) += 1;
+
+       return result;
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+       struct talloc_chunk *tc = NULL;
+
+       if (unlikely(context == NULL)) {
+               context = null_context;
+       }
+
+       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+               return NULL;
+       }
+
+       if (context != NULL) {
+               tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+                                      TC_HDR_SIZE+size);
+       }
+
+       if (tc == NULL) {
+               tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+               if (unlikely(tc == NULL)) return NULL;
+               tc->flags = TALLOC_MAGIC;
+               tc->pool  = NULL;
+       }
+
+       tc->size = size;
+       tc->destructor = NULL;
+       tc->child = NULL;
+       tc->name = NULL;
+       tc->refs = NULL;
+
+       if (likely(context)) {
+               struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+               if (parent->child) {
+                       parent->child->parent = NULL;
+                       tc->next = parent->child;
+                       tc->next->prev = tc;
+               } else {
+                       tc->next = NULL;
+               }
+               tc->parent = parent;
+               tc->prev = NULL;
+               parent->child = tc;
+       } else {
+               tc->next = tc->prev = tc->parent = NULL;
+       }
+
+       return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+_PUBLIC_ void *talloc_pool(const void *context, size_t size)
+{
+       void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+       struct talloc_chunk *tc;
+
+       if (unlikely(result == NULL)) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(result);
+
+       tc->flags |= TALLOC_FLAG_POOL;
+       tc->pool = TC_POOL_FIRST_CHUNK(tc);
+
+       *talloc_pool_objectcount(tc) = 1;
+
+       TC_INVALIDATE_POOL(tc);
+
+       return result;
+}
+
+/*
+  setup a destructor to be called on free of a pointer
+  the destructor should return 0 on success, or -1 on failure.
+  if the destructor fails then the free is failed, and the memory can
+  be continued to be used
+*/
+_PUBLIC_ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->destructor = destructor;
+}
+
+/*
+  increase the reference count on a piece of memory. 
+*/
+_PUBLIC_ int talloc_increase_ref_count(const void *ptr)
+{
+       if (unlikely(!talloc_reference(null_context, ptr))) {
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  helper for talloc_reference()
+
+  this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+       struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+       _TLIST_REMOVE(ptr_tc->refs, handle);
+       return 0;
+}
+
+/*
+   more efficient way to add a name to a pointer - the name must point to a 
+   true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->name = name;
+}
+
+/*
+  internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+       void *ptr;
+
+       ptr = __talloc(context, size);
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       _talloc_set_name_const(ptr, name);
+
+       return ptr;
+}
+
+/*
+  make a secondary reference to a pointer, hanging off the given context.
+  the pointer remains valid until both the original caller and this given
+  context are freed.
+  
+  the major use for this is when two different structures need to reference the 
+  same underlying data, and you want to be able to free the two instances separately,
+  and in either order
+*/
+_PUBLIC_ void *_talloc_reference_loc(const void *context, const void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+       struct talloc_reference_handle *handle;
+       if (unlikely(ptr == NULL)) return NULL;
+
+       tc = talloc_chunk_from_ptr(ptr);
+       handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+                                                  sizeof(struct talloc_reference_handle),
+                                                  TALLOC_MAGIC_REFERENCE);
+       if (unlikely(handle == NULL)) return NULL;
+
+       /* note that we hang the destructor off the handle, not the
+          main context as that allows the caller to still setup their
+          own destructor on the context if they want to */
+       talloc_set_destructor(handle, talloc_reference_destructor);
+       handle->ptr = discard_const_p(void, ptr);
+       handle->location = location;
+       _TLIST_ADD(tc->refs, handle);
+       return handle->ptr;
+}
+
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
+
+static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
+                                       const char *location)
+{
+       struct talloc_chunk *pool;
+       void *next_tc;
+       unsigned int *pool_object_count;
+
+       pool = (struct talloc_chunk *)tc->pool;
+       next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+
+       tc->flags |= TALLOC_FLAG_FREE;
+
+       /* we mark the freed memory with where we called the free
+        * from. This means on a double free error we can report where
+        * the first free came from
+        */
+       tc->name = location;
+
+       TC_INVALIDATE_FULL_CHUNK(tc);
+
+       pool_object_count = talloc_pool_objectcount(pool);
+
+       if (unlikely(*pool_object_count == 0)) {
+               talloc_abort("Pool object count zero!");
+               return;
+       }
+
+       *pool_object_count -= 1;
+
+       if (unlikely(*pool_object_count == 1 && !(pool->flags & TALLOC_FLAG_FREE))) {
+               /*
+                * if there is just one object left in the pool
+                * and pool->flags does not have TALLOC_FLAG_FREE,
+                * it means this is the pool itself and
+                * the rest is available for new objects
+                * again.
+                */
+               pool->pool = TC_POOL_FIRST_CHUNK(pool);
+               TC_INVALIDATE_POOL(pool);
+       } else if (unlikely(*pool_object_count == 0)) {
+               /*
+                * we mark the freed memory with where we called the free
+                * from. This means on a double free error we can report where
+                * the first free came from
+                */
+               pool->name = location;
+
+               TC_INVALIDATE_FULL_CHUNK(pool);
+               free(pool);
+       } else if (pool->pool == next_tc) {
+               /*
+                * if pool->pool still points to end of
+                * 'tc' (which is stored in the 'next_tc' variable),
+                * we can reclaim the memory of 'tc'.
+                */
+               pool->pool = tc;
+       }
+}
+
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location);
+
+/* 
+   internal talloc_free call
+*/
+static inline int _talloc_free_internal(void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return -1;
+       }
+
+       /* possibly initialised the talloc fill value */
+       if (unlikely(!talloc_fill.initialised)) {
+               const char *fill = getenv(TALLOC_FILL_ENV);
+               if (fill != NULL) {
+                       talloc_fill.enabled = true;
+                       talloc_fill.fill_value = strtoul(fill, NULL, 0);
+               }
+               talloc_fill.initialised = true;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (unlikely(tc->refs)) {
+               int is_child;
+               /* check if this is a reference from a child or
+                * grandchild back to it's parent or grandparent
+                *
+                * in that case we need to remove the reference and
+                * call another instance of talloc_free() on the current
+                * pointer.
+                */
+               is_child = talloc_is_parent(tc->refs, ptr);
+               _talloc_free_internal(tc->refs, location);
+               if (is_child) {
+                       return _talloc_free_internal(ptr, location);
+               }
+               return -1;
+       }
+
+       if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+               /* we have a free loop - stop looping */
+               return 0;
+       }
+
+       if (unlikely(tc->destructor)) {
+               talloc_destructor_t d = tc->destructor;
+               if (d == (talloc_destructor_t)-1) {
+                       return -1;
+               }
+               tc->destructor = (talloc_destructor_t)-1;
+               if (d(ptr) == -1) {
+                       tc->destructor = d;
+                       return -1;
+               }
+               tc->destructor = NULL;
+       }
+
+       if (tc->parent) {
+               _TLIST_REMOVE(tc->parent->child, tc);
+               if (tc->parent->child) {
+                       tc->parent->child->parent = tc->parent;
+               }
+       } else {
+               if (tc->prev) tc->prev->next = tc->next;
+               if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       _talloc_free_children_internal(tc, ptr, location);
+
+       tc->flags |= TALLOC_FLAG_FREE;
+
+       /* we mark the freed memory with where we called the free
+        * from. This means on a double free error we can report where
+        * the first free came from 
+        */      
+       tc->name = location;
+
+       if (tc->flags & TALLOC_FLAG_POOL) {
+               unsigned int *pool_object_count;
+
+               pool_object_count = talloc_pool_objectcount(tc);
+
+               if (unlikely(*pool_object_count == 0)) {
+                       talloc_abort("Pool object count zero!");
+                       return 0;
+               }
+
+               *pool_object_count -= 1;
+
+               if (unlikely(*pool_object_count == 0)) {
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+                       free(tc);
+               }
+       } else if (tc->flags & TALLOC_FLAG_POOLMEM) {
+               _talloc_free_poolmem(tc, location);
+       } else {
+               TC_INVALIDATE_FULL_CHUNK(tc);
+               free(tc);
+       }
+       return 0;
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
+{
+       struct talloc_chunk *tc, *new_tc;
+
+       if (unlikely(!ptr)) {
+               return NULL;
+       }
+
+       if (unlikely(new_ctx == NULL)) {
+               new_ctx = null_context;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (unlikely(new_ctx == NULL)) {
+               if (tc->parent) {
+                       _TLIST_REMOVE(tc->parent->child, tc);
+                       if (tc->parent->child) {
+                               tc->parent->child->parent = tc->parent;
+                       }
+               } else {
+                       if (tc->prev) tc->prev->next = tc->next;
+                       if (tc->next) tc->next->prev = tc->prev;
+               }
+               
+               tc->parent = tc->next = tc->prev = NULL;
+               return discard_const_p(void, ptr);
+       }
+
+       new_tc = talloc_chunk_from_ptr(new_ctx);
+
+       if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+               return discard_const_p(void, ptr);
+       }
+
+       if (tc->parent) {
+               _TLIST_REMOVE(tc->parent->child, tc);
+               if (tc->parent->child) {
+                       tc->parent->child->parent = tc->parent;
+               }
+       } else {
+               if (tc->prev) tc->prev->next = tc->next;
+               if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
+       }
+
+       tc->parent = new_tc;
+       if (new_tc->child) new_tc->child->parent = NULL;
+       _TLIST_ADD(new_tc->child, tc);
+
+       return discard_const_p(void, ptr);
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+_PUBLIC_ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+       
+       tc = talloc_chunk_from_ptr(ptr);
+       
+       if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) {
+               struct talloc_reference_handle *h;
+
+               talloc_log("WARNING: talloc_steal with references at %s\n",
+                          location);
+
+               for (h=tc->refs; h; h=h->next) {
+                       talloc_log("\treference at %s\n",
+                                  h->location);
+               }
+       }
+
+#if 0
+       /* this test is probably too expensive to have on in the
+          normal build, but it useful for debugging */
+       if (talloc_is_parent(new_ctx, ptr)) {
+               talloc_log("WARNING: stealing into talloc child at %s\n", location);
+       }
+#endif
+       
+       return _talloc_steal_internal(new_ctx, ptr);
+}
+
+/* 
+   this is like a talloc_steal(), but you must supply the old
+   parent. This resolves the ambiguity in a talloc_steal() which is
+   called on a context that has more than one parent (via references)
+
+   The old parent can be either a reference or a parent
+*/
+_PUBLIC_ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr)
+{
+       struct talloc_chunk *tc;
+       struct talloc_reference_handle *h;
+
+       if (unlikely(ptr == NULL)) {
+               return NULL;
+       }
+
+       if (old_parent == talloc_parent(ptr)) {
+               return _talloc_steal_internal(new_parent, ptr);
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+       for (h=tc->refs;h;h=h->next) {
+               if (talloc_parent(h) == old_parent) {
+                       if (_talloc_steal_internal(new_parent, h) != h) {
+                               return NULL;
+                       }
+                       return discard_const_p(void, ptr);
+               }
+       }       
+
+       /* it wasn't a parent */
+       return NULL;
+}
+
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       struct talloc_reference_handle *h;
+
+       if (unlikely(context == NULL)) {
+               context = null_context;
+       }
+
+       for (h=tc->refs;h;h=h->next) {
+               struct talloc_chunk *p = talloc_parent_chunk(h);
+               if (p == NULL) {
+                       if (context == NULL) break;
+               } else if (TC_PTR_FROM_CHUNK(p) == context) {
+                       break;
+               }
+       }
+       if (h == NULL) {
+               return -1;
+       }
+
+       return _talloc_free_internal(h, __location__);
+}
+
+/*
+  remove a specific parent context from a pointer. This is a more
+  controlled varient of talloc_free()
+*/
+_PUBLIC_ int talloc_unlink(const void *context, void *ptr)
+{
+       struct talloc_chunk *tc_p, *new_p;
+       void *new_parent;
+
+       if (ptr == NULL) {
+               return -1;
+       }
+
+       if (context == NULL) {
+               context = null_context;
+       }
+
+       if (talloc_unreference(context, ptr) == 0) {
+               return 0;
+       }
+
+       if (context == NULL) {
+               if (talloc_parent_chunk(ptr) != NULL) {
+                       return -1;
+               }
+       } else {
+               if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+                       return -1;
+               }
+       }
+       
+       tc_p = talloc_chunk_from_ptr(ptr);
+
+       if (tc_p->refs == NULL) {
+               return _talloc_free_internal(ptr, __location__);
+       }
+
+       new_p = talloc_parent_chunk(tc_p->refs);
+       if (new_p) {
+               new_parent = TC_PTR_FROM_CHUNK(new_p);
+       } else {
+               new_parent = NULL;
+       }
+
+       if (talloc_unreference(new_parent, ptr) != 0) {
+               return -1;
+       }
+
+       _talloc_steal_internal(new_parent, ptr);
+
+       return 0;
+}
+
+/*
+  add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       tc->name = talloc_vasprintf(ptr, fmt, ap);
+       if (likely(tc->name)) {
+               _talloc_set_name_const(tc->name, ".name");
+       }
+       return tc->name;
+}
+
+/*
+  add a name to an existing pointer
+*/
+_PUBLIC_ const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+       const char *name;
+       va_list ap;
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+       return name;
+}
+
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+_PUBLIC_ void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+       va_list ap;
+       void *ptr;
+       const char *name;
+
+       ptr = __talloc(context, size);
+       if (unlikely(ptr == NULL)) return NULL;
+
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+
+       if (unlikely(name == NULL)) {
+               _talloc_free_internal(ptr, __location__);
+               return NULL;
+       }
+
+       return ptr;
+}
+
+/*
+  return the name of a talloc ptr, or "UNNAMED"
+*/
+_PUBLIC_ const char *talloc_get_name(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+               return ".reference";
+       }
+       if (likely(tc->name)) {
+               return tc->name;
+       }
+       return "UNNAMED";
+}
+
+
+/*
+  check if a pointer has the given name. If it does, return the pointer,
+  otherwise return NULL
+*/
+_PUBLIC_ void *talloc_check_name(const void *ptr, const char *name)
+{
+       const char *pname;
+       if (unlikely(ptr == NULL)) return NULL;
+       pname = talloc_get_name(ptr);
+       if (likely(pname == name || strcmp(pname, name) == 0)) {
+               return discard_const_p(void, ptr);
+       }
+       return NULL;
+}
+
+static void talloc_abort_type_missmatch(const char *location,
+                                       const char *name,
+                                       const char *expected)
+{
+       const char *reason;
+
+       reason = talloc_asprintf(NULL,
+                                "%s: Type mismatch: name[%s] expected[%s]",
+                                location,
+                                name?name:"NULL",
+                                expected);
+       if (!reason) {
+               reason = "Type mismatch";
+       }
+
+       talloc_abort(reason);
+}
+
+_PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
+{
+       const char *pname;
+
+       if (unlikely(ptr == NULL)) {
+               talloc_abort_type_missmatch(location, NULL, name);
+               return NULL;
+       }
+
+       pname = talloc_get_name(ptr);
+       if (likely(pname == name || strcmp(pname, name) == 0)) {
+               return discard_const_p(void, ptr);
+       }
+
+       talloc_abort_type_missmatch(location, pname, name);
+       return NULL;
+}
+
+/*
+  this is for compatibility with older versions of talloc
+*/
+_PUBLIC_ void *talloc_init(const char *fmt, ...)
+{
+       va_list ap;
+       void *ptr;
+       const char *name;
+
+       ptr = __talloc(NULL, 0);
+       if (unlikely(ptr == NULL)) return NULL;
+
+       va_start(ap, fmt);
+       name = talloc_set_name_v(ptr, fmt, ap);
+       va_end(ap);
+
+       if (unlikely(name == NULL)) {
+               _talloc_free_internal(ptr, __location__);
+               return NULL;
+       }
+
+       return ptr;
+}
+
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location)
+{
+       while (tc->child) {
+               /* we need to work out who will own an abandoned child
+                  if it cannot be freed. In priority order, the first
+                  choice is owner of any remaining reference to this
+                  pointer, the second choice is our parent, and the
+                  final choice is the null context. */
+               void *child = TC_PTR_FROM_CHUNK(tc->child);
+               const void *new_parent = null_context;
+               if (unlikely(tc->child->refs)) {
+                       struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+                       if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+               }
+               if (unlikely(_talloc_free_internal(child, location) == -1)) {
+                       if (new_parent == null_context) {
+                               struct talloc_chunk *p = talloc_parent_chunk(ptr);
+                               if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+                       }
+                       _talloc_steal_internal(new_parent, child);
+               }
+       }
+}
+
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+_PUBLIC_ void talloc_free_children(void *ptr)
+{
+       struct talloc_chunk *tc_name = NULL;
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       /* we do not want to free the context name if it is a child .. */
+       if (likely(tc->child)) {
+               for (tc_name = tc->child; tc_name; tc_name = tc_name->next) {
+                       if (tc->name == TC_PTR_FROM_CHUNK(tc_name)) break;
+               }
+               if (tc_name) {
+                       _TLIST_REMOVE(tc->child, tc_name);
+                       if (tc->child) {
+                               tc->child->parent = tc;
+                       }
+               }
+       }
+
+       _talloc_free_children_internal(tc, ptr, __location__);
+
+       /* .. so we put it back after all other children have been freed */
+       if (tc_name) {
+               if (tc->child) {
+                       tc->child->parent = NULL;
+               }
+               tc_name->parent = tc;
+               _TLIST_ADD(tc->child, tc_name);
+       }
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+_PUBLIC_ void *_talloc(const void *context, size_t size)
+{
+       return __talloc(context, size);
+}
+
+/*
+  externally callable talloc_set_name_const()
+*/
+_PUBLIC_ void talloc_set_name_const(const void *ptr, const char *name)
+{
+       _talloc_set_name_const(ptr, name);
+}
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+_PUBLIC_ void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+       return _talloc_named_const(context, size, name);
+}
+
+/* 
+   free a talloc pointer. This also frees all child pointers of this 
+   pointer recursively
+
+   return 0 if the memory is actually freed, otherwise -1. The memory
+   will not be freed if the ref_count is > 1 or the destructor (if
+   any) returns non-zero
+*/
+_PUBLIC_ int _talloc_free(void *ptr, const char *location)
+{
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return -1;
+       }
+       
+       tc = talloc_chunk_from_ptr(ptr);
+       
+       if (unlikely(tc->refs != NULL)) {
+               struct talloc_reference_handle *h;
+
+               if (talloc_parent(ptr) == null_context && tc->refs->next == NULL) {
+                       /* in this case we do know which parent should
+                          get this pointer, as there is really only
+                          one parent */
+                       return talloc_unlink(null_context, ptr);
+               }
+
+               talloc_log("ERROR: talloc_free with references at %s\n",
+                          location);
+
+               for (h=tc->refs; h; h=h->next) {
+                       talloc_log("\treference at %s\n",
+                                  h->location);
+               }
+               return -1;
+       }
+       
+       return _talloc_free_internal(ptr, location);
+}
+
+
+
+/*
+  A talloc version of realloc. The context argument is only used if
+  ptr is NULL
+*/
+_PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+       struct talloc_chunk *tc;
+       void *new_ptr;
+       bool malloced = false;
+       struct talloc_chunk *pool_tc = NULL;
+
+       /* size zero is equivalent to free() */
+       if (unlikely(size == 0)) {
+               talloc_unlink(context, ptr);
+               return NULL;
+       }
+
+       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+               return NULL;
+       }
+
+       /* realloc(NULL) is equivalent to malloc() */
+       if (ptr == NULL) {
+               return _talloc_named_const(context, size, name);
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       /* don't allow realloc on referenced pointers */
+       if (unlikely(tc->refs)) {
+               return NULL;
+       }
+
+       /* don't let anybody try to realloc a talloc_pool */
+       if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
+               return NULL;
+       }
+
+       /* don't let anybody try to realloc a talloc_pool */
+       if (unlikely(tc->flags & TALLOC_FLAG_POOLMEM)) {
+               pool_tc = (struct talloc_chunk *)tc->pool;
+       }
+
+#if (ALWAYS_REALLOC == 0)
+       /* don't shrink if we have less than 1k to gain */
+       if (size < tc->size) {
+               if (pool_tc) {
+                       void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+                       TC_INVALIDATE_SHRINK_CHUNK(tc, size);
+                       tc->size = size;
+                       if (next_tc == pool_tc->pool) {
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                       }
+                       return ptr;
+               } else if ((tc->size - size) < 1024) {
+                       /*
+                        * if we call TC_INVALIDATE_SHRINK_CHUNK() here
+                        * we would need to call TC_UNDEFINE_GROW_CHUNK()
+                        * after each realloc call, which slows down
+                        * testing a lot :-(.
+                        *
+                        * That is why we only mark memory as undefined here.
+                        */
+                       TC_UNDEFINE_SHRINK_CHUNK(tc, size);
+
+                       /* do not shrink if we have less than 1k to gain */
+                       tc->size = size;
+                       return ptr;
+               }
+       } else if (tc->size == size) {
+               /*
+                * do not change the pointer if it is exactly
+                * the same size.
+                */
+               return ptr;
+       }
+#endif
+
+       /* by resetting magic we catch users of the old memory */
+       tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+       if (pool_tc) {
+               new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+               *talloc_pool_objectcount(pool_tc) -= 1;
+
+               if (new_ptr == NULL) {
+                       new_ptr = malloc(TC_HDR_SIZE+size);
+                       malloced = true;
+               }
+
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+               }
+       } else {
+               new_ptr = malloc(size + TC_HDR_SIZE);
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE);
+                       free(tc);
+               }
+       }
+#else
+       if (pool_tc) {
+               void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+               size_t old_chunk_size = TC_POOLMEM_CHUNK_SIZE(tc);
+               size_t new_chunk_size = TC_ALIGN16(TC_HDR_SIZE + size);
+               size_t space_needed;
+               size_t space_left;
+               unsigned int chunk_count = *talloc_pool_objectcount(pool_tc);
+
+               if (!(pool_tc->flags & TALLOC_FLAG_FREE)) {
+                       chunk_count -= 1;
+               }
+
+               if (chunk_count == 1) {
+                       /*
+                        * optimize for the case where 'tc' is the only
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size;
+                       space_left = pool_tc->size - TALLOC_POOL_HDR_SIZE;
+
+                       if (space_left >= space_needed) {
+                               size_t old_used = TC_HDR_SIZE + tc->size;
+                               size_t new_used = TC_HDR_SIZE + size;
+                               pool_tc->pool = TC_POOL_FIRST_CHUNK(pool_tc);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+                               /*
+                                * we need to prepare the memmove into
+                                * the unaccessable area.
+                                */
+                               {
+                                       size_t diff = PTR_DIFF(tc, pool_tc->pool);
+                                       size_t flen = MIN(diff, old_used);
+                                       char *fptr = (char *)pool_tc->pool;
+                                       VALGRIND_MAKE_MEM_UNDEFINED(fptr, flen);
+                               }
+#endif
+                               memmove(pool_tc->pool, tc, old_used);
+                               new_ptr = pool_tc->pool;
+
+                               tc = (struct talloc_chunk *)new_ptr;
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+
+                               /*
+                                * first we do not align the pool pointer
+                                * because we want to invalidate the padding
+                                * too.
+                                */
+                               pool_tc->pool = new_used + (char *)new_ptr;
+                               TC_INVALIDATE_POOL(pool_tc);
+
+                               /* now the aligned pointer */
+                               pool_tc->pool = new_chunk_size + (char *)new_ptr;
+                               goto got_new_ptr;
+                       }
+
+                       next_tc = NULL;
+               }
+
+               if (new_chunk_size == old_chunk_size) {
+                       TC_UNDEFINE_GROW_CHUNK(tc, size);
+                       tc->flags &= ~TALLOC_FLAG_FREE;
+                       tc->size = size;
+                       return ptr;
+               }
+
+               if (next_tc == pool_tc->pool) {
+                       /*
+                        * optimize for the case where 'tc' is the last
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size - old_chunk_size;
+                       space_left = TC_POOL_SPACE_LEFT(pool_tc);
+
+                       if (space_left >= space_needed) {
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+                               tc->flags &= ~TALLOC_FLAG_FREE;
+                               tc->size = size;
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                               return ptr;
+                       }
+               }
+
+               new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+
+               if (new_ptr == NULL) {
+                       new_ptr = malloc(TC_HDR_SIZE+size);
+                       malloced = true;
+               }
+
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+
+                       _talloc_free_poolmem(tc, __location__ "_talloc_realloc");
+               }
+       }
+       else {
+               new_ptr = realloc(tc, size + TC_HDR_SIZE);
+       }
+got_new_ptr:
+#endif
+       if (unlikely(!new_ptr)) {       
+               tc->flags &= ~TALLOC_FLAG_FREE; 
+               return NULL; 
+       }
+
+       tc = (struct talloc_chunk *)new_ptr;
+       tc->flags &= ~TALLOC_FLAG_FREE;
+       if (malloced) {
+               tc->flags &= ~TALLOC_FLAG_POOLMEM;
+       }
+       if (tc->parent) {
+               tc->parent->child = tc;
+       }
+       if (tc->child) {
+               tc->child->parent = tc;
+       }
+
+       if (tc->prev) {
+               tc->prev->next = tc;
+       }
+       if (tc->next) {
+               tc->next->prev = tc;
+       }
+
+       tc->size = size;
+       _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+       return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+  a wrapper around talloc_steal() for situations where you are moving a pointer
+  between two structures, and want the old pointer to be set to NULL
+*/
+_PUBLIC_ void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+       const void **pptr = discard_const_p(const void *,_pptr);
+       void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr));
+       (*pptr) = NULL;
+       return ret;
+}
+
+/*
+  return the total size of a talloc pool (subtree)
+*/
+_PUBLIC_ size_t talloc_total_size(const void *ptr)
+{
+       size_t total = 0;
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return 0;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
+               total = tc->size;
+       }
+       for (c=tc->child;c;c=c->next) {
+               total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+       }
+
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+
+       return total;
+}
+
+/*
+  return the total number of blocks in a talloc pool (subtree)
+*/
+_PUBLIC_ size_t talloc_total_blocks(const void *ptr)
+{
+       size_t total = 0;
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return 0;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+
+       total++;
+       for (c=tc->child;c;c=c->next) {
+               total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+       }
+
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+
+       return total;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+_PUBLIC_ size_t talloc_reference_count(const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       struct talloc_reference_handle *h;
+       size_t ret = 0;
+
+       for (h=tc->refs;h;h=h->next) {
+               ret++;
+       }
+       return ret;
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+                           void (*callback)(const void *ptr,
+                                            int depth, int max_depth,
+                                            int is_ref,
+                                            void *private_data),
+                           void *private_data)
+{
+       struct talloc_chunk *c, *tc;
+
+       if (ptr == NULL) {
+               ptr = null_context;
+       }
+       if (ptr == NULL) return;
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       if (tc->flags & TALLOC_FLAG_LOOP) {
+               return;
+       }
+
+       callback(ptr, depth, max_depth, 0, private_data);
+
+       if (max_depth >= 0 && depth >= max_depth) {
+               return;
+       }
+
+       tc->flags |= TALLOC_FLAG_LOOP;
+       for (c=tc->child;c;c=c->next) {
+               if (c->name == TALLOC_MAGIC_REFERENCE) {
+                       struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+                       callback(h->ptr, depth + 1, max_depth, 1, private_data);
+               } else {
+                       talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+               }
+       }
+       tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+       const char *name = talloc_get_name(ptr);
+       FILE *f = (FILE *)_f;
+
+       if (is_ref) {
+               fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+               return;
+       }
+
+       if (depth == 0) {
+               fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
+                       (max_depth < 0 ? "full " :""), name,
+                       (unsigned long)talloc_total_size(ptr),
+                       (unsigned long)talloc_total_blocks(ptr));
+               return;
+       }
+
+       fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
+               depth*4, "",
+               name,
+               (unsigned long)talloc_total_size(ptr),
+               (unsigned long)talloc_total_blocks(ptr),
+               (int)talloc_reference_count(ptr), ptr);
+
+#if 0
+       fprintf(f, "content: ");
+       if (talloc_total_size(ptr)) {
+               int tot = talloc_total_size(ptr);
+               int i;
+
+               for (i = 0; i < tot; i++) {
+                       if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+                               fprintf(f, "%c", ((char *)ptr)[i]);
+                       } else {
+                               fprintf(f, "~%02x", ((char *)ptr)[i]);
+                       }
+               }
+       }
+       fprintf(f, "\n");
+#endif
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+       if (f) {
+               talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+               fflush(f);
+       }
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+_PUBLIC_ void talloc_report_full(const void *ptr, FILE *f)
+{
+       talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+  report on memory usage by all children of a pointer
+*/
+_PUBLIC_ void talloc_report(const void *ptr, FILE *f)
+{
+       talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+       if (talloc_total_size(null_context) != 0) {
+               talloc_report(null_context, stderr);
+       }
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+       if (talloc_total_size(null_context) != 0) {
+               talloc_report_full(null_context, stderr);
+       }
+}
+
+/*
+  enable tracking of the NULL context
+*/
+_PUBLIC_ void talloc_enable_null_tracking(void)
+{
+       if (null_context == NULL) {
+               null_context = _talloc_named_const(NULL, 0, "null_context");
+               if (autofree_context != NULL) {
+                       talloc_reparent(NULL, null_context, autofree_context);
+               }
+       }
+}
+
+/*
+  enable tracking of the NULL context, not moving the autofree context
+  into the NULL context. This is needed for the talloc testsuite
+*/
+_PUBLIC_ void talloc_enable_null_tracking_no_autofree(void)
+{
+       if (null_context == NULL) {
+               null_context = _talloc_named_const(NULL, 0, "null_context");
+       }
+}
+
+/*
+  disable tracking of the NULL context
+*/
+_PUBLIC_ void talloc_disable_null_tracking(void)
+{
+       if (null_context != NULL) {
+               /* we have to move any children onto the real NULL
+                  context */
+               struct talloc_chunk *tc, *tc2;
+               tc = talloc_chunk_from_ptr(null_context);
+               for (tc2 = tc->child; tc2; tc2=tc2->next) {
+                       if (tc2->parent == tc) tc2->parent = NULL;
+                       if (tc2->prev == tc) tc2->prev = NULL;
+               }
+               for (tc2 = tc->next; tc2; tc2=tc2->next) {
+                       if (tc2->parent == tc) tc2->parent = NULL;
+                       if (tc2->prev == tc) tc2->prev = NULL;
+               }
+               tc->child = NULL;
+               tc->next = NULL;
+       }
+       talloc_free(null_context);
+       null_context = NULL;
+}
+
+/*
+  enable leak reporting on exit
+*/
+_PUBLIC_ void talloc_enable_leak_report(void)
+{
+       talloc_enable_null_tracking();
+       atexit(talloc_report_null);
+}
+
+/*
+  enable full leak reporting on exit
+*/
+_PUBLIC_ void talloc_enable_leak_report_full(void)
+{
+       talloc_enable_null_tracking();
+       atexit(talloc_report_null_full);
+}
+
+/* 
+   talloc and zero memory. 
+*/
+_PUBLIC_ void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+       void *p = _talloc_named_const(ctx, size, name);
+
+       if (p) {
+               memset(p, '\0', size);
+       }
+
+       return p;
+}
+
+/*
+  memdup with a talloc. 
+*/
+_PUBLIC_ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+       void *newp = _talloc_named_const(t, size, name);
+
+       if (likely(newp)) {
+               memcpy(newp, p, size);
+       }
+
+       return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+       char *ret;
+
+       ret = (char *)__talloc(t, len + 1);
+       if (unlikely(!ret)) return NULL;
+
+       memcpy(ret, p, len);
+       ret[len] = 0;
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+/*
+  strdup with a talloc
+*/
+_PUBLIC_ char *talloc_strdup(const void *t, const char *p)
+{
+       if (unlikely(!p)) return NULL;
+       return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+  strndup with a talloc
+*/
+_PUBLIC_ char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+       if (unlikely(!p)) return NULL;
+       return __talloc_strlendup(t, p, strnlen(p, n));
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+                                             const char *a, size_t alen)
+{
+       char *ret;
+
+       ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+       if (unlikely(!ret)) return NULL;
+
+       /* append the string and the trailing \0 */
+       memcpy(&ret[slen], a, alen);
+       ret[slen+alen] = 0;
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+_PUBLIC_ char *talloc_strdup_append(char *s, const char *a)
+{
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+_PUBLIC_ char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+_PUBLIC_ char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+_PUBLIC_ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_strdup(NULL, a);
+       }
+
+       if (unlikely(!a)) {
+               return s;
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_strlendup_append(s, slen, a, strnlen(a, n));
+}
+
+_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+       int len;
+       char *ret;
+       va_list ap2;
+       char c;
+
+       /* this call looks strange, but it makes it work on older solaris boxes */
+       va_copy(ap2, ap);
+       len = vsnprintf(&c, 1, fmt, ap2);
+       va_end(ap2);
+       if (unlikely(len < 0)) {
+               return NULL;
+       }
+
+       ret = (char *)__talloc(t, len+1);
+       if (unlikely(!ret)) return NULL;
+
+       va_copy(ap2, ap);
+       vsnprintf(ret, len+1, fmt, ap2);
+       va_end(ap2);
+
+       _talloc_set_name_const(ret, ret);
+       return ret;
+}
+
+
+/*
+  Perform string formatting, and return a pointer to newly allocated
+  memory holding the result, inside a memory pool.
+ */
+_PUBLIC_ char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = talloc_vasprintf(t, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+                                                const char *fmt, va_list ap)
+                                                PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+                                                const char *fmt, va_list ap)
+{
+       ssize_t alen;
+       va_list ap2;
+       char c;
+
+       va_copy(ap2, ap);
+       alen = vsnprintf(&c, 1, fmt, ap2);
+       va_end(ap2);
+
+       if (alen <= 0) {
+               /* Either the vsnprintf failed or the format resulted in
+                * no characters being formatted. In the former case, we
+                * ought to return NULL, in the latter we ought to return
+                * the original string. Most current callers of this
+                * function expect it to never return NULL.
+                */
+               return s;
+       }
+
+       s = talloc_realloc(NULL, s, char, slen + alen + 1);
+       if (!s) return NULL;
+
+       va_copy(ap2, ap);
+       vsnprintf(s + slen, alen + 1, fmt, ap2);
+       va_end(ap2);
+
+       _talloc_set_name_const(s, s);
+       return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+_PUBLIC_ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+       if (unlikely(!s)) {
+               return talloc_vasprintf(NULL, fmt, ap);
+       }
+
+       return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+_PUBLIC_ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+       size_t slen;
+
+       if (unlikely(!s)) {
+               return talloc_vasprintf(NULL, fmt, ap);
+       }
+
+       slen = talloc_get_size(s);
+       if (likely(slen > 0)) {
+               slen--;
+       }
+
+       return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a string buffer.
+ */
+_PUBLIC_ char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       s = talloc_vasprintf_append(s, fmt, ap);
+       va_end(ap);
+       return s;
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a buffer.
+ */
+_PUBLIC_ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       s = talloc_vasprintf_append_buffer(s, fmt, ap);
+       va_end(ap);
+       return s;
+}
+
+/*
+  alloc an array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+  alloc an zero array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+  realloc an array, checking for integer overflow in the array size
+*/
+_PUBLIC_ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+       if (count >= MAX_TALLOC_SIZE/el_size) {
+               return NULL;
+       }
+       return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+  a function version of talloc_realloc(), so it can be passed as a function pointer
+  to libraries that want a realloc function (a realloc function encapsulates
+  all the basic capabilities of an allocation library, which is why this is useful)
+*/
+_PUBLIC_ void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+       return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+       autofree_context = NULL;
+       return 0;
+}
+
+static void talloc_autofree(void)
+{
+       talloc_free(autofree_context);
+}
+
+/*
+  return a context which will be auto-freed on exit
+  this is useful for reducing the noise in leak reports
+*/
+_PUBLIC_ void *talloc_autofree_context(void)
+{
+       if (autofree_context == NULL) {
+               autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+               talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+               atexit(talloc_autofree);
+       }
+       return autofree_context;
+}
+
+_PUBLIC_ size_t talloc_get_size(const void *context)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               context = null_context;
+       }
+       if (context == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+
+       return tc->size;
+}
+
+/*
+  find a parent of this context that has the given name, if any
+*/
+_PUBLIC_ void *talloc_find_parent_byname(const void *context, const char *name)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               return NULL;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       while (tc) {
+               if (tc->name && strcmp(tc->name, name) == 0) {
+                       return TC_PTR_FROM_CHUNK(tc);
+               }
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+               }
+       }
+       return NULL;
+}
+
+/*
+  show the parentage of a context
+*/
+_PUBLIC_ void talloc_show_parents(const void *context, FILE *file)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               fprintf(file, "talloc no parents for NULL\n");
+               return;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+       while (tc) {
+               fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+               }
+       }
+       fflush(file);
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+static int _talloc_is_parent(const void *context, const void *ptr, int depth)
+{
+       struct talloc_chunk *tc;
+
+       if (context == NULL) {
+               return 0;
+       }
+
+       tc = talloc_chunk_from_ptr(context);
+       while (tc && depth > 0) {
+               if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+               while (tc && tc->prev) tc = tc->prev;
+               if (tc) {
+                       tc = tc->parent;
+                       depth--;
+               }
+       }
+       return 0;
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+_PUBLIC_ int talloc_is_parent(const void *context, const void *ptr)
+{
+       return _talloc_is_parent(context, ptr, TALLOC_MAX_DEPTH);
+}
index d549715f831b414fc098b334a97f6572c74ffc76..88ff7accd4d800287cb48828f6ad93ddbc83b98d 100644 (file)
@@ -25,7 +25,6 @@
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
-
 #include "tdb_private.h"
 
 /* check for an out of bounds access - if it is out of bounds then
@@ -212,7 +211,7 @@ void tdb_mmap(struct tdb_context *tdb)
        if (!(tdb->flags & TDB_NOMMAP)) {
                tdb->map_ptr = mmap(NULL, tdb->map_size, 
                                    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
-                                   MAP_SHARED|MAP_FILE, tdb->fd, 0);
+                                   MAP_SHARED, tdb->fd, 0);
 
                /*
                 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
index 41389c41c97a349acf69d8f1239751295546a518..de5dd02b141f3ca5cae4115938f8195aaeea4fc1 100644 (file)
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
-#if 0
-#include "replace.h"
-#include "system/filesys.h"
-#include "system/time.h"
-#include "system/shmem.h"
-#include "system/select.h"
-#include "system/wait.h"
-#include "tdb.h"
-#endif
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
 
 #include <unistd.h>
 #include <stdint.h>
@@ -50,6 +44,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <sys/param.h>
+#include <stddef.h>
 
 #ifndef __STRING
 #define __STRING(x)    #x
 typedef uint32_t tdb_len_t;
 typedef uint32_t tdb_off_t;
 
-#ifndef offsetof
-#define offsetof(t,f) ((unsigned int)&((t *)0)->f)
-#endif
-
 #define TDB_MAGIC_FOOD "TDB file\n"
 #define TDB_VERSION (0x26011967 + 6)
 #define TDB_MAGIC (0x26011999U)
index b56f9f354d754e6fde0c155562bd8536fa7ca1f4..abf6f3266b006ec5e5bf64228b7309399723463a 100644 (file)
@@ -17,6 +17,6 @@ libunicode_la_SOURCES = \
 
 libunicode_la_LIBADD = $(LIBUNICODE_DEPS)
 
-noinst_HEADERS = utf16_casetable.h precompose.h byteorder.h
+noinst_HEADERS = utf16_casetable.h precompose.h
 
 LIBS=@ICONV_LIBS@
diff --git a/libatalk/unicode/byteorder.h b/libatalk/unicode/byteorder.h
deleted file mode 100644 (file)
index cc9a7f0..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-   SMB Byte handling
-   Copyright (C) Andrew Tridgell 1992-1998
-   
-   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
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-   
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef _BYTEORDER_H
-#define _BYTEORDER_H
-#include <arpa/inet.h>
-
-/*
-   This file implements macros for machine independent short and 
-   int manipulation
-
-Here is a description of this file that I emailed to the samba list once:
-
-> I am confused about the way that byteorder.h works in Samba. I have
-> looked at it, and I would have thought that you might make a distinction
-> between LE and BE machines, but you only seem to distinguish between 386
-> and all other architectures.
-> 
-> Can you give me a clue?
-
-sure.
-
-The distinction between 386 and other architectures is only there as
-an optimisation. You can take it out completely and it will make no
-difference. The routines (macros) in byteorder.h are totally byteorder
-independent. The 386 optimsation just takes advantage of the fact that
-the x86 processors don't care about alignment, so we don't have to
-align ints on int boundaries etc. If there are other processors out
-there that aren't alignment sensitive then you could also define
-CAREFUL_ALIGNMENT=0 on those processors as well.
-
-Ok, now to the macros themselves. I'll take a simple example, say we
-want to extract a 2 byte integer from a SMB packet and put it into a
-type called uint16 that is in the local machines byte order, and you
-want to do it with only the assumption that uint16 is _at_least_ 16
-bits long (this last condition is very important for architectures
-that don't have any int types that are 2 bytes long)
-
-You do this:
-
-#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
-#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
-#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
-
-then to extract a uint16 value at offset 25 in a buffer you do this:
-
-char *buffer = foo_bar();
-uint16 xx = SVAL(buffer,25);
-
-We are using the byteoder independence of the ANSI C bitshifts to do
-the work. A good optimising compiler should turn this into efficient
-code, especially if it happens to have the right byteorder :-)
-
-I know these macros can be made a bit tidier by removing some of the
-casts, but you need to look at byteorder.h as a whole to see the
-reasoning behind them. byteorder.h defines the following macros:
-
-SVAL(buf,pos) - extract a 2 byte SMB value
-IVAL(buf,pos) - extract a 4 byte SMB value
-SVALS(buf,pos) signed version of SVAL()
-IVALS(buf,pos) signed version of IVAL()
-
-SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
-SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
-SSVALS(buf,pos,val) - signed version of SSVAL()
-SIVALS(buf,pos,val) - signed version of SIVAL()
-
-RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
-RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
-RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
-RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
-RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
-RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
-RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
-
-it also defines lots of intermediate macros, just ignore those :-)
-
-*/
-
-#undef CAREFUL_ALIGNMENT
-
-/* we know that the 386 can handle misalignment and has the "right" 
-   byteorder */
-#ifdef __i386__
-#define CAREFUL_ALIGNMENT 0
-#endif
-
-#ifndef CAREFUL_ALIGNMENT
-#define CAREFUL_ALIGNMENT 1
-#endif
-
-#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
-#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
-#define PVAL(buf,pos) (CVAL(buf,pos))
-#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
-
-
-#if CAREFUL_ALIGNMENT
-
-#if BYTE_ORDER==BIG_ENDIAN
-
-#define SVAL(buf,pos) (PVAL(buf,(pos)+1)|PVAL(buf,pos)<<8)
-#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
-#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos+1)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos)=(unsigned char)((val)>>8))
-#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
-
-#else
-
-#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
-#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
-#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
-#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
-
-#endif
-
-#else /* CAREFUL_ALIGNMENT */
-
-/* this handles things for architectures like the 386 that can handle
-   alignment errors */
-/*
-   WARNING: This section is dependent on the length of int16 and int32
-   being correct 
-*/
-
-/* get single value from an SMB buffer */
-#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
-#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
-#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
-#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
-#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
-
-/* store single value in an SMB buffer */
-#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
-#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
-#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
-#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
-
-#endif /* CAREFUL_ALIGNMENT */
-
-/* now the reverse routines - these are used in nmb packets (mostly) */
-#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
-#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
-
-#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
-#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
-#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
-#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
-#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
-#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
-#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
-#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
-
-/* Alignment macros. */
-#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
-#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
-
-#endif /* _BYTEORDER_H */
index 785e47720f1d7ab2e74ce2be611c93164386fdb9..7242b576776a1198a4776f31bb2b14f2d9342330 100644 (file)
@@ -42,8 +42,7 @@
 #include <atalk/unicode.h>
 #include <atalk/util.h>
 #include <atalk/compat.h>
-
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 
 /**
@@ -95,6 +94,16 @@ int set_charset_name(charset_t ch, const char *name)
     return 0;
 }
 
+void free_charset_names(void)
+{
+    for (int ch = 0; ch < MAX_CHARSETS; ch++) {
+        if (charset_names[ch]) {
+            free(charset_names[ch]);
+            charset_names[ch] = NULL;
+        }
+    }
+}
+
 static struct charset_functions* get_charset_functions (charset_t ch)
 {
     if (charsets[ch] != NULL)
@@ -693,7 +702,7 @@ char * debug_out ( char * seq, size_t len)
  *      for e.g. HFS cdroms.
  */
 
-static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
+static size_t pull_charset_flags (charset_t from_set, charset_t to_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
 {
     const uint16_t option = (flags ? *flags : 0);
     size_t i_len, o_len;
@@ -702,6 +711,7 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     char* outbuf = dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
+    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     if (srclen == (size_t)-1)
         srclen = strlen(src) + 1;
@@ -717,10 +727,30 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     i_len=srclen;
     o_len=destlen;
 
+    if ((option & CONV_ESCAPEDOTS) && i_len >= 2 && inbuf[0] == '.') {
+        if (o_len < 6) {
+            errno = E2BIG;
+            goto end;
+        }
+        ucs2_t ucs2 = ':';
+        memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+        ucs2 = '2';
+        memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        ucs2 = 'e';
+        memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        outbuf += 6;
+        o_len -= 6;
+        inbuf++;
+        i_len--;
+        *flags |= CONV_REQESCAPE;
+    }
+
     while (i_len > 0) {
         for (j = 0; j < i_len; ++j)
-            if (inbuf[j] == ':')
+            if (inbuf[j] == ':' || inbuf[j] == '/') {
+                escch = inbuf[j];
                 break;
+            }
         j = i_len - j;
         i_len -= j;
 
@@ -750,48 +780,108 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
         }
 
         if (j) {
-            /* we have a ':' */
+            /* we have a ':' or '/' */
             i_len = j, j = 0;
 
-            if ((option & CONV_UNESCAPEHEX)) {
-                /* treat it as a CAP hex encoded char */
-                char h[MAXPATHLEN];
-                size_t hlen = 0;
-
-                while (i_len >= 3 && inbuf[0] == ':' &&
-                       isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
-                    h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
-                    inbuf += 3;
-                    i_len -= 3;
-                }
-                if (hlen) {
-                    const char *h_buf = h;
-                    if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
-                        i_len += hlen * 3;
-                        inbuf -= hlen * 3;
-                        if (errno == EILSEQ && (option & CONV_IGNORE)) {
+            if (escch == ':') {
+                if ((option & CONV_UNESCAPEHEX)) {
+                    /* treat it as a CAP hex encoded char */
+                    char h[MAXPATHLEN];
+                    size_t hlen = 0;
+
+                    while (i_len >= 3 && inbuf[0] == ':' &&
+                           isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
+                        h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
+                        inbuf += 3;
+                        i_len -= 3;
+                    }
+                    if (hlen) {
+                        const char *h_buf = h;
+                        if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
+                            i_len += hlen * 3;
+                            inbuf -= hlen * 3;
+                            if (errno == EILSEQ && (option & CONV_IGNORE)) {
+                                *flags |= CONV_REQMANGLE;
+                                return destlen - o_len;
+                            }
+                            goto end;
+                        }
+                    } else {
+                        /* We have an invalid :xx sequence */
+                        errno = EILSEQ;
+                        if ((option & CONV_IGNORE)) {
                             *flags |= CONV_REQMANGLE;
                             return destlen - o_len;
                         }
                         goto end;
                     }
-                } else {
-                    /* We have an invalid :xx sequence */
-                    errno = EILSEQ;
-                    if ((option & CONV_IGNORE)) {
-                        *flags |= CONV_REQMANGLE;
-                        return destlen - o_len;
+                } else if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
                     }
-                    goto end;
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '3';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'a';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if (to_set == CH_UTF8_MAC || to_set == CH_MAC) {
+                    /* convert to a '/' */
+                    ucs2_t slash = 0x002f;
+                    memcpy(outbuf, &slash, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
                 }
             } else {
-                /* a ':' that we just convert to a '/' */
-                ucs2_t slash = 0x002f;
-                memcpy(outbuf, &slash, sizeof(ucs2_t));
-                outbuf += 2;
-                o_len -= 2;
-                inbuf++;
-                i_len--;
+                /* '/' */
+                if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
+                    }
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '2';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'f';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if ((from_set == CH_UTF8_MAC || from_set == CH_MAC)
+                           && (to_set != CH_UTF8_MAC  || to_set != CH_MAC)) {
+                    /* convert to ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as '/' */
+                    ucs2_t ucs2 = 0x002f;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                }
             }
         }
     }
@@ -824,7 +914,6 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     char* outbuf = (char*)dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
-    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     descriptor = conv_handles[CH_UCS2][to_set];
     descriptor_cap = conv_handles[CH_UCS2][cap_set];
@@ -837,42 +926,7 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     i_len=srclen;
     o_len=destlen;
 
-    if ((option & CONV_ESCAPEDOTS) &&
-        i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
-        if (o_len < 3) {
-            errno = E2BIG;
-            goto end;
-        }
-        *outbuf++ = ':';
-        *outbuf++ = '2';
-        *outbuf++ = 'e';
-        o_len -= 3;
-        inbuf += 2;
-        i_len -= 2;
-        *flags |= CONV_REQESCAPE;
-    }
-
     while (i_len >= 2) {
-        for (i = 0; i < i_len; i += 2) {
-            ucs2_t c = SVAL(inbuf, i);
-            switch (c) {
-            case 0x003a: /* 0x003a = ':' */
-                if ( ! (option & CONV_ALLOW_COLON)) {
-                    errno = EILSEQ;
-                    goto end;
-                }
-                escch = c;
-                j = i_len - i;
-                i_len = i;
-                break;
-            case 0x002f: /* 0x002f = '/' */
-                if (option & CONV_ALLOW_SLASH) break;
-                escch = c;
-                j = i_len - i;
-                i_len = i;
-                break;
-            }
-        }
         while (i_len > 0 &&
                atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
             if (errno == EILSEQ) {
@@ -921,57 +975,8 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
             }
             goto end;
         }
+    } /* while (i_len >= 2) */
 
-        if (j) {
-            /* we have a ':' or '/' */
-            i_len = j, j = 0;
-
-            if ((option & CONV_ESCAPEHEX)) {
-                /* CAP hex encode it */
-                if (o_len < 3) {
-                    errno = E2BIG;
-                    goto end;
-                }
-                switch (escch) {
-                case '/':
-                    *outbuf++ = ':';
-                    *outbuf++ = '2';
-                    *outbuf++ = 'f';
-                    break;
-                case ':':
-                    *outbuf++ = ':';
-                    *outbuf++ = '3';
-                    *outbuf++ = 'a';
-                    break;
-                default:
-                    /*
-                     *  THIS SHOULD NEVER BE REACHED !!!
-                     *  As a safety net I put in a ' ' here
-                     */
-                    *outbuf++ = ':';
-                    *outbuf++ = '2';
-                    *outbuf++ = '0';
-                    break;
-                }
-                o_len -= 3;
-                inbuf += 2;
-                i_len -= 2;
-            } else {
-                switch (escch) {
-                case '/':
-                case ':':
-                    *outbuf++ = ':';
-                    break;
-                default: /* should never be reached */
-                    *outbuf++ = ' ';
-                    break;
-                }
-                o_len--;
-                inbuf += 2;
-                i_len -= 2;
-            }
-        }
-    }
     if (i_len > 0) errno = EINVAL;
 end:
     return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
@@ -991,7 +996,7 @@ size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_cha
     lazy_initialize_conv();
 
     /* convert from_set to UCS2 */
-    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len,
+    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, to_set, cap_charset, src, src_len,
                                                       (char *) buffer, sizeof(buffer) -2, flags)) ) {
         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
         return (size_t) -1;
index 5dab15a6284a6ce323a245594a9498e90e2b2ebf..3c4a48c706b309d4c9bc312b18af0f0b5c486cec 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <atalk/unicode.h>
 #include <iconv.h>
-#include "../byteorder.h"
+#include <atalk/byteorder.h>
 
 #define CJK_PUSH_BUFFER 4
 #define CJK_PULL_BUFFER 8
index 7b36997d804a035a4f6b86f1d0686d95df9b6759..0347c3e755cc2752bd83b16880f9cb020d8d9f5b 100644 (file)
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
+#include <atalk/byteorder.h>
 
 #include "generic_mb.h"
-#include "../byteorder.h"
+
 
 
 /* ------------------------ */
index 91a022bd330983a0dcb2df6eb5decefba8cbf7c6..34d96902b16e6ac3c0ace2e2c85f7a2d7f231b75 100644 (file)
@@ -34,8 +34,8 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
+#include <atalk/byteorder.h>
 
-#include "../byteorder.h"
 #include "mac_hebrew.h"
 
 static size_t   mac_hebrew_pull(void *,char **, size_t *, char **, size_t *);
index bf3a5bca9ef1e951b966a362f514e57954893458..5d2c72e6f0e2678f5b2e99b0ad36b6224c851616 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: mac_roman.h,v 1.2 2005-04-28 20:50:04 bfernhomberg Exp $
  *
  * 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
index 7c5eb67e3ce0ddd653696ef014d657d324e07c15..c44df315de2ed4797e53d44554543849ec48e69b 100644 (file)
@@ -43,7 +43,7 @@
 
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 
 /**
index 256c1fae06cbb849bf7e20f5d9b60c2f4cbfc9f7..cbf1a3970eac32f67d4285bdd134ceb6693c0623 100644 (file)
@@ -19,8 +19,8 @@
 #define DECOMP_COUNT 955
 #define MAXCOMBLEN 3
 
-#define PRECOMP_SP_COUNT 18
-#define DECOMP_SP_COUNT 18
+#define PRECOMP_SP_COUNT 25
+#define DECOMP_SP_COUNT 25
 #define MAXCOMBSPLEN 4
 
 #define COMBBUFLEN 4  /* max(MAXCOMBLEN,MAXCOMBSPLEN) */
@@ -1035,6 +1035,13 @@ static const struct {
 /*{ 0x000110AB, 0x000110A5, 0x000110BA },*/   /* KAITHI LETTER VA */
 /*{ 0x0001112E, 0x00011131, 0x00011127 },*/   /* CHAKMA VOWEL SIGN O */
 /*{ 0x0001112F, 0x00011132, 0x00011127 },*/   /* CHAKMA VOWEL SIGN AU */
+/*{ 0x0001134B, 0x00011347, 0x0001133E },*/   /* GRANTHA VOWEL SIGN OO */
+/*{ 0x0001134C, 0x00011347, 0x00011357 },*/   /* GRANTHA VOWEL SIGN AU */
+/*{ 0x000114BC, 0x000114B9, 0x000114B0 },*/   /* TIRHUTA VOWEL SIGN O */
+/*{ 0x000114BB, 0x000114B9, 0x000114BA },*/   /* TIRHUTA VOWEL SIGN AI */
+/*{ 0x000114BE, 0x000114B9, 0x000114BD },*/   /* TIRHUTA VOWEL SIGN AU */
+/*{ 0x000115BA, 0x000115B8, 0x000115AF },*/   /* SIDDHAM VOWEL SIGN O */
+/*{ 0x000115BB, 0x000115B9, 0x000115AF },*/   /* SIDDHAM VOWEL SIGN AU */
 /*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/   /* MUSICAL SYMBOL HALF NOTE */
 /*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/   /* MUSICAL SYMBOL QUARTER NOTE */
 /*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/   /* MUSICAL SYMBOL EIGHTH NOTE */
@@ -2060,6 +2067,13 @@ static const struct {
 /*{ 0x000110AB, 0x000110A5, 0x000110BA },*/   /* KAITHI LETTER VA */
 /*{ 0x0001112E, 0x00011131, 0x00011127 },*/   /* CHAKMA VOWEL SIGN O */
 /*{ 0x0001112F, 0x00011132, 0x00011127 },*/   /* CHAKMA VOWEL SIGN AU */
+/*{ 0x0001134B, 0x00011347, 0x0001133E },*/   /* GRANTHA VOWEL SIGN OO */
+/*{ 0x0001134C, 0x00011347, 0x00011357 },*/   /* GRANTHA VOWEL SIGN AU */
+/*{ 0x000114BB, 0x000114B9, 0x000114BA },*/   /* TIRHUTA VOWEL SIGN AI */
+/*{ 0x000114BC, 0x000114B9, 0x000114B0 },*/   /* TIRHUTA VOWEL SIGN O */
+/*{ 0x000114BE, 0x000114B9, 0x000114BD },*/   /* TIRHUTA VOWEL SIGN AU */
+/*{ 0x000115BA, 0x000115B8, 0x000115AF },*/   /* SIDDHAM VOWEL SIGN O */
+/*{ 0x000115BB, 0x000115B9, 0x000115AF },*/   /* SIDDHAM VOWEL SIGN AU */
 /*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/   /* MUSICAL SYMBOL HALF NOTE */
 /*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/   /* MUSICAL SYMBOL QUARTER NOTE */
 /*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/   /* MUSICAL SYMBOL EIGHTH NOTE */
@@ -2085,6 +2099,13 @@ static const struct {
   { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA },     /* KAITHI LETTER VA */
   { 0xD804DD2E, 0xD804DD31, 0xD804DD27 },     /* CHAKMA VOWEL SIGN O */
   { 0xD804DD2F, 0xD804DD32, 0xD804DD27 },     /* CHAKMA VOWEL SIGN AU */
+  { 0xD804DF4B, 0xD804DF47, 0xD804DF3E },     /* GRANTHA VOWEL SIGN OO */
+  { 0xD804DF4C, 0xD804DF47, 0xD804DF57 },     /* GRANTHA VOWEL SIGN AU */
+  { 0xD805DCBC, 0xD805DCB9, 0xD805DCB0 },     /* TIRHUTA VOWEL SIGN O */
+  { 0xD805DCBB, 0xD805DCB9, 0xD805DCBA },     /* TIRHUTA VOWEL SIGN AI */
+  { 0xD805DCBE, 0xD805DCB9, 0xD805DCBD },     /* TIRHUTA VOWEL SIGN AU */
+  { 0xD805DDBA, 0xD805DDB8, 0xD805DDAF },     /* SIDDHAM VOWEL SIGN O */
+  { 0xD805DDBB, 0xD805DDB9, 0xD805DDAF },     /* SIDDHAM VOWEL SIGN AU */
   { 0xD834DD5E, 0xD834DD57, 0xD834DD65 },     /* MUSICAL SYMBOL HALF NOTE */
   { 0xD834DD5F, 0xD834DD58, 0xD834DD65 },     /* MUSICAL SYMBOL QUARTER NOTE */
   { 0xD834DD60, 0xD834DD5F, 0xD834DD6E },     /* MUSICAL SYMBOL EIGHTH NOTE */
@@ -2110,6 +2131,13 @@ static const struct {
   { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA },     /* KAITHI LETTER VA */
   { 0xD804DD2E, 0xD804DD31, 0xD804DD27 },     /* CHAKMA VOWEL SIGN O */
   { 0xD804DD2F, 0xD804DD32, 0xD804DD27 },     /* CHAKMA VOWEL SIGN AU */
+  { 0xD804DF4B, 0xD804DF47, 0xD804DF3E },     /* GRANTHA VOWEL SIGN OO */
+  { 0xD804DF4C, 0xD804DF47, 0xD804DF57 },     /* GRANTHA VOWEL SIGN AU */
+  { 0xD805DCBB, 0xD805DCB9, 0xD805DCBA },     /* TIRHUTA VOWEL SIGN AI */
+  { 0xD805DCBC, 0xD805DCB9, 0xD805DCB0 },     /* TIRHUTA VOWEL SIGN O */
+  { 0xD805DCBE, 0xD805DCB9, 0xD805DCBD },     /* TIRHUTA VOWEL SIGN AU */
+  { 0xD805DDBA, 0xD805DDB8, 0xD805DDAF },     /* SIDDHAM VOWEL SIGN O */
+  { 0xD805DDBB, 0xD805DDB9, 0xD805DDAF },     /* SIDDHAM VOWEL SIGN AU */
   { 0xD834DD5E, 0xD834DD57, 0xD834DD65 },     /* MUSICAL SYMBOL HALF NOTE */
   { 0xD834DD5F, 0xD834DD58, 0xD834DD65 },     /* MUSICAL SYMBOL QUARTER NOTE */
   { 0xD834DD60, 0xD834DD5F, 0xD834DD6E },     /* MUSICAL SYMBOL EIGHTH NOTE */
index 3fc7ed36586081469f0423671d86b7d0ccc6cfea..ca9285cb5d1d6e61a0cbb20c58cc56ce0bc97a7a 100644 (file)
@@ -58,6 +58,9 @@ uint32_t toupper_sp(uint32_t val)
     if ( val >= 0xD801DC00 && val <= 0xD801DC7F)
         return upper_table_sp_1[val-0xD801DC00];
 
+    if ( val >= 0xD806DCC0 && val <= 0xD806DCFF)
+        return upper_table_sp_2[val-0xD806DCC0];
+
        return (val);
 }
 
@@ -110,6 +113,9 @@ uint32_t tolower_sp(uint32_t val)
     if ( val >= 0xD801DC00 && val <= 0xD801DC3F)
         return lower_table_sp_1[val-0xD801DC00];
 
+    if ( val >= 0xD806DC80 && val <= 0xD806DCBF)
+        return lower_table_sp_2[val-0xD806DC80];
+
        return (val);
 }
 
index 188e1ea75fb24df3d57a04bbfa854d5338b9f48f..4d32d78df84e74efc20fc091d851b4989f4807ea 100644 (file)
@@ -613,12 +613,12 @@ static const uint16_t upper_table_1[704] = {
   0x018F, /*U+0259*/ /*LATIN SMALL LETTER SCHWA*/
   0x025A, /*U+025A*/ /**/
   0x0190, /*U+025B*/ /*LATIN SMALL LETTER OPEN E*/
-  0x025C, /*U+025C*/ /**/
+  0xA7AB, /*U+025C*/ /*LATIN SMALL LETTER REVERSED OPEN E*/
   0x025D, /*U+025D*/ /**/
   0x025E, /*U+025E*/ /**/
   0x025F, /*U+025F*/ /**/
   0x0193, /*U+0260*/ /*LATIN SMALL LETTER G WITH HOOK*/
-  0x0261, /*U+0261*/ /**/
+  0xA7AC, /*U+0261*/ /*LATIN SMALL LETTER SCRIPT G*/
   0x0262, /*U+0262*/ /**/
   0x0194, /*U+0263*/ /*LATIN SMALL LETTER GAMMA*/
   0x0264, /*U+0264*/ /**/
@@ -629,7 +629,7 @@ static const uint16_t upper_table_1[704] = {
   0x0196, /*U+0269*/ /*LATIN SMALL LETTER IOTA*/
   0x026A, /*U+026A*/ /**/
   0x2C62, /*U+026B*/ /*LATIN SMALL LETTER L WITH MIDDLE TILDE*/
-  0x026C, /*U+026C*/ /**/
+  0xA7AD, /*U+026C*/ /*LATIN SMALL LETTER L WITH BELT*/
   0x026D, /*U+026D*/ /**/
   0x026E, /*U+026E*/ /**/
   0x019C, /*U+026F*/ /*LATIN SMALL LETTER TURNED M*/
@@ -656,7 +656,7 @@ static const uint16_t upper_table_1[704] = {
   0x0284, /*U+0284*/ /**/
   0x0285, /*U+0285*/ /**/
   0x0286, /*U+0286*/ /**/
-  0x0287, /*U+0287*/ /**/
+  0xA7B1, /*U+0287*/ /*LATIN SMALL LETTER TURNED T*/
   0x01AE, /*U+0288*/ /*LATIN SMALL LETTER T WITH RETROFLEX HOOK*/
   0x0244, /*U+0289*/ /*LATIN SMALL LETTER U BAR*/
   0x01B1, /*U+028A*/ /*LATIN SMALL LETTER UPSILON*/
@@ -679,7 +679,7 @@ static const uint16_t upper_table_1[704] = {
   0x029B, /*U+029B*/ /**/
   0x029C, /*U+029C*/ /**/
   0x029D, /*U+029D*/ /**/
-  0x029E, /*U+029E*/ /**/
+  0xA7B0, /*U+029E*/ /*LATIN SMALL LETTER TURNED K*/
   0x029F, /*U+029F*/ /**/
   0x02A0, /*U+02A0*/ /**/
   0x02A1, /*U+02A1*/ /**/
@@ -895,7 +895,7 @@ static const uint16_t upper_table_2[640] = {
   0x039A, /*U+03F0*/ /*GREEK KAPPA SYMBOL*/
   0x03A1, /*U+03F1*/ /*GREEK RHO SYMBOL*/
   0x03F9, /*U+03F2*/ /*GREEK LUNATE SIGMA SYMBOL*/
-  0x03F3, /*U+03F3*/ /**/
+  0x037F, /*U+03F3*/ /*GREEK LETTER YOT*/
   0x03F4, /*U+03F4*/ /**/
   0x0395, /*U+03F5*/ /*GREEK LUNATE EPSILON SYMBOL*/
   0x03F6, /*U+03F6*/ /**/
@@ -1205,13 +1205,13 @@ static const uint16_t upper_table_2[640] = {
   0x0526, /*U+0526*/ /**/
   0x0526, /*U+0527*/ /*CYRILLIC SMALL LETTER SHHA WITH DESCENDER*/
   0x0528, /*U+0528*/ /**/
-  0x0529, /*U+0529*/ /**/
+  0x0528, /*U+0529*/ /*CYRILLIC SMALL LETTER EN WITH LEFT HOOK*/
   0x052A, /*U+052A*/ /**/
-  0x052B, /*U+052B*/ /**/
+  0x052A, /*U+052B*/ /*CYRILLIC SMALL LETTER DZZHE*/
   0x052C, /*U+052C*/ /**/
-  0x052D, /*U+052D*/ /**/
+  0x052C, /*U+052D*/ /*CYRILLIC SMALL LETTER DCHE*/
   0x052E, /*U+052E*/ /**/
-  0x052F, /*U+052F*/ /**/
+  0x052E, /*U+052F*/ /*CYRILLIC SMALL LETTER EL WITH DESCENDER*/
   0x0530, /*U+0530*/ /**/
   0x0531, /*U+0531*/ /**/
   0x0532, /*U+0532*/ /**/
@@ -2551,9 +2551,9 @@ static const uint16_t upper_table_8[128] = {
   0xA696, /*U+A696*/ /**/
   0xA696, /*U+A697*/ /*CYRILLIC SMALL LETTER SHWE*/
   0xA698, /*U+A698*/ /**/
-  0xA699, /*U+A699*/ /**/
+  0xA698, /*U+A699*/ /*CYRILLIC SMALL LETTER DOUBLE O*/
   0xA69A, /*U+A69A*/ /**/
-  0xA69B, /*U+A69B*/ /**/
+  0xA69A, /*U+A69B*/ /*CYRILLIC SMALL LETTER CROSSED O*/
   0xA69C, /*U+A69C*/ /**/
   0xA69D, /*U+A69D*/ /**/
   0xA69E, /*U+A69E*/ /**/
@@ -2744,15 +2744,15 @@ static const uint16_t upper_table_9[192] = {
   0xA794, /*U+A794*/ /**/
   0xA795, /*U+A795*/ /**/
   0xA796, /*U+A796*/ /**/
-  0xA797, /*U+A797*/ /**/
+  0xA796, /*U+A797*/ /*LATIN SMALL LETTER B WITH FLOURISH*/
   0xA798, /*U+A798*/ /**/
-  0xA799, /*U+A799*/ /**/
+  0xA798, /*U+A799*/ /*LATIN SMALL LETTER F WITH STROKE*/
   0xA79A, /*U+A79A*/ /**/
-  0xA79B, /*U+A79B*/ /**/
+  0xA79A, /*U+A79B*/ /*LATIN SMALL LETTER VOLAPUK AE*/
   0xA79C, /*U+A79C*/ /**/
-  0xA79D, /*U+A79D*/ /**/
+  0xA79C, /*U+A79D*/ /*LATIN SMALL LETTER VOLAPUK OE*/
   0xA79E, /*U+A79E*/ /**/
-  0xA79F, /*U+A79F*/ /**/
+  0xA79E, /*U+A79F*/ /*LATIN SMALL LETTER VOLAPUK UE*/
   0xA7A0, /*U+A7A0*/ /**/
   0xA7A0, /*U+A7A1*/ /*LATIN SMALL LETTER G WITH OBLIQUE STROKE*/
   0xA7A2, /*U+A7A2*/ /**/
@@ -2985,6 +2985,73 @@ static const uint32_t upper_table_sp_1[128] = {
   0xD801DC7F, /*0xD801DC7F*/ /*U+01047F*/ /*U+01047F*/ /**/
 };
 
+static const uint32_t upper_table_sp_2[64] = {
+  0xD806DCA0, /*0xD806DCC0*/ /*U+0118A0*/ /*U+0118C0*/ /*WARANG CITI SMALL LETTER NGAA*/
+  0xD806DCA1, /*0xD806DCC1*/ /*U+0118A1*/ /*U+0118C1*/ /*WARANG CITI SMALL LETTER A*/
+  0xD806DCA2, /*0xD806DCC2*/ /*U+0118A2*/ /*U+0118C2*/ /*WARANG CITI SMALL LETTER WI*/
+  0xD806DCA3, /*0xD806DCC3*/ /*U+0118A3*/ /*U+0118C3*/ /*WARANG CITI SMALL LETTER YU*/
+  0xD806DCA4, /*0xD806DCC4*/ /*U+0118A4*/ /*U+0118C4*/ /*WARANG CITI SMALL LETTER YA*/
+  0xD806DCA5, /*0xD806DCC5*/ /*U+0118A5*/ /*U+0118C5*/ /*WARANG CITI SMALL LETTER YO*/
+  0xD806DCA6, /*0xD806DCC6*/ /*U+0118A6*/ /*U+0118C6*/ /*WARANG CITI SMALL LETTER II*/
+  0xD806DCA7, /*0xD806DCC7*/ /*U+0118A7*/ /*U+0118C7*/ /*WARANG CITI SMALL LETTER UU*/
+  0xD806DCA8, /*0xD806DCC8*/ /*U+0118A8*/ /*U+0118C8*/ /*WARANG CITI SMALL LETTER E*/
+  0xD806DCA9, /*0xD806DCC9*/ /*U+0118A9*/ /*U+0118C9*/ /*WARANG CITI SMALL LETTER O*/
+  0xD806DCAA, /*0xD806DCCA*/ /*U+0118AA*/ /*U+0118CA*/ /*WARANG CITI SMALL LETTER ANG*/
+  0xD806DCAB, /*0xD806DCCB*/ /*U+0118AB*/ /*U+0118CB*/ /*WARANG CITI SMALL LETTER GA*/
+  0xD806DCAC, /*0xD806DCCC*/ /*U+0118AC*/ /*U+0118CC*/ /*WARANG CITI SMALL LETTER KO*/
+  0xD806DCAD, /*0xD806DCCD*/ /*U+0118AD*/ /*U+0118CD*/ /*WARANG CITI SMALL LETTER ENY*/
+  0xD806DCAE, /*0xD806DCCE*/ /*U+0118AE*/ /*U+0118CE*/ /*WARANG CITI SMALL LETTER YUJ*/
+  0xD806DCAF, /*0xD806DCCF*/ /*U+0118AF*/ /*U+0118CF*/ /*WARANG CITI SMALL LETTER UC*/
+  0xD806DCB0, /*0xD806DCD0*/ /*U+0118B0*/ /*U+0118D0*/ /*WARANG CITI SMALL LETTER ENN*/
+  0xD806DCB1, /*0xD806DCD1*/ /*U+0118B1*/ /*U+0118D1*/ /*WARANG CITI SMALL LETTER ODD*/
+  0xD806DCB2, /*0xD806DCD2*/ /*U+0118B2*/ /*U+0118D2*/ /*WARANG CITI SMALL LETTER TTE*/
+  0xD806DCB3, /*0xD806DCD3*/ /*U+0118B3*/ /*U+0118D3*/ /*WARANG CITI SMALL LETTER NUNG*/
+  0xD806DCB4, /*0xD806DCD4*/ /*U+0118B4*/ /*U+0118D4*/ /*WARANG CITI SMALL LETTER DA*/
+  0xD806DCB5, /*0xD806DCD5*/ /*U+0118B5*/ /*U+0118D5*/ /*WARANG CITI SMALL LETTER AT*/
+  0xD806DCB6, /*0xD806DCD6*/ /*U+0118B6*/ /*U+0118D6*/ /*WARANG CITI SMALL LETTER AM*/
+  0xD806DCB7, /*0xD806DCD7*/ /*U+0118B7*/ /*U+0118D7*/ /*WARANG CITI SMALL LETTER BU*/
+  0xD806DCB8, /*0xD806DCD8*/ /*U+0118B8*/ /*U+0118D8*/ /*WARANG CITI SMALL LETTER PU*/
+  0xD806DCB9, /*0xD806DCD9*/ /*U+0118B9*/ /*U+0118D9*/ /*WARANG CITI SMALL LETTER HIYO*/
+  0xD806DCBA, /*0xD806DCDA*/ /*U+0118BA*/ /*U+0118DA*/ /*WARANG CITI SMALL LETTER HOLO*/
+  0xD806DCBB, /*0xD806DCDB*/ /*U+0118BB*/ /*U+0118DB*/ /*WARANG CITI SMALL LETTER HORR*/
+  0xD806DCBC, /*0xD806DCDC*/ /*U+0118BC*/ /*U+0118DC*/ /*WARANG CITI SMALL LETTER HAR*/
+  0xD806DCBD, /*0xD806DCDD*/ /*U+0118BD*/ /*U+0118DD*/ /*WARANG CITI SMALL LETTER SSUU*/
+  0xD806DCBE, /*0xD806DCDE*/ /*U+0118BE*/ /*U+0118DE*/ /*WARANG CITI SMALL LETTER SII*/
+  0xD806DCBF, /*0xD806DCDF*/ /*U+0118BF*/ /*U+0118DF*/ /*WARANG CITI SMALL LETTER VIYO*/
+  0xD806DCE0, /*0xD806DCE0*/ /*U+0118E0*/ /*U+0118E0*/ /**/
+  0xD806DCE1, /*0xD806DCE1*/ /*U+0118E1*/ /*U+0118E1*/ /**/
+  0xD806DCE2, /*0xD806DCE2*/ /*U+0118E2*/ /*U+0118E2*/ /**/
+  0xD806DCE3, /*0xD806DCE3*/ /*U+0118E3*/ /*U+0118E3*/ /**/
+  0xD806DCE4, /*0xD806DCE4*/ /*U+0118E4*/ /*U+0118E4*/ /**/
+  0xD806DCE5, /*0xD806DCE5*/ /*U+0118E5*/ /*U+0118E5*/ /**/
+  0xD806DCE6, /*0xD806DCE6*/ /*U+0118E6*/ /*U+0118E6*/ /**/
+  0xD806DCE7, /*0xD806DCE7*/ /*U+0118E7*/ /*U+0118E7*/ /**/
+  0xD806DCE8, /*0xD806DCE8*/ /*U+0118E8*/ /*U+0118E8*/ /**/
+  0xD806DCE9, /*0xD806DCE9*/ /*U+0118E9*/ /*U+0118E9*/ /**/
+  0xD806DCEA, /*0xD806DCEA*/ /*U+0118EA*/ /*U+0118EA*/ /**/
+  0xD806DCEB, /*0xD806DCEB*/ /*U+0118EB*/ /*U+0118EB*/ /**/
+  0xD806DCEC, /*0xD806DCEC*/ /*U+0118EC*/ /*U+0118EC*/ /**/
+  0xD806DCED, /*0xD806DCED*/ /*U+0118ED*/ /*U+0118ED*/ /**/
+  0xD806DCEE, /*0xD806DCEE*/ /*U+0118EE*/ /*U+0118EE*/ /**/
+  0xD806DCEF, /*0xD806DCEF*/ /*U+0118EF*/ /*U+0118EF*/ /**/
+  0xD806DCF0, /*0xD806DCF0*/ /*U+0118F0*/ /*U+0118F0*/ /**/
+  0xD806DCF1, /*0xD806DCF1*/ /*U+0118F1*/ /*U+0118F1*/ /**/
+  0xD806DCF2, /*0xD806DCF2*/ /*U+0118F2*/ /*U+0118F2*/ /**/
+  0xD806DCF3, /*0xD806DCF3*/ /*U+0118F3*/ /*U+0118F3*/ /**/
+  0xD806DCF4, /*0xD806DCF4*/ /*U+0118F4*/ /*U+0118F4*/ /**/
+  0xD806DCF5, /*0xD806DCF5*/ /*U+0118F5*/ /*U+0118F5*/ /**/
+  0xD806DCF6, /*0xD806DCF6*/ /*U+0118F6*/ /*U+0118F6*/ /**/
+  0xD806DCF7, /*0xD806DCF7*/ /*U+0118F7*/ /*U+0118F7*/ /**/
+  0xD806DCF8, /*0xD806DCF8*/ /*U+0118F8*/ /*U+0118F8*/ /**/
+  0xD806DCF9, /*0xD806DCF9*/ /*U+0118F9*/ /*U+0118F9*/ /**/
+  0xD806DCFA, /*0xD806DCFA*/ /*U+0118FA*/ /*U+0118FA*/ /**/
+  0xD806DCFB, /*0xD806DCFB*/ /*U+0118FB*/ /*U+0118FB*/ /**/
+  0xD806DCFC, /*0xD806DCFC*/ /*U+0118FC*/ /*U+0118FC*/ /**/
+  0xD806DCFD, /*0xD806DCFD*/ /*U+0118FD*/ /*U+0118FD*/ /**/
+  0xD806DCFE, /*0xD806DCFE*/ /*U+0118FE*/ /*U+0118FE*/ /**/
+  0xD806DCFF, /*0xD806DCFF*/ /*U+0118FF*/ /*U+0118FF*/ /**/
+};
+
 static const uint16_t lower_table_1[128] = {
   0x0000, /*U+0000*/ /**/
   0x0001, /*U+0001*/ /**/
@@ -3631,7 +3698,7 @@ static const uint16_t lower_table_3[576] = {
   0x037C, /*U+037C*/ /**/
   0x037D, /*U+037D*/ /**/
   0x037E, /*U+037E*/ /**/
-  0x037F, /*U+037F*/ /**/
+  0x03F3, /*U+037F*/ /*GREEK CAPITAL LETTER YOT*/
   0x0380, /*U+0380*/ /**/
   0x0381, /*U+0381*/ /**/
   0x0382, /*U+0382*/ /**/
@@ -4056,13 +4123,13 @@ static const uint16_t lower_table_3[576] = {
   0x0525, /*U+0525*/ /**/
   0x0527, /*U+0526*/ /*CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER*/
   0x0527, /*U+0527*/ /**/
-  0x0528, /*U+0528*/ /**/
+  0x0529, /*U+0528*/ /*CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK*/
   0x0529, /*U+0529*/ /**/
-  0x052A, /*U+052A*/ /**/
+  0x052B, /*U+052A*/ /*CYRILLIC CAPITAL LETTER DZZHE*/
   0x052B, /*U+052B*/ /**/
-  0x052C, /*U+052C*/ /**/
+  0x052D, /*U+052C*/ /*CYRILLIC CAPITAL LETTER DCHE*/
   0x052D, /*U+052D*/ /**/
-  0x052E, /*U+052E*/ /**/
+  0x052F, /*U+052E*/ /*CYRILLIC CAPITAL LETTER EL WITH DESCENDER*/
   0x052F, /*U+052F*/ /**/
   0x0530, /*U+0530*/ /**/
   0x0561, /*U+0531*/ /*ARMENIAN CAPITAL LETTER AYB*/
@@ -5466,9 +5533,9 @@ static const uint16_t lower_table_9[128] = {
   0xA695, /*U+A695*/ /**/
   0xA697, /*U+A696*/ /*CYRILLIC CAPITAL LETTER SHWE*/
   0xA697, /*U+A697*/ /**/
-  0xA698, /*U+A698*/ /**/
+  0xA699, /*U+A698*/ /*CYRILLIC CAPITAL LETTER DOUBLE O*/
   0xA699, /*U+A699*/ /**/
-  0xA69A, /*U+A69A*/ /**/
+  0xA69B, /*U+A69A*/ /*CYRILLIC CAPITAL LETTER CROSSED O*/
   0xA69B, /*U+A69B*/ /**/
   0xA69C, /*U+A69C*/ /**/
   0xA69D, /*U+A69D*/ /**/
@@ -5659,15 +5726,15 @@ static const uint16_t lower_table_10[192] = {
   0xA793, /*U+A793*/ /**/
   0xA794, /*U+A794*/ /**/
   0xA795, /*U+A795*/ /**/
-  0xA796, /*U+A796*/ /**/
+  0xA797, /*U+A796*/ /*LATIN CAPITAL LETTER B WITH FLOURISH*/
   0xA797, /*U+A797*/ /**/
-  0xA798, /*U+A798*/ /**/
+  0xA799, /*U+A798*/ /*LATIN CAPITAL LETTER F WITH STROKE*/
   0xA799, /*U+A799*/ /**/
-  0xA79A, /*U+A79A*/ /**/
+  0xA79B, /*U+A79A*/ /*LATIN CAPITAL LETTER VOLAPUK AE*/
   0xA79B, /*U+A79B*/ /**/
-  0xA79C, /*U+A79C*/ /**/
+  0xA79D, /*U+A79C*/ /*LATIN CAPITAL LETTER VOLAPUK OE*/
   0xA79D, /*U+A79D*/ /**/
-  0xA79E, /*U+A79E*/ /**/
+  0xA79F, /*U+A79E*/ /*LATIN CAPITAL LETTER VOLAPUK UE*/
   0xA79F, /*U+A79F*/ /**/
   0xA7A1, /*U+A7A0*/ /*LATIN CAPITAL LETTER G WITH OBLIQUE STROKE*/
   0xA7A1, /*U+A7A1*/ /**/
@@ -5680,13 +5747,13 @@ static const uint16_t lower_table_10[192] = {
   0xA7A9, /*U+A7A8*/ /*LATIN CAPITAL LETTER S WITH OBLIQUE STROKE*/
   0xA7A9, /*U+A7A9*/ /**/
   0x0266, /*U+A7AA*/ /*LATIN CAPITAL LETTER H WITH HOOK*/
-  0xA7AB, /*U+A7AB*/ /**/
-  0xA7AC, /*U+A7AC*/ /**/
-  0xA7AD, /*U+A7AD*/ /**/
+  0x025C, /*U+A7AB*/ /*LATIN CAPITAL LETTER REVERSED OPEN E*/
+  0x0261, /*U+A7AC*/ /*LATIN CAPITAL LETTER SCRIPT G*/
+  0x026C, /*U+A7AD*/ /*LATIN CAPITAL LETTER L WITH BELT*/
   0xA7AE, /*U+A7AE*/ /**/
   0xA7AF, /*U+A7AF*/ /**/
-  0xA7B0, /*U+A7B0*/ /**/
-  0xA7B1, /*U+A7B1*/ /**/
+  0x029E, /*U+A7B0*/ /*LATIN CAPITAL LETTER TURNED K*/
+  0x0287, /*U+A7B1*/ /*LATIN CAPITAL LETTER TURNED T*/
   0xA7B2, /*U+A7B2*/ /**/
   0xA7B3, /*U+A7B3*/ /**/
   0xA7B4, /*U+A7B4*/ /**/
@@ -5837,4 +5904,71 @@ static const uint32_t lower_table_sp_1[64] = {
   0xD801DC3F, /*0xD801DC3F*/ /*U+01043F*/ /*U+01043F*/ /**/
 };
 
+static const uint32_t lower_table_sp_2[64] = {
+  0xD806DC80, /*0xD806DC80*/ /*U+011880*/ /*U+011880*/ /**/
+  0xD806DC81, /*0xD806DC81*/ /*U+011881*/ /*U+011881*/ /**/
+  0xD806DC82, /*0xD806DC82*/ /*U+011882*/ /*U+011882*/ /**/
+  0xD806DC83, /*0xD806DC83*/ /*U+011883*/ /*U+011883*/ /**/
+  0xD806DC84, /*0xD806DC84*/ /*U+011884*/ /*U+011884*/ /**/
+  0xD806DC85, /*0xD806DC85*/ /*U+011885*/ /*U+011885*/ /**/
+  0xD806DC86, /*0xD806DC86*/ /*U+011886*/ /*U+011886*/ /**/
+  0xD806DC87, /*0xD806DC87*/ /*U+011887*/ /*U+011887*/ /**/
+  0xD806DC88, /*0xD806DC88*/ /*U+011888*/ /*U+011888*/ /**/
+  0xD806DC89, /*0xD806DC89*/ /*U+011889*/ /*U+011889*/ /**/
+  0xD806DC8A, /*0xD806DC8A*/ /*U+01188A*/ /*U+01188A*/ /**/
+  0xD806DC8B, /*0xD806DC8B*/ /*U+01188B*/ /*U+01188B*/ /**/
+  0xD806DC8C, /*0xD806DC8C*/ /*U+01188C*/ /*U+01188C*/ /**/
+  0xD806DC8D, /*0xD806DC8D*/ /*U+01188D*/ /*U+01188D*/ /**/
+  0xD806DC8E, /*0xD806DC8E*/ /*U+01188E*/ /*U+01188E*/ /**/
+  0xD806DC8F, /*0xD806DC8F*/ /*U+01188F*/ /*U+01188F*/ /**/
+  0xD806DC90, /*0xD806DC90*/ /*U+011890*/ /*U+011890*/ /**/
+  0xD806DC91, /*0xD806DC91*/ /*U+011891*/ /*U+011891*/ /**/
+  0xD806DC92, /*0xD806DC92*/ /*U+011892*/ /*U+011892*/ /**/
+  0xD806DC93, /*0xD806DC93*/ /*U+011893*/ /*U+011893*/ /**/
+  0xD806DC94, /*0xD806DC94*/ /*U+011894*/ /*U+011894*/ /**/
+  0xD806DC95, /*0xD806DC95*/ /*U+011895*/ /*U+011895*/ /**/
+  0xD806DC96, /*0xD806DC96*/ /*U+011896*/ /*U+011896*/ /**/
+  0xD806DC97, /*0xD806DC97*/ /*U+011897*/ /*U+011897*/ /**/
+  0xD806DC98, /*0xD806DC98*/ /*U+011898*/ /*U+011898*/ /**/
+  0xD806DC99, /*0xD806DC99*/ /*U+011899*/ /*U+011899*/ /**/
+  0xD806DC9A, /*0xD806DC9A*/ /*U+01189A*/ /*U+01189A*/ /**/
+  0xD806DC9B, /*0xD806DC9B*/ /*U+01189B*/ /*U+01189B*/ /**/
+  0xD806DC9C, /*0xD806DC9C*/ /*U+01189C*/ /*U+01189C*/ /**/
+  0xD806DC9D, /*0xD806DC9D*/ /*U+01189D*/ /*U+01189D*/ /**/
+  0xD806DC9E, /*0xD806DC9E*/ /*U+01189E*/ /*U+01189E*/ /**/
+  0xD806DC9F, /*0xD806DC9F*/ /*U+01189F*/ /*U+01189F*/ /**/
+  0xD806DCC0, /*0xD806DCA0*/ /*U+0118C0*/ /*U+0118A0*/ /*WARANG CITI CAPITAL LETTER NGAA*/
+  0xD806DCC1, /*0xD806DCA1*/ /*U+0118C1*/ /*U+0118A1*/ /*WARANG CITI CAPITAL LETTER A*/
+  0xD806DCC2, /*0xD806DCA2*/ /*U+0118C2*/ /*U+0118A2*/ /*WARANG CITI CAPITAL LETTER WI*/
+  0xD806DCC3, /*0xD806DCA3*/ /*U+0118C3*/ /*U+0118A3*/ /*WARANG CITI CAPITAL LETTER YU*/
+  0xD806DCC4, /*0xD806DCA4*/ /*U+0118C4*/ /*U+0118A4*/ /*WARANG CITI CAPITAL LETTER YA*/
+  0xD806DCC5, /*0xD806DCA5*/ /*U+0118C5*/ /*U+0118A5*/ /*WARANG CITI CAPITAL LETTER YO*/
+  0xD806DCC6, /*0xD806DCA6*/ /*U+0118C6*/ /*U+0118A6*/ /*WARANG CITI CAPITAL LETTER II*/
+  0xD806DCC7, /*0xD806DCA7*/ /*U+0118C7*/ /*U+0118A7*/ /*WARANG CITI CAPITAL LETTER UU*/
+  0xD806DCC8, /*0xD806DCA8*/ /*U+0118C8*/ /*U+0118A8*/ /*WARANG CITI CAPITAL LETTER E*/
+  0xD806DCC9, /*0xD806DCA9*/ /*U+0118C9*/ /*U+0118A9*/ /*WARANG CITI CAPITAL LETTER O*/
+  0xD806DCCA, /*0xD806DCAA*/ /*U+0118CA*/ /*U+0118AA*/ /*WARANG CITI CAPITAL LETTER ANG*/
+  0xD806DCCB, /*0xD806DCAB*/ /*U+0118CB*/ /*U+0118AB*/ /*WARANG CITI CAPITAL LETTER GA*/
+  0xD806DCCC, /*0xD806DCAC*/ /*U+0118CC*/ /*U+0118AC*/ /*WARANG CITI CAPITAL LETTER KO*/
+  0xD806DCCD, /*0xD806DCAD*/ /*U+0118CD*/ /*U+0118AD*/ /*WARANG CITI CAPITAL LETTER ENY*/
+  0xD806DCCE, /*0xD806DCAE*/ /*U+0118CE*/ /*U+0118AE*/ /*WARANG CITI CAPITAL LETTER YUJ*/
+  0xD806DCCF, /*0xD806DCAF*/ /*U+0118CF*/ /*U+0118AF*/ /*WARANG CITI CAPITAL LETTER UC*/
+  0xD806DCD0, /*0xD806DCB0*/ /*U+0118D0*/ /*U+0118B0*/ /*WARANG CITI CAPITAL LETTER ENN*/
+  0xD806DCD1, /*0xD806DCB1*/ /*U+0118D1*/ /*U+0118B1*/ /*WARANG CITI CAPITAL LETTER ODD*/
+  0xD806DCD2, /*0xD806DCB2*/ /*U+0118D2*/ /*U+0118B2*/ /*WARANG CITI CAPITAL LETTER TTE*/
+  0xD806DCD3, /*0xD806DCB3*/ /*U+0118D3*/ /*U+0118B3*/ /*WARANG CITI CAPITAL LETTER NUNG*/
+  0xD806DCD4, /*0xD806DCB4*/ /*U+0118D4*/ /*U+0118B4*/ /*WARANG CITI CAPITAL LETTER DA*/
+  0xD806DCD5, /*0xD806DCB5*/ /*U+0118D5*/ /*U+0118B5*/ /*WARANG CITI CAPITAL LETTER AT*/
+  0xD806DCD6, /*0xD806DCB6*/ /*U+0118D6*/ /*U+0118B6*/ /*WARANG CITI CAPITAL LETTER AM*/
+  0xD806DCD7, /*0xD806DCB7*/ /*U+0118D7*/ /*U+0118B7*/ /*WARANG CITI CAPITAL LETTER BU*/
+  0xD806DCD8, /*0xD806DCB8*/ /*U+0118D8*/ /*U+0118B8*/ /*WARANG CITI CAPITAL LETTER PU*/
+  0xD806DCD9, /*0xD806DCB9*/ /*U+0118D9*/ /*U+0118B9*/ /*WARANG CITI CAPITAL LETTER HIYO*/
+  0xD806DCDA, /*0xD806DCBA*/ /*U+0118DA*/ /*U+0118BA*/ /*WARANG CITI CAPITAL LETTER HOLO*/
+  0xD806DCDB, /*0xD806DCBB*/ /*U+0118DB*/ /*U+0118BB*/ /*WARANG CITI CAPITAL LETTER HORR*/
+  0xD806DCDC, /*0xD806DCBC*/ /*U+0118DC*/ /*U+0118BC*/ /*WARANG CITI CAPITAL LETTER HAR*/
+  0xD806DCDD, /*0xD806DCBD*/ /*U+0118DD*/ /*U+0118BD*/ /*WARANG CITI CAPITAL LETTER SSUU*/
+  0xD806DCDE, /*0xD806DCBE*/ /*U+0118DE*/ /*U+0118BE*/ /*WARANG CITI CAPITAL LETTER SII*/
+  0xD806DCDF, /*0xD806DCBF*/ /*U+0118DF*/ /*U+0118BF*/ /*WARANG CITI CAPITAL LETTER VIYO*/
+};
+
 /* EOF */
index 896a2611e6b4857485b7a5e85b7a2db146ecdc3c..4a3003988c2351017f9df0adfff553b17d78c4f6 100644 (file)
@@ -34,7 +34,7 @@
 #include <atalk/unicode.h>
 #include <atalk/logger.h>
 #include <atalk/unicode.h>
-#include "byteorder.h"
+#include <atalk/byteorder.h>
 
 /* Given a trailing UTF-8 byte, get the contribution from it to
  * the Unicode scalar value for a particular bit shift amount
index ce3355df980c84c0f65e65f13f9b55ddd6556a86..5108a4c8e86f0ef8d73d97f7300f025eeae84b92 100644 (file)
@@ -22,8 +22,9 @@
 #include <arpa/inet.h>
 
 #include <atalk/unicode.h>
+#include <atalk/byteorder.h>
+
 #include "precompose.h"
-#include "byteorder.h"
 
 /*******************************************************************
  Convert a string to lower case.
@@ -291,12 +292,12 @@ int strcasecmp_w(const ucs2_t *a, const ucs2_t *b)
 
        while (*a && *b) {
                if ((0xD800 <= *a) && (*a < 0xDC00)) {
-                       if (ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1])) return ret;
+                       if ((ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1]))) return ret;
                        a++;
                        b++;
                        if (!(*a && *b)) return (tolower_w(*a) - tolower_w(*b)); /* avoid buffer over run */
                } else {
-                       if (ret = tolower_w(*a) - tolower_w(*b)) return ret;
+                       if ((ret = tolower_w(*a) - tolower_w(*b))) return ret;
                }
                a++;
                b++;
@@ -317,13 +318,13 @@ int strncasecmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
 
        while ((n < len) && *a && *b) {
                if ((0xD800 <= *a) && (*a < 0xDC00)) {
-                       if (ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1])) return ret;
+                       if ((ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1]))) return ret;
                        a++;
                        b++;
                        n++;
                        if (!((n < len) && *a && *b)) return (tolower_w(*a) - tolower_w(*b));
                } else {
-                       if (ret = tolower_w(*a) - tolower_w(*b)) return ret;
+                       if ((ret = tolower_w(*a) - tolower_w(*b))) return ret;
                }
                a++;
                b++;
@@ -607,7 +608,7 @@ size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
                                base_sp = ((uint32_t)base << 16) | (uint32_t)comb;
                                do {
                                        comb_sp = ((uint32_t)in[1] << 16) | (uint32_t)in[2];
-                                       if (result_sp = do_precomposition_sp(base_sp, comb_sp)) {
+                                       if ((result_sp = do_precomposition_sp(base_sp, comb_sp))) {
                                                base_sp = result_sp;
                                                i += 4;
                                                in +=2;
@@ -641,7 +642,7 @@ size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
                }
 
                /* Binary Search for BMP */
-               else if (result = do_precomposition(base, comb)) {
+               else if ((result = do_precomposition(base, comb))) {
                        base = result;
                }
                
index 65ad1d6ca9704b8e535a1aa6e71d2d1b5847e7fd..201951664cd4d81b2da6445ca4ebeb866f99be8d 100644 (file)
@@ -9,6 +9,7 @@ libutil_la_SOURCES = \
        cnid.c          \
        fault.c         \
        getiface.c      \
+       gettok.c        \
        locking.c   \
        logger.c        \
        module.c        \
index 0cc56d49514ea4ae5ba454a009c4bdf3e5620948..7fb079004cc586f9008e1248fb94b65b114b5c66 100644 (file)
@@ -35,7 +35,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/stat.h>
-
+#include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -80,8 +80,7 @@ bstring rel_path_in_vol(const char *path, const char *volpath)
         return NULL;
 
     EC_NEG1_LOG(cwd = open(".", O_RDONLY));
-
-    EC_ZERO_LOGSTR(lstat(path, &st), "lstat(%s): %s", path, strerror(errno));
+    EC_ZERO( lstat(path, &st) );
 
     if (path[0] == '/') {
         EC_NULL(fpath = bfromcstr(path));
@@ -134,3 +133,69 @@ EC_CLEANUP:
         return NULL;
     return fpath;
 }
+
+/*!
+ * Resolves CNID of a given path
+ *
+ * path might be:
+ * (a) relative:
+ *     "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ *     "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param cdb     (r) CNID db handle
+ * @param volpath (r) UNIX path of volume
+ * @param path    (r) path, see above
+ * @param did     (w) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_path(struct _cnid_db *cdb,
+                     const char *volpath,
+                     const char *path,
+                     cnid_t *did)
+{
+    EC_INIT;
+
+    cnid_t cnid;
+    bstring rpath = NULL;
+    bstring statpath = NULL;
+    struct bstrList *l = NULL;
+    struct stat st;
+
+    cnid = htonl(2);
+
+    EC_NULL(rpath = rel_path_in_vol(path, volpath));
+    EC_NULL(statpath = bfromcstr(volpath));
+    EC_ZERO(bcatcstr(statpath, "/"));
+
+    l = bsplit(rpath, '/');
+    for (int i = 0; i < l->qty ; i++) {
+        *did = cnid;
+
+        EC_ZERO( bconcat(statpath, l->entry[i]) );
+        EC_ZERO( lstat(cfrombstr(statpath), &st) );
+
+        if ((cnid = cnid_add(cdb,
+                             &st,
+                             *did,
+                             cfrombstr(l->entry[i]),
+                             blength(l->entry[i]),
+                             0)) == CNID_INVALID) {
+            EC_FAIL;
+        }
+        EC_ZERO(bcatcstr(statpath, "/"));
+    }
+
+EC_CLEANUP:
+    bdestroy(rpath);
+    bstrListDestroy(l);
+    bdestroy(statpath);
+    if (ret != 0)
+        return CNID_INVALID;
+
+    return cnid;
+}
index 3a32c12e0293a35329a95bef956c5000646c6eb4..f0ac17f1b41be54dc9ee86c091a96799c6bc2d36 100644 (file)
@@ -45,7 +45,9 @@
 
 #include <atalk/ftw.h>
 
+#ifndef HAVE_MEMPCPY
 #define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
+#endif
 
 #define NDEBUG 1
 #include <assert.h>
diff --git a/libatalk/util/gettok.c b/libatalk/util/gettok.c
new file mode 100644 (file)
index 0000000..4859fec
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include <atalk/globals.h>
+
+static char    *l_curr;
+static char    *l_end;
+
+void initline( int len, char *line)
+{
+    l_curr = line;
+    l_end = line + len;
+}
+
+#define ST_QUOTE       0
+#define ST_WORD                1
+#define ST_BEGIN       2
+
+int
+parseline(int len, char *token)
+{
+    char       *p, *e;
+    int                state;
+
+    state = ST_BEGIN;
+    p = token;
+    e = token + len;
+
+    for (;;) {
+        if ( l_curr > l_end ) {                        /* end of line */
+            *token = '\0';
+            return( -1 );
+        }
+
+        switch ( *l_curr ) {
+        case '"' :
+            if ( state == ST_QUOTE ) {
+                state = ST_WORD;
+            } else {
+                state = ST_QUOTE;
+            }
+            break;
+
+        case '\0' :
+        case '\t' :
+        case '\n' :
+        case ' ' :
+            if ( state == ST_WORD ) {
+                *p = '\0';
+                return( p - token );
+            }
+            if ( state != ST_QUOTE ) {
+                break;
+            }
+            /* FALL THROUGH */
+
+        default :
+            if ( state == ST_BEGIN ) {
+                state = ST_WORD;
+            }
+            if ( p > e ) {                     /* end of token */
+                *token = '\0';
+                return( -1 );
+            }
+            *p++ = *l_curr;
+            break;
+        }
+
+        l_curr++;
+    }
+}
+
+#ifdef notdef
+void parseline(char *token, char *user)
+{
+    char               *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
+    struct passwd      *pwent;
+    int                        quoted = 0;
+
+    while ( isspace( *p )) {
+        p++;
+    }
+
+    /*
+     * If we've reached the end of the line, or a comment,
+     * don't return any more tokens.
+     */
+    if ( *p == '\0' || *p == '#' ) {
+        *token = '\0';
+        return;
+    }
+
+    if ( *p == '"' ) {
+        p++;
+        quoted = 1;
+    }
+    while ( *p != '\0' && ( quoted || !isspace( *p ))) {
+        if ( *p == '"' ) {
+            if ( quoted ) {
+                *t = '\0';
+                break;
+            }
+            quoted = 1;
+            p++;
+        } else {
+            *t++ = *p++;
+        }
+    }
+    pos = p;
+    *t = '\0';
+
+    /*
+     * We got to the end of the line without closing an open quote
+     */
+    if ( *p == '\0' && quoted ) {
+        *token = '\0';
+        return;
+    }
+
+    t = token;
+    if ( *t == '~' ) {
+        t++;
+        if ( *t == '\0' || *t == '/' ) {
+            u = user;
+            if ( *t == '/' ) {
+                t++;
+            }
+        } else {
+            u = t;
+            if (( q = strchr( t, '/' )) == NULL ) {
+                t = "";
+            } else {
+                *q = '\0';
+                t = q + 1;
+            }
+        }
+        if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
+            *token = '\0';
+            return;
+        }
+        strcpy( buf, pwent->pw_dir );
+        if ( *t != '\0' ) {
+            strcat( buf, "/" );
+            strcat( buf, t );
+        }
+        strcpy( token, buf );
+    }
+    return;
+}
+#endif /* notdef */
index 2d5b913e8026bb8e3121d45efba5d7f5e7e1ea42..0b473b608ba38cf4534b13fc513f34390123b1d7 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: locking.c,v 1.4 2010-01-05 19:05:52 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index c8a2a0a8acd2845c43bfbd088e81b7a496855a1b..4bf0e33802551ee3fac2a17cfadae4117360ae2d 100644 (file)
@@ -59,10 +59,11 @@ Netatalk 2001 (c)
   "CNID",                            \
   "AFPDaemon",                       \
   "DSI",                             \
-  "ATalkDaemon",                     \
-  "PAPDaemon",                       \
   "UAMS",                            \
-  "end_of_list_marker"}              \
+  "FCE",                             \
+  "ad",                              \
+  "Spotlight",                       \
+  "end_of_list_marker"}
 
 /* =========================================================================
    Config
@@ -85,9 +86,10 @@ UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = {
     DEFAULT_LOG_CONFIG, /* logtype_cnid */
     DEFAULT_LOG_CONFIG, /* logtype_afpd */
     DEFAULT_LOG_CONFIG, /* logtype_dsi */
-    DEFAULT_LOG_CONFIG, /* logtype_atalkd */
-    DEFAULT_LOG_CONFIG, /* logtype_papd */
-    DEFAULT_LOG_CONFIG /* logtype_uams */
+    DEFAULT_LOG_CONFIG, /* logtype_uams */
+    DEFAULT_LOG_CONFIG, /* logtype_fce */
+    DEFAULT_LOG_CONFIG, /* logtype_ad */
+    DEFAULT_LOG_CONFIG  /* logtype_sl */
 };
 
 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility);
@@ -594,11 +596,8 @@ log:
                                  loglevel, logtype);
 
         /* If default wasnt setup its fd is -1 */
-        iov[0].iov_base = log_details_buffer;
-        iov[0].iov_len = strlen(log_details_buffer);
-        iov[1].iov_base = temp_buffer;
-        iov[1].iov_len = strlen(temp_buffer);
-        writev( fd,  iov, 2);
+        write(fd, log_details_buffer, strlen(log_details_buffer));
+        write(fd, temp_buffer, strlen(temp_buffer));
     } else {
         write(fd, temp_buffer, strlen(temp_buffer));
     }
index 1e370373b3f810a7de13ce96b8f72b0f4d50ea0b..b651476524c73230cb3f3875fd8288bd52e86ec2 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: module.c,v 1.5 2003-02-17 02:03:12 srittau Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 0c0a9416f0f60bb4c4948ee64f589d9ca5744ffc..b20446d2177c470d849b0e0e4578d62e56fe4f08 100644 (file)
@@ -51,6 +51,7 @@
 #include <atalk/uuid.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #define VOLPASSLEN  8
 #ifndef UUID_PRINTABLE_STRING_LENGTH
@@ -231,7 +232,7 @@ static int check_vol_acl_support(const struct vol *vol)
 {
     int ret = 0;
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     ace_t *aces = NULL;
     ret = 1;
     if (get_nfsv4_acl(vol->v_path, &aces) == -1)
@@ -244,7 +245,7 @@ static int check_vol_acl_support(const struct vol *vol)
         ret = 0;
 #endif
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     if (aces) free(aces);
 #endif
 #ifdef HAVE_POSIX_ACLS
@@ -401,8 +402,11 @@ static char *volxlate(const AFPObj *obj,
 /*!
  * check access list
  *
- * this function wants something of the following form:
- * "@group,name,name2,@group2,name3" or "@group name name2 @group2 name3"
+ * this function wants a string consisting of names seperated by comma
+ * or space. Names may be quoted within a pair of quotes. Groups are
+ * denoted by a leading @ symbol.
+ * Example:
+ * user1 user2, user3, @group1 @group2, @group3 "user name1", "@group name1"
  * A NULL argument allows everybody to have access.
  * We return three things:
  *     -1: no list
@@ -411,26 +415,31 @@ static char *volxlate(const AFPObj *obj,
  */
 static int accessvol(const AFPObj *obj, const char *args, const char *name)
 {
-    char buf[MAXPATHLEN + 1], *p;
+    EC_INIT;
+    char *names = NULL, *p;
     struct group *gr;
 
     if (!args)
-        return -1;
+        EC_EXIT_STATUS(-1);
 
-    strlcpy(buf, args, sizeof(buf));
-    if ((p = strtok(buf, ", ")) == NULL) /* nothing, return okay */
-        return -1;
+    EC_NULL_LOG( names = strdup(args) );
+
+    if ((p = strtok_quote(names, ", ")) == NULL) /* nothing, return okay */
+        EC_EXIT_STATUS(-1);
 
     while (p) {
         if (*p == '@') { /* it's a group */
             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid, obj->ngroups, obj->groups))
-                return 1;
+                EC_EXIT_STATUS(1);
         } else if (strcasecmp(p, name) == 0) /* it's a user name */
-            return 1;
-        p = strtok(NULL, ", ");
+            EC_EXIT_STATUS(1);
+        p = strtok_quote(NULL, ", ");
     }
 
-    return 0;
+EC_CLEANUP:
+    if (names)
+        free(names);
+    EC_EXIT;
 }
 
 static int hostaccessvol(const AFPObj *obj, const char *volname, const char *args)
@@ -505,13 +514,11 @@ static int hostaccessvol(const AFPObj *obj, const char *volname, const char *arg
  */
 static const char *getoption(const dictionary *conf, const char *vol, const char *opt, const char *defsec, const char *defval)
 {
-    EC_INIT;
     const char *result;
 
-    if ((!(result = iniparser_getstring(conf, vol, opt, NULL))) && (defsec != NULL))
-        result = iniparser_getstring(conf, defsec, opt, NULL);
+    if ((!(result = atalk_iniparser_getstring(conf, vol, opt, NULL))) && (defsec != NULL))
+        result = atalk_iniparser_getstring(conf, defsec, opt, NULL);
     
-EC_CLEANUP:
     if (result == NULL)
         result = defval;
     return result;
@@ -530,13 +537,11 @@ EC_CLEANUP:
  */
 static int getoption_bool(const dictionary *conf, const char *vol, const char *opt, const char *defsec, int defval)
 {
-    EC_INIT;
     int result;
 
-    if (((result = iniparser_getboolean(conf, vol, opt, -1)) == -1) && (defsec != NULL))
-        result = iniparser_getboolean(conf, defsec, opt, -1);
+    if (((result = atalk_iniparser_getboolean(conf, vol, opt, -1)) == -1) && (defsec != NULL))
+        result = atalk_iniparser_getboolean(conf, defsec, opt, -1);
     
-EC_CLEANUP:
     if (result == -1)
         result = defval;
     return result;
@@ -549,7 +554,7 @@ EC_CLEANUP:
  * @param pwd      (r) struct passwd of logged in user, may be NULL in master afpd
  * @param section  (r) volume name wo variables expanded (exactly as in iniconfig)
  * @param name     (r) volume name
- * @param path     (r) volume path
+ * @param path_in  (r) volume path
  * @param preset   (r) default preset, may be NULL
  * @returns            vol on success, NULL on error
  */
@@ -557,40 +562,57 @@ static struct vol *creatvol(AFPObj *obj,
                             const struct passwd *pwd,
                             const char *section,
                             const char *name,
-                            const char *path,
+                            const char *path_in,
                             const char *preset)
 {
     EC_INIT;
     struct vol  *volume = NULL;
     int         i, suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
-    char        *tmpname;
+    char        tmpname[AFPVOL_U8MNAMELEN+1];
+    char        path[MAXPATHLEN + 1];
     ucs2_t      u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
     char        suffix[6]; /* max is #FFFF */
     uint16_t    flags;
     const char  *val;
     char        *p, *q;
+    bstring     dbpath = NULL;
+    bstring     global_path_tmp = NULL;
+
+    strlcpy(path, path_in, MAXPATHLEN);
 
-    LOG(log_debug, logtype_afpd, "createvol(volume: '%s', path: \"%s\", preset: '%s'): BEGIN",
+    LOG(log_debug, logtype_afpd, "creatvol(volume: '%s', path: \"%s\", preset: '%s'): BEGIN",
         name, path, preset ? preset : "-");
 
     if ( name == NULL || *name == '\0' ) {
-        if ((name = strrchr( path, '/' )) == NULL) {
+        if ((name = strrchr( path, '/' )) == NULL)
             EC_FAIL;
-        }
-
         /* if you wish to share /, you need to specify a name. */
         if (*++name == '\0')
             EC_FAIL;
     }
 
     /* Once volumes are loaded, we never change options again, we just delete em when they're removed from afp.conf */
+
     for (struct vol *vol = Volumes; vol; vol = vol->v_next) {
-        if (STRCMP(path, ==, vol->v_path)) {
-            LOG(log_debug, logtype_afpd, "createvol('%s'): already loaded", name);
+        if (STRCMP(name, ==, vol->v_localname) && vol->v_deleted) {
+            /* 
+             * reloading config, volume still present, nothing else to do,
+             * we don't change options for volumes once they're loaded
+             */
             vol->v_deleted = 0;
             volume = vol;
-            goto EC_CLEANUP;
+            EC_EXIT_STATUS(0);
+        }
+        if (STRCMP(path, ==, vol->v_path)) {
+            LOG(log_note, logtype_afpd, "volume \"%s\" path \"%s\" is the same as volumes \"%s\" path",
+                name, path, vol->v_configname);
+            EC_EXIT_STATUS(0);
         }
+        /*
+         * We could check for nested volume paths here, but
+         * nobody was able to come up with an implementation yet,
+         * that is simple, fast and correct.
+         */
     }
 
     /*
@@ -616,14 +638,14 @@ static struct vol *creatvol(AFPObj *obj,
     volume->v_vfs_ea = AFPVOL_EA_AUTO;
     volume->v_umask = obj->options.umask;
 
-    if (val = getoption(obj->iniconfig, section, "password", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "password", preset, NULL)))
         EC_NULL( volume->v_password = strdup(val) );
 
-    if (val = getoption(obj->iniconfig, section, "veto files", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "veto files", preset, NULL)))
         EC_NULL( volume->v_veto = strdup(val) );
 
     /* vol charset is in [G] and [V] */
-    if (val = getoption(obj->iniconfig, section, "vol charset", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "vol charset", preset, NULL))) {
         if (strcasecmp(val, "UTF-8") == 0) {
             val = strdup("UTF8");
         }
@@ -633,7 +655,7 @@ static struct vol *creatvol(AFPObj *obj,
         EC_NULL( volume->v_volcodepage = strdup(obj->options.volcodepage) );
 
     /* mac charset is in [G] and [V] */
-    if (val = getoption(obj->iniconfig, section, "mac charset", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "mac charset", preset, NULL))) {
         if (strncasecmp(val, "MAC", 3) != 0) {
             LOG(log_warning, logtype_afpd, "Is '%s' really mac charset? ", val);
         }
@@ -643,46 +665,72 @@ static struct vol *creatvol(AFPObj *obj,
     EC_NULL( volume->v_maccodepage = strdup(obj->options.maccodepage) );
 
     vlen = strlen(name);
-    tmpname = strdup(name);
+    strlcpy(tmpname, name, sizeof(tmpname));
     for(i = 0; i < vlen; i++)
         if(tmpname[i] == '/') tmpname[i] = ':';
 
-    bstring dbpath;
-    EC_NULL_LOG( val = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
-    EC_NULL_LOG( dbpath = bformat("%s/%s/", val, tmpname) );
-    EC_NULL_LOG( volume->v_dbpath = strdup(bdata(dbpath)) );
-    bdestroy(dbpath);
 
-    if (val = getoption(obj->iniconfig, section, "cnid scheme", preset, NULL))
+    if (atalk_iniparser_getboolean(obj->iniconfig, INISEC_GLOBAL, "vol dbnest", 0)) {
+        EC_NULL( volume->v_dbpath = strdup(path) );
+    } else {
+        char *global_path;
+        val = getoption(obj->iniconfig, section, "vol dbpath", preset, NULL);
+        if (val == NULL) {
+            /* check global option */
+            global_path = atalk_iniparser_getstring(obj->iniconfig,
+                                                    INISEC_GLOBAL,
+                                                    "vol dbpath",
+                                                    NULL);
+            if (global_path) {
+                /* check for pre 3.1.1 behaviour without variable */
+                if (strchr(global_path, '$') == NULL) {
+                    global_path_tmp = bformat("%s/%s/", global_path, tmpname);
+                    val = cfrombstr(global_path_tmp);
+                } else {
+                    val = global_path;
+                }
+            }
+        }
+
+        if (val == NULL) {
+            EC_NULL( dbpath = bformat("%s/%s/", _PATH_STATEDIR "CNID/", tmpname) );
+        } else {
+            EC_NULL( dbpath = bfromcstr(val));
+        }
+        EC_NULL( volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN + 1,
+                                             cfrombstr(dbpath), pwd, NULL, tmpname) );
+    }
+
+    if ((val = getoption(obj->iniconfig, section, "cnid scheme", preset, NULL)))
         EC_NULL( volume->v_cnidscheme = strdup(val) );
     else
         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
 
-    if (val = getoption(obj->iniconfig, section, "umask", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "umask", preset, NULL)))
         volume->v_umask = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "directory perm", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "directory perm", preset, NULL)))
         volume->v_dperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "file perm", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "file perm", preset, NULL)))
         volume->v_fperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "vol size limit", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "vol size limit", preset, NULL)))
         volume->v_limitsize = (uint32_t)strtoul(val, NULL, 10);
 
-    if (val = getoption(obj->iniconfig, section, "preexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "preexec", preset, NULL)))
         EC_NULL( volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "postexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "postexec", preset, NULL)))
         EC_NULL( volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "root preexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "root preexec", preset, NULL)))
         EC_NULL( volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "root postexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "root postexec", preset, NULL)))
         EC_NULL( volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "appledouble", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "appledouble", preset, NULL))) {
         if (strcmp(val, "v2") == 0)
             volume->v_adouble = AD_VERSION2;
         else if (strcmp(val, "ea") == 0)
@@ -691,10 +739,10 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_adouble = AD_VERSION;
     }
 
-    if (val = getoption(obj->iniconfig, section, "cnid server", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "cnid server", preset, NULL))) {
         EC_NULL( p = strdup(val) );
         volume->v_cnidserver = p;
-        if (q = strrchr(val, ':')) {
+        if ((q = strrchr(val, ':'))) {
             *q++ = 0;
             volume->v_cnidport = strdup(q);
         } else {
@@ -706,7 +754,7 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_cnidport = strdup(obj->options.Cnid_port);
     }
 
-    if (val = getoption(obj->iniconfig, section, "ea", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "ea", preset, NULL))) {
         if (strcasecmp(val, "ad") == 0)
             volume->v_vfs_ea = AFPVOL_EA_AD;
         else if (strcasecmp(val, "sys") == 0)
@@ -715,7 +763,7 @@ static struct vol *creatvol(AFPObj *obj,
             volume->v_vfs_ea = AFPVOL_EA_NONE;
     }
 
-    if (val = getoption(obj->iniconfig, section, "casefold", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "casefold", preset, NULL))) {
         if (strcasecmp(val, "tolower") == 0)
             volume->v_casefold = AFPVOL_UMLOWER;
         else if (strcasecmp(val, "toupper") == 0)
@@ -750,12 +798,48 @@ static struct vol *creatvol(AFPObj *obj,
 #endif
     if (!getoption_bool(obj->iniconfig, section, "convert appledouble", preset, 1))
         volume->v_flags |= AFPVOL_NOV2TOEACONV;
+    if (getoption_bool(obj->iniconfig, section, "follow symlinks", preset, 0))
+        volume->v_flags |= AFPVOL_FOLLOWSYM;
+    if (getoption_bool(obj->iniconfig, section, "spotlight", preset, obj->options.flags & OPTION_SPOTLIGHT_VOL)) {
+        volume->v_flags |= AFPVOL_SPOTLIGHT;
+        obj->options.flags |= OPTION_SPOTLIGHT;
+    }
+    if (getoption_bool(obj->iniconfig, section, "delete veto files", preset, 0))
+        volume->v_flags |= AFPVOL_DELVETO;
 
     if (getoption_bool(obj->iniconfig, section, "preexec close", preset, 0))
         volume->v_preexec_close = 1;
     if (getoption_bool(obj->iniconfig, section, "root preexec close", preset, 0))
         volume->v_root_preexec_close = 1;
 
+    if ((val = getoption(obj->iniconfig, section, "ignored attributes", preset, obj->options.ignored_attr))) {
+        if (strstr(val, "all")) {
+            volume->v_ignattr |= ATTRBIT_NOWRITE | ATTRBIT_NORENAME | ATTRBIT_NODELETE;
+        }
+        if (strstr(val, "nowrite")) {
+            volume->v_ignattr |= ATTRBIT_NOWRITE;
+        }
+        if (strstr(val, "norename")) {
+            volume->v_ignattr |= ATTRBIT_NORENAME;
+        }
+        if (strstr(val, "nodelete")) {
+            volume->v_ignattr |= ATTRBIT_NODELETE;
+        }
+    }
+
+    val = getoption(obj->iniconfig, section, "chmod request", preset, NULL);
+    if (val == NULL) {
+        val = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "chmod request", "preserve");
+    }
+    if (strcasecmp(val, "ignore") == 0) {
+        volume->v_flags |= AFPVOL_CHMOD_IGNORE;
+    } else if (strcasecmp(val, "preserve") == 0) {
+        volume->v_flags |= AFPVOL_CHMOD_PRESERVE_ACL;
+    } else if (strcasecmp(val, "simple") != 0) {
+        LOG(log_warning, logtype_afpd, "unknown 'chmod request' setting: '%s', using default", val);
+        volume->v_flags |= AFPVOL_CHMOD_PRESERVE_ACL;
+    }
+
     /*
      * Handle read-only behaviour. semantics:
      * 1) neither the rolist nor the rwlist exist -> rw
@@ -777,6 +861,10 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_ad_options |= ADVOL_UNIXPRIV;
     if ((volume->v_flags & AFPVOL_INV_DOTS))
         volume->v_ad_options |= ADVOL_INVDOTS;
+    if ((volume->v_flags & AFPVOL_FOLLOWSYM))
+        volume->v_ad_options |= ADVOL_FOLLO_SYML;
+    if ((volume->v_flags & AFPVOL_RO))
+        volume->v_ad_options |= ADVOL_RO;
 
     /* Mac to Unix conversion flags*/
     if ((volume->v_flags & AFPVOL_EILSEQ))
@@ -802,7 +890,7 @@ static struct vol *creatvol(AFPObj *obj,
 
     /* Unicode Volume Name */
     /* Firstly convert name from unixcharset to UTF8-MAC */
-    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
+    flags = CONV_IGNORE;
     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -812,7 +900,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
         if (tmpvlen + suffixlen > obj->options.volnamelen) {
-            flags = CONV_FORCE | CONV_ALLOW_SLASH;
+            flags = CONV_FORCE;
             tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
         }
@@ -824,11 +912,11 @@ static struct vol *creatvol(AFPObj *obj,
     if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
         EC_FAIL;
 
-    LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
+    LOG(log_maxdebug, logtype_afpd, "creatvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
 
     /* Maccharset Volume Name */
     /* Firsty convert name from unixcharset to maccharset */
-    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
+    flags = CONV_IGNORE;
     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -838,7 +926,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
-            flags = CONV_FORCE | CONV_ALLOW_SLASH;
+            flags = CONV_FORCE;
             tmpvlen = convert_charset(obj->options.unixcharset,
                                       obj->options.maccharset,
                                       0,
@@ -862,15 +950,14 @@ static struct vol *creatvol(AFPObj *obj,
                                          AFPVOL_U8MNAMELEN*2)) )
         EC_FAIL;
 
-    LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
+    LOG(log_maxdebug, logtype_afpd, "creatvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
 
     EC_NULL( volume->v_localname = strdup(name) );
     EC_NULL( volume->v_u8mname = strdup_w(u8mtmpname) );
     EC_NULL( volume->v_macname = strdup_w(mactmpname) );
-    EC_NULL( volume->v_path = malloc(strlen(path) + 1) );
-
+    EC_NULL( volume->v_path = strdup(path) ); 
+        
     volume->v_name = utf8_encoding(obj) ? volume->v_u8mname : volume->v_macname;
-    strcpy(volume->v_path, path);
 
 #ifdef __svr4__
     volume->v_qfd = -1;
@@ -893,16 +980,16 @@ static struct vol *creatvol(AFPObj *obj,
     initvol_vfs(volume);
 
     /* get/store uuid from file in afpd master*/
-    if (!(pwd) && (volume->v_flags & AFPVOL_TM)) {
-        char *uuid = get_vol_uuid(obj, volume->v_localname);
-        if (!uuid) {
-            LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
-                volume->v_localname);
-        } else {
-            volume->v_uuid = uuid;
-            LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
-                volume->v_localname, volume->v_uuid);
-        }
+    become_root();
+    char *uuid = get_vol_uuid(obj, volume->v_localname);
+    unbecome_root();
+    if (!uuid) {
+        LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
+            volume->v_localname);
+    } else {
+        volume->v_uuid = uuid;
+        LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
+            volume->v_localname, volume->v_uuid);
     }
 
     /* no errors shall happen beyond this point because the cleanup would mess the volume chain up */
@@ -911,12 +998,14 @@ static struct vol *creatvol(AFPObj *obj,
     volume->v_obj = obj;
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_afpd, "createvol: END: %d", ret);
+    LOG(log_debug, logtype_afpd, "creatvol: END: %d", ret);
+    if (dbpath)
+        bdestroy(dbpath);
+    if (global_path_tmp)
+        bdestroy(global_path_tmp);
     if (ret != 0) {
-        if (volume) {
+        if (volume)
             volume_free(volume);
-            free(volume);
-        }
         return NULL;
     }
     return volume;
@@ -953,27 +1042,25 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
     EC_INIT;
     static int regexerr = -1;
     static regex_t reg;
-    char        path[MAXPATHLEN + 1];
+    char        *realvolpath;
     char        volname[AFPVOL_U8MNAMELEN + 1];
-    char        tmp[MAXPATHLEN + 1];
+    char        path[MAXPATHLEN + 1], tmp[MAXPATHLEN + 1];
     const char  *preset, *default_preset, *p, *basedir;
-    char        *q, *u;
     int         i;
-    struct passwd   *pw;
     regmatch_t match[1];
 
     LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
 
-    int secnum = iniparser_getnsec(obj->iniconfig);    
+    int secnum = atalk_iniparser_getnsec(obj->iniconfig);    
     LOG(log_debug, logtype_afpd, "readvolfile: sections: %d", secnum);
     const char *secname;
 
-    if ((default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL))) {
+    if ((default_preset = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL))) {
         LOG(log_debug, logtype_afpd, "readvolfile: default_preset: %s", default_preset);
     }
 
     for (i = 0; i < secnum; i++) { 
-        secname = iniparser_getsecname(obj->iniconfig, i);
+        secname = atalk_iniparser_getsecname(obj->iniconfig, i);
 
         if (!vol_section(secname))
             continue;
@@ -983,12 +1070,20 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 || strcmp(obj->username, obj->options.guest) == 0)
                 /* not an AFP session, but cnid daemon, dbd or ad util, or guest login */
                 continue;
-            if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir))
-                /* no user home */
+            if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir)) {
+                LOG(log_debug, logtype_afpd, "readvolfile: pwent->pw_dir: NULL or \"\" - no user home");
                 continue;
+            }
+            LOG(log_debug, logtype_afpd, "readvolfile: pwent->pw_dir: '%s'", pwent->pw_dir);
+
+            if ((realpath(pwent->pw_dir, tmp)) == NULL) {
+                LOG(log_debug, logtype_afpd, "readvolfile: Cannot get realpath '%s' (%s).", pwent->pw_dir, strerror(errno));
+                continue;
+            }
+            LOG(log_debug, logtype_afpd, "readvolfile: realpath pwent->pw_dir: '%s'", tmp);
 
             /* check if user home matches our "basedir regex" */
-            if ((basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
+            if ((basedir = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
                 LOG(log_error, logtype_afpd, "\"basedir regex =\" must be defined in [Homes] section");
                 continue;
             }
@@ -998,21 +1093,22 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 char errbuf[1024];
                 regerror(regexerr, &reg, errbuf, sizeof(errbuf));
                 LOG(log_debug, logtype_default, "readvolfile: bad basedir regex: %s", errbuf);
+                continue;
             }
 
-            if (regexec(&reg, pwent->pw_dir, 1, match, 0) == REG_NOMATCH) {
-                LOG(log_debug, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
-                    pwent->pw_dir, basedir);
+            if (regexec(&reg, tmp, 1, match, 0) == REG_NOMATCH) {
+                LOG(log_error, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
+                    tmp, basedir);
                 continue;
             }
 
-            strlcpy(tmp, pwent->pw_dir, MAXPATHLEN);
-            strlcat(tmp, "/", MAXPATHLEN);
-            if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
+            if ((p = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))) {
+                strlcat(tmp, "/", MAXPATHLEN);
                 strlcat(tmp, p, MAXPATHLEN);
+            }
         } else {
             /* Get path */
-            if ((p = iniparser_getstring(obj->iniconfig, secname, "path", NULL)) == NULL)
+            if ((p = atalk_iniparser_getstring(obj->iniconfig, secname, "path", NULL)) == NULL)
                 continue;
             strlcpy(tmp, p, MAXPATHLEN);
         }
@@ -1022,7 +1118,7 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
 
         /* do variable substitution for volume name */
         if (STRCMP(secname, ==, INISEC_HOMES)) {
-            p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
+            p = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
             if (strstr(p, "$u") == NULL) {
                 LOG(log_warning, logtype_afpd, "home name must contain $u.");
                 p = "$u's home";
@@ -1038,11 +1134,168 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
         if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, path, NULL) == NULL)
             continue;
 
-        preset = iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
+        preset = atalk_iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
+
+        if ((realvolpath = realpath_safe(path)) == NULL)
+            continue;
+
+        creatvol(obj, pwent, secname, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
+        free(realvolpath);
+    }
+
+// EC_CLEANUP:
+    EC_EXIT;
+}
+
+static struct extmap    *Extmap = NULL, *Defextmap = NULL;
+static int              Extmap_cnt;
+
+static int setextmap(char *ext, char *type, char *creator)
+{
+    EC_INIT;
+    struct extmap *em;
+    int           cnt;
+
+    if (Extmap == NULL) {
+        EC_NULL_LOG( Extmap = calloc(1, sizeof( struct extmap )) );
+    }
+
+    ext++;
+
+    for (em = Extmap, cnt = 0; em->em_ext; em++, cnt++)
+        if ((strdiacasecmp(em->em_ext, ext)) == 0)
+            goto EC_CLEANUP;
+
+    EC_NULL_LOG( Extmap = realloc(Extmap, sizeof(struct extmap) * (cnt + 2)) );
+    (Extmap + cnt + 1)->em_ext = NULL;
+    em = Extmap + cnt;
+
+    EC_NULL( em->em_ext = strdup(ext) );
+
+    if ( *type == '\0' ) {
+        memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type ));
+    } else {
+        memcpy(em->em_type, type, sizeof( em->em_type ));
+    }
+    if ( *creator == '\0' ) {
+        memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator ));
+    } else {
+        memcpy(em->em_creator, creator, sizeof( em->em_creator ));
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+/* -------------------------- */
+static int extmap_cmp(const void *map1, const void *map2)
+{
+    const struct extmap *em1 = map1;
+    const struct extmap *em2 = map2;
+    return strdiacasecmp(em1->em_ext, em2->em_ext);
+}
+
+static void sortextmap( void)
+{
+    struct extmap   *em;
+
+    Extmap_cnt = 0;
+    if ((em = Extmap) == NULL) {
+        return;
+    }
+    while (em->em_ext) {
+        em++;
+        Extmap_cnt++;
+    }
+    if (Extmap_cnt) {
+        qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp);
+        if (*Extmap->em_ext == 0) {
+            /* the first line is really "." the default entry,
+             * we remove the leading '.' in setextmap
+             */
+            Defextmap = Extmap;
+        }
+    }
+}
+
+static void free_extmap( void)
+{
+    struct extmap   *em;
+
+    if (Extmap) {
+        for ( em = Extmap; em->em_ext; em++) {
+            free (em->em_ext);
+        }
+        free(Extmap);
+        Extmap = NULL;
+        Defextmap = Extmap;
+        Extmap_cnt = 0;
+    }
+}
+
+static int ext_cmp_key(const void *key, const void *obj)
+{
+    const char          *p = key;
+    const struct extmap *em = obj;
+    return strdiacasecmp(p, em->em_ext);
+}
+
+struct extmap *getextmap(const char *path)
+{
+    char      *p;
+    struct extmap *em;
+
+    if (!Extmap_cnt || NULL == ( p = strrchr( path, '.' )) ) {
+        return( Defextmap );
+    }
+    p++;
+    if (!*p) {
+        return( Defextmap );
+    }
+    em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
+    if (em) {
+        return( em );
+    } else {
+        return( Defextmap );
+    }
+}
+
+struct extmap *getdefextmap(void)
+{
+    return( Defextmap );
+}
+
+static int readextmap(const char *file)
+{
+    EC_INIT;
+    FILE        *fp;
+    char        ext[256];
+    char        buf[256];
+    char        type[5], creator[5];
+
+    LOG(log_debug, logtype_afpd, "readextmap: loading \"%s\"", file);
 
-        creatvol(obj, pwent, secname, volname, path, preset ? preset : default_preset ? default_preset : NULL);
+    EC_NULL_LOGSTR( fp = fopen(file, "r"), "Couldn't open extension maping file %s", file);
+
+    while (fgets(buf, sizeof(buf), fp) != NULL) {
+        initline(strlen(buf), buf);
+        parseline(sizeof(ext) - 1, ext);
+
+        switch (ext[0]) {
+        case '.' :
+            parseline(sizeof(type) - 1, type);
+            parseline(sizeof(creator) - 1, creator);
+            setextmap(ext, type, creator);
+            LOG(log_debug, logtype_afpd, "readextmap: mapping: '%s' -> %s/%s", ext, type, creator);
+            break;
+        }
     }
 
+    sortextmap();
+    EC_ZERO( fclose(fp) );
+
+    LOG(log_debug, logtype_afpd, "readextmap: done", file);
+
 EC_CLEANUP:
     EC_EXIT;
 }
@@ -1076,12 +1329,14 @@ void volume_unlink(struct vol *volume)
 }
 
 /*!
- * Free all resources allocated in a struct vol, only struct dir *v_root can't be freed
+ * Free all resources allocated in a struct vol in load_volumes()
+ *
+ * Actually opening a volume (afp_openvol()) will allocate additional
+ * ressources which are freed in closevol()
  */
 void volume_free(struct vol *vol)
 {
-    LOG(log_debug, logtype_afpd, "volume_free('%s'): BEGIN", vol->v_localname);
-
+    free(vol->v_configname);
     free(vol->v_localname);
     free(vol->v_u8mname);
     free(vol->v_macname);
@@ -1096,10 +1351,12 @@ void volume_free(struct vol *vol)
     free(vol->v_uuid);
     free(vol->v_cnidserver);
     free(vol->v_cnidport);
+    free(vol->v_preexec);
     free(vol->v_root_preexec);
     free(vol->v_postexec);
+    free(vol->v_root_postexec);
 
-    LOG(log_debug, logtype_afpd, "volume_free: END");
+    free(vol);
 }
 
 /*!
@@ -1123,22 +1380,41 @@ int load_charset(struct vol *vol)
 /*!
  * Initialize volumes and load ini configfile
  *
- * Depending on the value of obj->uid either access checks are done (!=0) or skipped (=0)
- *
- * @param obj       (r) handle
- * @param delvol_fn (r) callback called for deleted volumes
+ * @param obj      (r) handle
+ * @param flags    (r) flags controlling volume load behaviour
  */
-int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *))
+int load_volumes(AFPObj *obj, lv_flags_t flags)
 {
     EC_INIT;
-    int fd = -1;
-    struct passwd   *pwent = NULL;
+
+    static long         bufsize;
+    static char        *pwbuf = NULL;
+
+    int                 fd = -1;
+    struct passwd       pwent;
+    struct passwd      *pwresult = NULL;
     struct stat         st;
-    int retries = 0;
-    struct vol *vol;
+    int                 retries = 0;
+    struct vol         *vol;
 
     LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
 
+    if (pwbuf == NULL) {
+        bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+        if (bufsize == -1)          /* Value was indeterminate */
+            bufsize = 16384;        /* Should be more than enough */
+        EC_NULL( pwbuf = malloc(bufsize) );
+    }
+
+    if (!(flags & lv_all) && obj->uid) {
+        ret = getpwuid_r(obj->uid, &pwent, pwbuf, bufsize, &pwresult);
+        if (pwresult == NULL) {
+            LOG(log_error, logtype_afpd, "load_volumes: getpwuid_r: %s", strerror(errno));
+            EC_FAIL;
+        }
+        pwresult = &pwent;
+    }
+
     if (Volumes) {
         if (!volfile_changed(&obj->options))
             goto EC_CLEANUP;
@@ -1146,6 +1422,15 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
         for (vol = Volumes; vol; vol = vol->v_next) {
             vol->v_deleted = 1;
         }
+        if (obj->uid && pwresult) {
+            become_root();
+            ret = set_groups(obj, pwresult);
+            unbecome_root();
+            if (ret != 0) {
+                LOG(log_error, logtype_afpd, "load_volumes: set_groups: %s", strerror(errno));
+                EC_FAIL;
+            }
+        }
     } else {
         LOG(log_debug, logtype_afpd, "load_volumes: no volumes yet");
         EC_ZERO_LOG( lstat(obj->options.configfile, &st) );
@@ -1170,22 +1455,31 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
         break;
     }
 
-    if (obj->uid)
-        pwent = getpwuid(obj->uid);
-
     if (obj->iniconfig)
-        iniparser_freedict(obj->iniconfig);
+        atalk_iniparser_freedict(obj->iniconfig);
     LOG(log_debug, logtype_afpd, "load_volumes: loading: %s", obj->options.configfile);
-    obj->iniconfig = iniparser_load(obj->options.configfile);
+    obj->iniconfig = atalk_iniparser_load(obj->options.configfile);
 
-    EC_ZERO_LOG( readvolfile(obj, pwent) );
+    EC_ZERO_LOG( readvolfile(obj, pwresult) );
 
-    for ( vol = Volumes; vol; vol = vol->v_next ) {
-        if (vol->v_deleted) {
+    struct vol *p, *prevvol;
+
+    vol = Volumes;
+    prevvol = NULL;
+
+    while (vol) {
+        if (vol->v_deleted && !(vol->v_flags & AFPVOL_OPEN)) {
             LOG(log_debug, logtype_afpd, "load_volumes: deleted: %s", vol->v_localname);
-            if (delvol_fn)
-                delvol_fn(obj, vol);
-            vol = Volumes;
+            if (prevvol)
+                prevvol->v_next = vol->v_next;
+            else
+                Volumes = NULL;
+            p = vol->v_next;
+            volume_free(vol);
+            vol = p;
+        } else {
+            prevvol = vol;
+            vol = vol->v_next;
         }
     }
 
@@ -1199,12 +1493,16 @@ EC_CLEANUP:
 
 void unload_volumes(AFPObj *obj)
 {
-    struct vol *vol;
+    struct vol *vol, *p;
 
     LOG(log_debug, logtype_afpd, "unload_volumes: BEGIN");
 
-    for (vol = Volumes; vol; vol = vol->v_next)
+    p = Volumes;
+    while (p) {
+        vol = p;
+        p = vol->v_next;
         volume_free(vol);
+    }
     Volumes = NULL;
     obj->options.volfile.mtime = 0;
     
@@ -1232,6 +1530,56 @@ struct vol *getvolbyvid(const uint16_t vid )
     return( vol );
 }
 
+/*
+ * get username by path
+ * 
+ * getvolbypath() assumes that the user home directory has the same name as the username.
+ * If that is not true, getuserbypath() is called and tries to retrieve the username
+ * from the directory owner, checking its validity.
+ * 
+ * @param   path (r) absolute volume path
+ * @returns NULL     if no match is found, pointer to username if successfull
+ *
+ */ 
+static char *getuserbypath(const char *path)
+{
+    EC_INIT;
+    struct stat sbuf;
+    struct passwd  *pwd;
+    char *hdir = NULL;
+
+    LOG(log_debug, logtype_afpd, "getuserbypath(\"%s\")", path);
+
+    /* does folder exists? */
+    if (stat(path, &sbuf) != 0)
+        EC_FAIL;
+
+    /* get uid of dir owner */
+    if ((pwd = getpwuid(sbuf.st_uid)) == NULL)
+        EC_FAIL;
+
+    /* does user home directory exists? */
+    if (stat(pwd->pw_dir, &sbuf) != 0)
+        EC_FAIL;
+
+    /* resolve and remove symlinks */
+    if ((hdir = realpath_safe(pwd->pw_dir)) == NULL) 
+        EC_FAIL;
+
+    /* handle subdirectories, path = */
+    if (strncmp(path, hdir, strlen(hdir)) != 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_afpd, "getuserbypath: match user: %s, home: %s, realhome: %s",
+        pwd->pw_name, pwd->pw_dir, hdir);
+
+EC_CLEANUP:
+    if (hdir)
+        free(hdir);
+    if (ret != 0)
+        return NULL;
+    return pwd->pw_name;
+}
 /*!
  * Search volume by path, creating user home vols as necessary
  *
@@ -1247,6 +1595,9 @@ struct vol *getvolbyvid(const uint16_t vid )
  * (3) If there is, match "path" with "basedir regex" to get the user home parent dir
  * (4) Built user home path by appending the basedir matched in (3) and appending the username
  * (5) The next path element then is the username
+ * (5b) getvolbypath() assumes that the user home directory has the same name as the username.
+ *     If that is not true, getuserbypath() is called and tries to retrieve the username
+ *     from the directory owner, checking its validity
  * (6) Append [Homes]->path subdirectory if defined
  * (7) Create volume
  *
@@ -1262,8 +1613,8 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     struct vol *tmp;
     const struct passwd *pw;
     char        volname[AFPVOL_U8MNAMELEN + 1];
-    char        abspath[MAXPATHLEN + 1];
-    char        volpath[MAXPATHLEN + 1];
+    char        *realabspath = NULL;
+    char        volpath[MAXPATHLEN + 1], *realvolpath = NULL;
     char        tmpbuf[MAXPATHLEN + 1];
     const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
     char *user = NULL, *prw;
@@ -1271,29 +1622,32 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
 
     LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\")", path);
 
-    if (path[0] != '/') {
-        /* relative path, build absolute path */
-        EC_NULL_LOG( getcwd(abspath, MAXPATHLEN) );
-        strlcat(abspath, "/", MAXPATHLEN);
-        strlcat(abspath, path, MAXPATHLEN);
-        path = abspath;
-    }
-
+    /*  build absolute path */
+    EC_NULL( realabspath = realpath_safe(path) );
+    path = realabspath;
 
     for (tmp = Volumes; tmp; tmp = tmp->v_next) { /* (1) */
-        if (strncmp(path, tmp->v_path, strlen(tmp->v_path)) == 0) {
-            vol = tmp;
-            goto EC_CLEANUP;
+        size_t v_path_len = strlen(tmp->v_path);
+        if (strncmp(path, tmp->v_path, v_path_len) == 0) {
+            if (v_path_len < strlen(path) && path[v_path_len] != '/') {
+                LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") != volume(\"%s\")", path, tmp->v_path);
+            } else {
+                LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") == volume(\"%s\")", path, tmp->v_path);
+                vol = tmp;
+                goto EC_CLEANUP;
+            }
+        } else {
+            LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") != volume(\"%s\")", path, tmp->v_path);
         }
     }
 
     if (!have_uservol) /* (2) */
         EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
 
-    int secnum = iniparser_getnsec(obj->iniconfig);
+    int secnum = atalk_iniparser_getnsec(obj->iniconfig);
 
     for (int i = 0; i < secnum; i++) { 
-        secname = iniparser_getsecname(obj->iniconfig, i);
+        secname = atalk_iniparser_getsecname(obj->iniconfig, i);
         if (STRCMP(secname, ==, INISEC_HOMES))
             break;
     }
@@ -1302,7 +1656,7 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
         EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
 
     /* (3) */
-    EC_NULL_LOG( basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL) );
+    EC_NULL_LOG( basedir = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL) );
     LOG(log_debug, logtype_afpd, "getvolbypath: user home section: '%s', basedir: '%s'", secname, basedir);
 
     if (regexerr != 0 && (regexerr = regcomp(&reg, basedir, REG_EXTENDED)) != 0) {
@@ -1328,25 +1682,37 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
     /* (5) */
-    p = path + strlen(basedir);
+    p = path + match[0].rm_eo - match[0].rm_so;
     while (*p == '/')
         p++;
     EC_NULL_LOG( user = strdup(p) );
 
-    if (prw = strchr(user, '/'))
+    if ((prw = strchr(user, '/')))
         *prw++ = 0;
     if (prw != 0)
         subpath = prw;
 
     strlcat(tmpbuf, user, MAXPATHLEN);
+    if ((pw = getpwnam(user)) == NULL) {
+        /* (5b) */
+        char *tuser;
+        if ((tuser = getuserbypath(tmpbuf)) != NULL) {
+            free(user);
+            user = strdup(tuser);
+        }
+        if ((pw = getpwnam(user)) == NULL)
+            EC_FAIL_LOG("unknown user: %s", user);
+    }
     strlcpy(obj->username, user, MAXUSERLEN);
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
     /* (6) */
-    if (subpathconfig = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL)) {
+    if ((subpathconfig = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))) {
+        /*
         if (!subpath || strncmp(subpathconfig, subpath, strlen(subpathconfig)) != 0) {
             EC_FAIL;
         }
+        */
         strlcat(tmpbuf, subpathconfig, MAXPATHLEN);
         strlcat(tmpbuf, "/", MAXPATHLEN);
     }
@@ -1354,29 +1720,34 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
 
     /* (7) */
     if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
-        return NULL;
+        EC_FAIL;
 
+    EC_NULL( realvolpath = realpath_safe(volpath) );
     EC_NULL( pw = getpwnam(user) );
 
-    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => volpath: \"%s\"",
-        path, user, pw->pw_dir, volpath);
+    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => realvolpath: \"%s\"",
+        path, user, pw->pw_dir, realvolpath);
 
     /* do variable substitution for volume name */
-    p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
+    p = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
     if (strstr(p, "$u") == NULL)
         p = "$u's home";
     strlcpy(tmpbuf, p, AFPVOL_U8MNAMELEN);
-    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, volpath, NULL) );
+    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, realvolpath, NULL) );
 
     const char  *preset, *default_preset;
-    default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL);
-    preset = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "vol preset", NULL);
+    default_preset = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL);
+    preset = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "vol preset", NULL);
 
-    vol = creatvol(obj, pw, INISEC_HOMES, volname, volpath, preset ? preset : default_preset ? default_preset : NULL);
+    vol = creatvol(obj, pw, INISEC_HOMES, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
 
 EC_CLEANUP:
     if (user)
         free(user);
+    if (realvolpath)
+        free(realvolpath);
+    if (realabspath)
+        free(realabspath);
     if (ret != 0)
         vol = NULL;
     return vol;
@@ -1405,8 +1776,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     EC_INIT;
     dictionary *config;
     struct afp_options *options = &AFPObj->options;
-    int i, c;
-    const char *p, *tmp;
+    int c;
+    const char *p;
     char *q, *r;
     char val[MAXVAL];
 
@@ -1417,72 +1788,103 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->configfile  = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf");
     options->sigconffile = strdup(_PATH_STATEDIR "afp_signature.conf");
     options->uuidconf    = strdup(_PATH_STATEDIR "afp_voluuid.conf");
+#ifdef HAVE_TRACKER_SPARQL
+    options->slmod_path  = strdup(_PATH_AFPDUAMPATH "slmod_sparql.so");
+#endif
     options->flags       = OPTION_UUID | AFPObj->cmdlineflags;
     
-    if ((config = iniparser_load(AFPObj->options.configfile)) == NULL)
+    if ((config = atalk_iniparser_load(AFPObj->options.configfile)) == NULL)
         return -1;
     AFPObj->iniconfig = config;
 
     /* [Global] */
-    options->logconfig = iniparser_getstrdup(config, INISEC_GLOBAL, "log level", "default:note");
-    options->logfile   = iniparser_getstrdup(config, INISEC_GLOBAL, "log file",  NULL);
+    options->logconfig = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "log level", "default:note");
+    options->logfile   = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "log file",  NULL);
 
     setuplog(options->logconfig, options->logfile);
 
     /* "server options" boolean options */
-    if (!iniparser_getboolean(config, INISEC_GLOBAL, "zeroconf", 1))
+    if (!atalk_iniparser_getboolean(config, INISEC_GLOBAL, "zeroconf", 1))
         options->flags |= OPTION_NOZEROCONF;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "advertise ssh", 0))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "advertise ssh", 0))
         options->flags |= OPTION_ANNOUNCESSH;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "map acls", 1))
-        options->flags |= OPTION_ACL2MACCESS;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "keep sessions", 0))
-        options->flags |= OPTION_KEEPSESSIONS;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "close vol", 0))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "close vol", 0))
         options->flags |= OPTION_CLOSEVOL;
-    if (!iniparser_getboolean(config, INISEC_GLOBAL, "client polling", 0))
+    if (!atalk_iniparser_getboolean(config, INISEC_GLOBAL, "client polling", 0))
         options->flags |= OPTION_SERVERNOTIF;
-    if (!iniparser_getboolean(config, INISEC_GLOBAL, "use sendfile", 1))
+    if (!atalk_iniparser_getboolean(config, INISEC_GLOBAL, "use sendfile", 1))
         options->flags |= OPTION_NOSENDFILE;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "solaris share reservations", 1))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "recvfile", 0))
+        options->flags |= OPTION_RECVFILE;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "solaris share reservations", 1))
         options->flags |= OPTION_SHARE_RESERV;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "afpstats", 0))
+        options->flags |= OPTION_DBUS_AFPSTATS;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
         options->flags |= OPTION_AFP_READ_LOCK;
-    if (!iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "spotlight", 0))
+        options->flags |= OPTION_SPOTLIGHT_VOL;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "veto message", 0))
+        options->flags |= OPTION_VETOMSG;
+    if (!atalk_iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
         options->passwdbits |= PASSWD_NOSAVE;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
         options->passwdbits |= PASSWD_SET;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "spotlight expr", 1))
+        options->flags |= OPTION_SPOTLIGHT_EXPR;
 
     /* figure out options w values */
-    options->loginmesg      = iniparser_getstrdup(config, INISEC_GLOBAL, "login message",  NULL);
-    options->guest          = iniparser_getstrdup(config, INISEC_GLOBAL, "guest account",  "nobody");
-    options->passwdfile     = iniparser_getstrdup(config, INISEC_GLOBAL, "passwd file",_PATH_AFPDPWFILE);
-    options->uampath        = iniparser_getstrdup(config, INISEC_GLOBAL, "uam path",       _PATH_AFPDUAMPATH);
-    options->uamlist        = iniparser_getstrdup(config, INISEC_GLOBAL, "uam list",       "uams_dhx.so uams_dhx2.so");
-    options->port           = iniparser_getstrdup(config, INISEC_GLOBAL, "afp port",       "548");
-    options->signatureopt   = iniparser_getstrdup(config, INISEC_GLOBAL, "signature",      "");
-    options->k5service      = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 service",     NULL);
-    options->k5realm        = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 realm",       NULL);
-    options->listen         = iniparser_getstrdup(config, INISEC_GLOBAL, "afp listen",     NULL);
-    options->ntdomain       = iniparser_getstrdup(config, INISEC_GLOBAL, "nt domain",      NULL);
-    options->ntseparator    = iniparser_getstrdup(config, INISEC_GLOBAL, "nt separator",   NULL);
-    options->mimicmodel     = iniparser_getstrdup(config, INISEC_GLOBAL, "mimic model",    NULL);
-    options->adminauthuser  = iniparser_getstrdup(config, INISEC_GLOBAL, "admin auth user",NULL);
-    options->connections    = iniparser_getint   (config, INISEC_GLOBAL, "max connections",200);
-    options->passwdminlen   = iniparser_getint   (config, INISEC_GLOBAL, "passwd minlen",  0);
-    options->tickleval      = iniparser_getint   (config, INISEC_GLOBAL, "tickleval",      30);
-    options->timeout        = iniparser_getint   (config, INISEC_GLOBAL, "timeout",        4);
-    options->dsireadbuf     = iniparser_getint   (config, INISEC_GLOBAL, "dsireadbuf",     12);
-    options->server_quantum = iniparser_getint   (config, INISEC_GLOBAL, "server quantum", DSI_SERVQUANT_DEF);
-    options->volnamelen     = iniparser_getint   (config, INISEC_GLOBAL, "volnamelen",     80);
-    options->dircachesize   = iniparser_getint   (config, INISEC_GLOBAL, "dircachesize",   DEFAULT_MAX_DIRCACHE_SIZE);
-    options->tcp_sndbuf     = iniparser_getint   (config, INISEC_GLOBAL, "tcpsndbuf",      0);
-    options->tcp_rcvbuf     = iniparser_getint   (config, INISEC_GLOBAL, "tcprcvbuf",      0);
-    options->fce_fmodwait   = iniparser_getint   (config, INISEC_GLOBAL, "fce holdfmod",   60);
-    options->sleep          = iniparser_getint   (config, INISEC_GLOBAL, "sleep time",     10);
-    options->disconnected   = iniparser_getint   (config, INISEC_GLOBAL, "disconnect time",24);
-
-    if ((p = iniparser_getstring(config, INISEC_GLOBAL, "hostname", NULL))) {
+    options->loginmesg      = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "login message",  NULL);
+    options->guest          = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "guest account",  "nobody");
+    options->extmapfile     = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "extmap file",    _PATH_CONFDIR "extmap.conf");
+    options->passwdfile     = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "passwd file",    _PATH_AFPDPWFILE);
+    options->uampath        = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "uam path",       _PATH_AFPDUAMPATH);
+    options->uamlist        = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "uam list",       "uams_dhx.so uams_dhx2.so");
+    options->port           = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "afp port",       "548");
+    options->signatureopt   = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "signature",      "");
+    options->k5service      = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "k5 service",     NULL);
+    options->k5realm        = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "k5 realm",       NULL);
+    options->listen         = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "afp listen",     NULL);
+    options->interfaces     = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "afp interfaces", NULL);
+    options->ntdomain       = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "nt domain",      NULL);
+    options->addomain       = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "ad domain",      NULL);
+    options->ntseparator    = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "nt separator",   NULL);
+    options->mimicmodel     = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "mimic model",    NULL);
+    options->adminauthuser  = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "admin auth user",NULL);
+    options->ignored_attr   = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "ignored attributes", NULL);
+    options->cnid_mysql_host = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "cnid mysql host", NULL);
+    options->cnid_mysql_user = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "cnid mysql user", NULL);
+    options->cnid_mysql_pw  = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "cnid mysql pw", NULL);
+    options->cnid_mysql_db  = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "cnid mysql db", NULL);
+    options->connections    = atalk_iniparser_getint   (config, INISEC_GLOBAL, "max connections",200);
+    options->passwdminlen   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "passwd minlen",  0);
+    options->tickleval      = atalk_iniparser_getint   (config, INISEC_GLOBAL, "tickleval",      30);
+    options->timeout        = atalk_iniparser_getint   (config, INISEC_GLOBAL, "timeout",        4);
+    options->dsireadbuf     = atalk_iniparser_getint   (config, INISEC_GLOBAL, "dsireadbuf",     12);
+    options->server_quantum = atalk_iniparser_getint   (config, INISEC_GLOBAL, "server quantum", DSI_SERVQUANT_DEF);
+    options->volnamelen     = atalk_iniparser_getint   (config, INISEC_GLOBAL, "volnamelen",     80);
+    options->dircachesize   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "dircachesize",   DEFAULT_MAX_DIRCACHE_SIZE);
+    options->tcp_sndbuf     = atalk_iniparser_getint   (config, INISEC_GLOBAL, "tcpsndbuf",      0);
+    options->tcp_rcvbuf     = atalk_iniparser_getint   (config, INISEC_GLOBAL, "tcprcvbuf",      0);
+    options->fce_fmodwait   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "fce holdfmod",   60);
+    options->sleep          = atalk_iniparser_getint   (config, INISEC_GLOBAL, "sleep time",     10);
+    options->disconnected   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "disconnect time",24);
+    options->splice_size    = atalk_iniparser_getint   (config, INISEC_GLOBAL, "splice size",    64*1024);
+    options->sparql_limit   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "sparql results limit", 0);
+
+    p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "map acls", "rights");
+    if (STRCMP(p, ==, "rights"))
+        options->flags |= OPTION_ACL2MACCESS;
+    else if (STRCMP(p, ==, "mode"))
+        options->flags |= OPTION_ACL2MODE | OPTION_ACL2MACCESS;
+    else {
+        if (STRCMP(p, !=, "none")) {
+            LOG(log_error, logtype_afpd, "bad ACL mapping option: %s, defaulting to 'rights'", p);
+            options->flags |= OPTION_ACL2MACCESS;
+        }
+    }
+
+    if ((p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "hostname", NULL))) {
         EC_NULL_LOG( options->hostname = strdup(p) );
     } else {
         if (gethostname(val, sizeof(val)) < 0 ) {
@@ -1494,21 +1896,21 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->hostname = strdup(val);
     }
 
-    if ((p = iniparser_getstring(config, INISEC_GLOBAL, "k5 keytab", NULL))) {
+    if ((p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "k5 keytab", NULL))) {
         EC_NULL_LOG( options->k5keytab = malloc(strlen(p) + 14) );
         snprintf(options->k5keytab, strlen(p) + 14, "KRB5_KTNAME=%s", p);
         putenv(options->k5keytab);
     }
 
 #ifdef ADMIN_GRP
-    if ((p = iniparser_getstring(config, INISEC_GLOBAL, "admin group",  NULL))) {
+    if ((p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "admin group",  NULL))) {
          struct group *gr = getgrnam(p);
          if (gr != NULL)
              options->admingid = gr->gr_gid;
     }
 #endif /* ADMIN_GRP */
 
-    q = iniparser_getstrdup(config, INISEC_GLOBAL, "cnid server", "localhost:4700");
+    q = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "cnid server", "localhost:4700");
     r = strrchr(q, ':');
     if (r)
         *r = 0;
@@ -1521,7 +1923,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     if (q)
         free(q);
 
-    if ((q = iniparser_getstrdup(config, INISEC_GLOBAL, "fqdn", NULL))) {
+    if ((q = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "fqdn", NULL))) {
         /* do a little checking for the domain name. */
         r = strchr(q, ':');
         if (r)
@@ -1531,7 +1933,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
                 *r = ':';
             EC_NULL_LOG( options->fqdn = strdup(q) );
         } else {
-            LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", c);
+            LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", q);
         }
         free(q);
     }
@@ -1539,7 +1941,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     /* Charset Options */
 
     /* unix charset is in [G] only */
-    if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "unix charset", NULL))) {
+    if (!(p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "unix charset", NULL))) {
         options->unixcodepage = strdup("UTF8");
         set_charset_name(CH_UNIX, "UTF8");
     } else {
@@ -1550,11 +1952,11 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
             LOG(log_debug, logtype_afpd, "Locale charset is '%s'", p);
 #else /* system doesn't have LOCALE support */
             LOG(log_warning, logtype_afpd, "system doesn't have LOCALE support");
-            p = strdup("UTF8");
+            p = "UTF8";
 #endif
         }
         if (strcasecmp(p, "UTF-8") == 0) {
-            p = strdup("UTF8");
+            p = "UTF8";
         }
         options->unixcodepage = strdup(p);
         set_charset_name(CH_UNIX, p);
@@ -1563,18 +1965,18 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     LOG(log_debug, logtype_afpd, "Global unix charset is %s", options->unixcodepage);
 
     /* vol charset is in [G] and [V] */
-    if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "vol charset", NULL))) {
+    if (!(p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "vol charset", NULL))) {
         options->volcodepage = strdup(options->unixcodepage);
     } else {
         if (strcasecmp(p, "UTF-8") == 0) {
-            p = strdup("UTF8");
+            p = "UTF8";
         }
         options->volcodepage = strdup(p);
     }
     LOG(log_debug, logtype_afpd, "Global vol charset is %s", options->volcodepage);
     
     /* mac charset is in [G] and [V] */
-    if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "mac charset", NULL))) {
+    if (!(p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "mac charset", NULL))) {
         options->maccodepage = strdup("MAC_ROMAN");
         set_charset_name(CH_MAC, "MAC_ROMAN");
     } else {
@@ -1587,6 +1989,11 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->maccharset = CH_MAC;
     LOG(log_debug, logtype_afpd, "Global mac charset is %s", options->maccodepage);
 
+    if (readextmap(options->extmapfile) != 0) {
+        LOG(log_error, logtype_afpd, "Couldn't load extension -> type/creator mappings file \"%s\"",
+            options->extmapfile);
+    }
+
     /* Check for sane values */
     if (options->tickleval <= 0)
         options->tickleval = 30;
@@ -1606,3 +2013,88 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
 EC_CLEANUP:
     EC_EXIT;
 }
+
+#define CONFIG_ARG_FREE(a) do {                     \
+    free(a);                                        \
+    a = NULL;                                       \
+    } while (0);
+
+/* get rid of any allocated afp_option buffers. */
+void afp_config_free(AFPObj *obj)
+{
+    if (obj->options.configfile)
+        CONFIG_ARG_FREE(obj->options.configfile);
+    if (obj->options.sigconffile)
+        CONFIG_ARG_FREE(obj->options.sigconffile);
+    if (obj->options.uuidconf)
+        CONFIG_ARG_FREE(obj->options.uuidconf);
+    if (obj->options.logconfig)
+        CONFIG_ARG_FREE(obj->options.logconfig);
+    if (obj->options.logfile)
+        CONFIG_ARG_FREE(obj->options.logfile);
+    if (obj->options.loginmesg)
+        CONFIG_ARG_FREE(obj->options.loginmesg);
+    if (obj->options.guest)
+        CONFIG_ARG_FREE(obj->options.guest);
+    if (obj->options.extmapfile)
+        CONFIG_ARG_FREE(obj->options.extmapfile);
+    if (obj->options.passwdfile)
+        CONFIG_ARG_FREE(obj->options.passwdfile);
+    if (obj->options.uampath)
+        CONFIG_ARG_FREE(obj->options.uampath);
+    if (obj->options.uamlist)
+        CONFIG_ARG_FREE(obj->options.uamlist);
+    if (obj->options.port)
+        CONFIG_ARG_FREE(obj->options.port);
+    if (obj->options.signatureopt)
+        CONFIG_ARG_FREE(obj->options.signatureopt);
+    if (obj->options.k5service)
+        CONFIG_ARG_FREE(obj->options.k5service);
+    if (obj->options.k5realm)
+        CONFIG_ARG_FREE(obj->options.k5realm);
+    if (obj->options.k5principal)
+        CONFIG_ARG_FREE(obj->options.k5principal);
+    if (obj->options.listen)
+        CONFIG_ARG_FREE(obj->options.listen);
+    if (obj->options.interfaces)
+        CONFIG_ARG_FREE(obj->options.interfaces);
+    if (obj->options.ntdomain)
+        CONFIG_ARG_FREE(obj->options.ntdomain);
+    if (obj->options.addomain)
+        CONFIG_ARG_FREE(obj->options.addomain);
+    if (obj->options.ntseparator)
+        CONFIG_ARG_FREE(obj->options.ntseparator);
+    if (obj->options.mimicmodel)
+        CONFIG_ARG_FREE(obj->options.mimicmodel);
+    if (obj->options.adminauthuser)
+        CONFIG_ARG_FREE(obj->options.adminauthuser);
+    if (obj->options.hostname)
+        CONFIG_ARG_FREE(obj->options.hostname);
+    if (obj->options.k5keytab)
+        CONFIG_ARG_FREE(obj->options.k5keytab);
+    if (obj->options.Cnid_srv)
+        CONFIG_ARG_FREE(obj->options.Cnid_srv);
+    if (obj->options.Cnid_port)
+        CONFIG_ARG_FREE(obj->options.Cnid_port);
+    if (obj->options.fqdn)
+        CONFIG_ARG_FREE(obj->options.fqdn);
+    if (obj->options.ignored_attr)
+        CONFIG_ARG_FREE(obj->options.ignored_attr);
+    if (obj->options.slmod_path)
+        CONFIG_ARG_FREE(obj->options.slmod_path);
+
+    if (obj->options.unixcodepage)
+        CONFIG_ARG_FREE(obj->options.unixcodepage);
+    if (obj->options.maccodepage)
+        CONFIG_ARG_FREE(obj->options.maccodepage);
+    if (obj->options.volcodepage)
+        CONFIG_ARG_FREE(obj->options.volcodepage);
+
+    obj->options.flags = 0;
+    obj->options.passwdbits = 0;
+
+    /* Free everything called from afp_config_parse() */
+    free_extmap();
+    atalk_iniparser_freedict(obj->iniconfig);
+    free_charset_names();
+}
index ada3e61052353b972776eae3fa85046dac7057f1..da78c6a064bd637418b40f5495653b2f77f5ebdf 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com
  * All rights reserved. See COPYRIGHT.
  *
- *
  * handle inserting, removing, and freeing of children.
  * this does it via a hash table. it incurs some overhead over
  * a linear append/remove in total removal and kills, but it makes
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/time.h>
+#include <pthread.h>
 
 #include <atalk/logger.h>
 #include <atalk/errchk.h>
 #endif
 
 /* hash/child functions: hash OR's pid */
-#define CHILD_HASHSIZE 32
 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
 
-typedef struct server_child_fork {
-    struct server_child_data *table[CHILD_HASHSIZE];
-    void (*cleanup)(const pid_t);
-} server_child_fork;
-
-static inline void hash_child(struct server_child_data **htable,
-                              struct server_child_data *child)
+static inline void hash_child(afp_child_t **htable, afp_child_t *child)
 {
-    struct server_child_data **table;
+    afp_child_t **table;
 
-    table = &htable[HASH(child->pid)];
-    if ((child->next = *table) != NULL)
-        (*table)->prevp = &child->next;
+    table = &htable[HASH(child->afpch_pid)];
+    if ((child->afpch_next = *table) != NULL)
+        (*table)->afpch_prevp = &child->afpch_next;
     *table = child;
-    child->prevp = table;
+    child->afpch_prevp = table;
 }
 
-static inline void unhash_child(struct server_child_data *child)
+static inline void unhash_child(afp_child_t *child)
 {
-    if (child->prevp) {
-        if (child->next)
-            child->next->prevp = child->prevp;
-        *(child->prevp) = child->next;
+    if (child->afpch_prevp) {
+        if (child->afpch_next)
+            child->afpch_next->afpch_prevp = child->afpch_prevp;
+        *(child->afpch_prevp) = child->afpch_next;
     }
 }
 
-static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
+afp_child_t *server_child_resolve(server_child_t *childs, id_t pid)
 {
-    struct server_child_data *child;
+    afp_child_t *child;
 
-    for (child = table[HASH(pid)]; child; child = child->next) {
-        if (child->pid == pid)
+    for (child = childs->servch_table[HASH(pid)]; child; child = child->afpch_next) {
+        if (child->afpch_pid == pid)
             break;
     }
 
@@ -89,23 +83,15 @@ static struct server_child_data *resolve_child(struct server_child_data **table,
 }
 
 /* initialize server_child structure */
-server_child *server_child_alloc(const int connections, const int nforks)
+server_child_t *server_child_alloc(int connections)
 {
-    server_child *children;
-
-    children = (server_child *) calloc(1, sizeof(server_child));
-    if (!children)
-        return NULL;
-
-    children->nsessions = connections;
-    children->nforks = nforks;
-    children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+    server_child_t *children;
 
-    if (!children->fork) {
-        free(children);
+    if (!(children = (server_child_t *)calloc(1, sizeof(server_child_t))))
         return NULL;
-    }
 
+    children->servch_nsessions = connections;
+    pthread_mutex_init(&children->servch_lock, NULL);
     return children;
 }
 
@@ -113,17 +99,11 @@ server_child *server_child_alloc(const int connections, const int nforks)
  * add a child
  * @return pointer to struct server_child_data on success, NULL on error
  */
-afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, int ipc_fd)
+afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
 {
-    server_child_fork *fork;
     afp_child_t *child = NULL;
-    sigset_t sig, oldsig;
 
-    /* we need to prevent deletions from occuring before we get a
-     * chance to add the child in. */
-    sigemptyset(&sig);
-    sigaddset(&sig, SIGCHLD);
-    pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
+    pthread_mutex_lock(&children->servch_lock);
 
     /* it's possible that the child could have already died before the
      * pthread_sigmask. we need to check for this. */
@@ -132,118 +112,106 @@ afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, int
         goto exit;
     }
 
-    fork = (server_child_fork *) children->fork + forkid;
-
     /* if we already have an entry. just return. */
-    if (child = resolve_child(fork->table, pid))
+    if ((child = server_child_resolve(children, pid)))
         goto exit;
 
     if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
         goto exit;
 
-    child->pid = pid;
-    child->valid = 0;
-    child->killed = 0;
-    child->ipc_fd = ipc_fd;
+    child->afpch_pid = pid;
+    child->afpch_ipc_fd = ipc_fd;
+    child->afpch_logintime = time(NULL);
 
-    hash_child(fork->table, child);
-    children->count++;
+    hash_child(children->servch_table, child);
+    children->servch_count++;
 
 exit:
-    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    pthread_mutex_unlock(&children->servch_lock);
     return child;
 }
 
 /* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid, pid_t pid)
+int server_child_remove(server_child_t *children, pid_t pid)
 {
     int fd;
-    server_child_fork *fork;
-    struct server_child_data *child;
+    afp_child_t *child;
 
-    fork = (server_child_fork *) children->fork + forkid;
-    if (!(child = resolve_child(fork->table, pid)))
+    if (!(child = server_child_resolve(children, pid)))
         return -1;
 
+    pthread_mutex_lock(&children->servch_lock);
+
     unhash_child(child);
-    if (child->clientid) {
-        free(child->clientid);
-        child->clientid = NULL;
+    if (child->afpch_clientid) {
+        free(child->afpch_clientid);
+        child->afpch_clientid = NULL;
     }
 
     /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
-    fd = child->ipc_fd;
+    fd = child->afpch_ipc_fd;
     if (fd != -1)
         close(fd);
 
     free(child);
-    children->count--;
+    children->servch_count--;
 
-    if (fork->cleanup)
-        fork->cleanup(pid);
+    pthread_mutex_unlock(&children->servch_lock);
 
     return fd;
 }
 
 /* free everything: by using a hash table, this increases the cost of
  * this part over a linked list by the size of the hash table */
-void server_child_free(server_child *children)
+void server_child_free(server_child_t *children)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
-    int i, j;
-
-    for (i = 0; i < children->nforks; i++) {
-        fork = (server_child_fork *) children->fork + i;
-        for (j = 0; j < CHILD_HASHSIZE; j++) {
-            child = fork->table[j]; /* start at the beginning */
-            while (child) {
-                tmp = child->next;
-                close(child->ipc_fd);
-                if (child->clientid) {
-                    free(child->clientid);
-                }
-                free(child);
-                child = tmp;
-            }
+    afp_child_t *child, *tmp;
+    int j;
+
+    for (j = 0; j < CHILD_HASHSIZE; j++) {
+        child = children->servch_table[j]; /* start at the beginning */
+        while (child) {
+            tmp = child->afpch_next;
+            close(child->afpch_ipc_fd);
+            if (child->afpch_clientid)
+                free(child->afpch_clientid);
+            if (child->afpch_volumes)
+                free(child->afpch_volumes);
+            free(child);
+            child = tmp;
         }
     }
-    free(children->fork);
+
     free(children);
 }
 
 /* send signal to all child processes */
-void server_child_kill(server_child *children, int forkid, int sig)
+void server_child_kill(server_child_t *children, int sig)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
+    afp_child_t *child, *tmp;
     int i;
 
-    fork = (server_child_fork *) children->fork + forkid;
     for (i = 0; i < CHILD_HASHSIZE; i++) {
-        child = fork->table[i];
+        child = children->servch_table[i];
         while (child) {
-            tmp = child->next;
-            kill(child->pid, sig);
+            tmp = child->afpch_next;
+            kill(child->afpch_pid, sig);
             child = tmp;
         }
     }
 }
 
-/* send kill to a child processes.
- * a plain-old linked list
- * FIXME use resolve_child ?
- */
-static int kill_child(struct server_child_data *child)
+/* send kill to a child processes */
+static int kill_child(afp_child_t *child)
 {
-    if (!child->killed) {
-        kill(child->pid, SIGTERM);
+    if (!child->afpch_killed) {
+        kill(child->afpch_pid, SIGTERM);
         /* we don't wait because there's no guarantee that we can really kill it */
-        child->killed = 1;
+        child->afpch_killed = 1;
         return 1;
     } else {
-        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
-        kill(child->pid, SIGKILL);
+        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->afpch_pid);
+        kill(child->afpch_pid, SIGKILL);
     }
     return 1;
 }
@@ -252,20 +220,16 @@ static int kill_child(struct server_child_data *child)
  * Try to find an old session and pass socket
  * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
  */
-int server_child_transfer_session(server_child *children,
-                                  int forkid,
+int server_child_transfer_session(server_child_t *children,
                                   pid_t pid,
                                   uid_t uid,
                                   int afp_socket,
                                   uint16_t DSI_requestID)
 {
     EC_INIT;
-    server_child_fork *fork;
-    struct server_child_data *child;
-    int i;
+    afp_child_t *child;
 
-    fork = (server_child_fork *) children->fork + forkid;
-    if ((child = resolve_child(fork->table, pid)) == NULL) {
+    if ((child = server_child_resolve(children, pid)) == NULL) {
         LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
         if (kill(pid, 0) == 0) {
             LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
@@ -280,23 +244,23 @@ int server_child_transfer_session(server_child *children,
         return 0;
     }
 
-    if (!child->valid) {
+    if (!child->afpch_valid) {
         /* hmm, client 'guess' the pid, rogue? */
         LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
         return 0;
-    } else if (child->uid != uid) {
+    } else if (child->afpch_uid != uid) {
         LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
         return 0;
     }
 
     LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
     
-    if (writet(child->ipc_fd, &DSI_requestID, 2, 0, 2) != 2) {
+    if (writet(child->afpch_ipc_fd, &DSI_requestID, 2, 0, 2) != 2) {
         LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
         EC_STATUS(-1);
         goto EC_CLEANUP;
     }
-    EC_ZERO_LOG(send_fd(child->ipc_fd, afp_socket));
+    EC_ZERO_LOG(send_fd(child->afpch_ipc_fd, afp_socket));
     EC_ZERO_LOG(kill(pid, SIGURG));
 
     EC_STATUS(1);
@@ -308,65 +272,55 @@ EC_CLEANUP:
 
 /* see if there is a process for the same mac     */
 /* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
                                  uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
+    afp_child_t *child, *tmp;
     int i;
 
-    fork = (server_child_fork *)children->fork + forkid;
-
+    pthread_mutex_lock(&children->servch_lock);
+    
     for (i = 0; i < CHILD_HASHSIZE; i++) {
-        child = fork->table[i];
+        child = children->servch_table[i];
         while (child) {
-            tmp = child->next;
-            if ( child->pid != pid) {
-                if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
-                    if ( child->time != boottime ) {
+            tmp = child->afpch_next;
+            if (child->afpch_pid != pid) {
+                if (child->afpch_idlen == idlen && memcmp(child->afpch_clientid, id, idlen) == 0) {
+                    if ( child->afpch_boottime != boottime ) {
                         /* Client rebooted */
-                        if (uid == child->uid) {
+                        if (uid == child->afpch_uid) {
                             kill_child(child);
                             LOG(log_warning, logtype_default,
                                 "Terminated disconnected child[%u], client rebooted.",
-                                child->pid);
+                                child->afpch_pid);
                         } else {
                             LOG(log_warning, logtype_default,
-                                "Session with different pid[%u]", child->pid);
+                                "Session with different pid[%u]", child->afpch_pid);
                         }
                     } else {
                         /* One client with multiple sessions */
                         LOG(log_debug, logtype_default,
-                            "Found another session[%u] for client[%u]", child->pid, pid);
+                            "Found another session[%u] for client[%u]", child->afpch_pid, pid);
                     }
                 }
             } else {
                 /* update childs own slot */
-                child->time = boottime;
-                if (child->clientid)
-                    free(child->clientid);
-                LOG(log_debug, logtype_default, "Setting client ID for %u", child->pid);
-                child->uid = uid;
-                child->valid = 1;
-                child->idlen = idlen;
-                child->clientid = id;
+                child->afpch_boottime = boottime;
+                if (child->afpch_clientid)
+                    free(child->afpch_clientid);
+                LOG(log_debug, logtype_default, "Setting client ID for %u", child->afpch_pid);
+                child->afpch_uid = uid;
+                child->afpch_valid = 1;
+                child->afpch_idlen = idlen;
+                child->afpch_clientid = id;
             }
             child = tmp;
         }
     }
-}
 
-/* for extra cleanup if necessary */
-void server_child_setup(server_child *children, const int forkid,
-                        void (*fcn)(const pid_t))
-{
-    server_child_fork *fork;
-
-    fork = (server_child_fork *) children->fork + forkid;
-    fork->cleanup = fcn;
+    pthread_mutex_unlock(&children->servch_lock);
 }
 
-
 /* ---------------------------
  * reset children signals
  */
index d40e3a49ea4cb07bf1b7984fc418cb5ec11521ee..e49d165d462133ba5dc3c1b222ff5d4d99621116 100644 (file)
@@ -18,6 +18,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <time.h>
+#include <pthread.h>
 
 #include <atalk/server_child.h>
 #include <atalk/server_ipc.h>
@@ -42,13 +43,15 @@ typedef struct ipc_header {
 } ipc_header_t;
 
 static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
-                               "IPC_GETSESSION"};
+                               "IPC_GETSESSION",
+                               "IPC_STATE",
+                               "IPC_VOLUMES"};
 
 /*
  * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
  * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
  */
-static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
+static int ipc_kill_token(struct ipc_header *ipc, server_child_t *children)
 {
     pid_t pid;
 
@@ -59,7 +62,6 @@ static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
     memcpy (&pid, ipc->msg, sizeof(pid_t));
 
     return server_child_transfer_session(children,
-                                         CHILD_DSIFORK,
                                          pid,
                                          ipc->uid,
                                          ipc->afp_socket,
@@ -67,7 +69,7 @@ static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
 }
 
 /* ----------------- */
-static int ipc_get_session(struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child_t *children)
 {
     uint32_t boottime;
     uint32_t idlen;
@@ -96,7 +98,6 @@ static int ipc_get_session(struct ipc_header *ipc, server_child *children)
         ipc->child_pid, ipc->uid, boottime); 
 
     server_child_kill_one_by_id(children,
-                                CHILD_DSIFORK,
                                 ipc->child_pid,
                                 ipc->uid,
                                 idlen,
@@ -106,100 +107,48 @@ static int ipc_get_session(struct ipc_header *ipc, server_child *children)
     return 0;
 }
 
-/***********************************************************************************
- * Public functions
- ***********************************************************************************/
-
-/*!
- * Listen on UNIX domain socket "name" for IPC from old sesssion
- *
- * @args name    (r) file name to use for UNIX domain socket
- * @returns      socket fd, -1 on error
- */
-int ipc_server_uds(const char *name)
+static int ipc_set_state(struct ipc_header *ipc, server_child_t *children)
 {
     EC_INIT;
-    struct sockaddr_un address;
-    socklen_t address_length;
-    int fd = -1;
-
-    EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
-    EC_ZERO_LOG( setnonblock(fd, 1) );
-    unlink(name);
-    address.sun_family = AF_UNIX;
-    address_length = sizeof(address.sun_family) + sprintf(address.sun_path, "%s", name);
-    EC_ZERO_LOG( bind(fd, (struct sockaddr *)&address, address_length) );
-    EC_ZERO_LOG( listen(fd, 1024) );
+    afp_child_t *child;
 
-EC_CLEANUP:
-    if (ret != 0) {
-        return -1;
-    }
+    pthread_mutex_lock(&children->servch_lock);
+
+    if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
+        EC_FAIL;
+
+    memcpy(&child->afpch_state, ipc->msg, sizeof(uint16_t));
 
-    return fd;
+EC_CLEANUP:
+    pthread_mutex_unlock(&children->servch_lock);
+    EC_EXIT;
 }
 
-/*!
- * Connect to UNIX domain socket "name" for IPC with new afpd master
- *
- * 1. Connect
- * 2. send pid, which establishes a child structure for us in the master
- *
- * @args name    (r) file name to use for UNIX domain socket
- * @returns      socket fd, -1 on error
- */
-int ipc_client_uds(const char *name)
+static int ipc_set_volumes(struct ipc_header *ipc, server_child_t *children)
 {
     EC_INIT;
-    struct sockaddr_un address;
-    socklen_t address_length;
-    int fd = -1;
-    pid_t pid = getpid();
-
-    EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
-    address.sun_family = AF_UNIX;
-    address_length = sizeof(address.sun_family) + sprintf(address.sun_path, "%s", name);
-
-    EC_ZERO_LOG( connect(fd, (struct sockaddr *)&address, address_length) ); /* 1 */
-    LOG(log_debug, logtype_afpd, "ipc_client_uds: connected to master");
+    afp_child_t *child;
 
-    EC_ZERO_LOG( setnonblock(fd, 1) );
+    pthread_mutex_lock(&children->servch_lock);
 
-    if (writet(fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
-        LOG(log_error, logtype_afpd, "ipc_client_uds: writet: %s", strerror(errno));
+    if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
         EC_FAIL;
+
+    if (child->afpch_volumes) {
+        free(child->afpch_volumes);
+        child->afpch_volumes = NULL;
     }
+    if (ipc->len)
+        child->afpch_volumes = strdup(ipc->msg);
 
 EC_CLEANUP:
-    if (ret != 0) {
-        return -1;
-    }
-    LOG(log_debug, logtype_afpd, "ipc_client_uds: fd: %d", fd);
-    return fd;
+    pthread_mutex_unlock(&children->servch_lock);
+    EC_EXIT;
 }
 
-int reconnect_ipc(AFPObj *obj)
-{
-    int retrycount = 0;
-
-    LOG(log_debug, logtype_afpd, "reconnect_ipc: start");
-
-    close(obj->ipc_fd);
-    obj->ipc_fd = -1;
-
-    sleep((getpid() % 5) + 15);  /* give it enough time to start */
-
-    while (retrycount++ < 10) {
-        if ((obj->ipc_fd = ipc_client_uds(_PATH_AFP_IPC)) == -1) {
-            LOG(log_error, logtype_afpd, "reconnect_ipc: cant reconnect to master");
-            sleep(1);
-            continue;
-        }
-        LOG(log_debug, logtype_afpd, "reconnect_ipc: succesfull IPC reconnect");
-        return 0;
-    }
-    return -1;
-}
+/***********************************************************************************
+ * Public functions
+ ***********************************************************************************/
 
 /* ----------------- 
  * Ipc format
@@ -219,7 +168,7 @@ int reconnect_ipc(AFPObj *obj)
  *
  * @returns -1 on error, 0 on success
  */
-int ipc_server_read(server_child *children, int fd)
+int ipc_server_read(server_child_t *children, int fd)
 {
     int       ret;
     struct ipc_header ipc;
@@ -267,8 +216,6 @@ int ipc_server_read(server_child *children, int fd)
     LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
         ipc_cmd_str[ipc.command], ipc.child_pid); 
 
-    int afp_socket;
-
     switch (ipc.command) {
 
        case IPC_DISCOLDSESSION:
@@ -296,6 +243,16 @@ int ipc_server_read(server_child *children, int fd)
             return -1;
         break;
 
+    case IPC_STATE:
+        if (ipc_set_state(&ipc, children) != 0)
+            return -1;
+        break;
+
+    case IPC_VOLUMES:
+        if (ipc_set_volumes(&ipc, children) != 0)
+            return -1;
+        break;
+
        default:
                LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
                return -1;
@@ -347,3 +304,8 @@ int ipc_child_write(int fd, uint16_t command, int len, void *msg)
 
    return 0;
 }
+
+int ipc_child_state(AFPObj *obj, uint16_t state)
+{
+    return ipc_child_write(obj->ipc_fd, IPC_STATE, sizeof(uint16_t), &state);
+}
index 0251eadaeba94652c2b45474881f648ea1c5dde2..6ae817e5547c701891ee7e2d6dcfd554283b9870 100644 (file)
@@ -126,9 +126,7 @@ int check_lockfile(const char *program, const char *pidfile)
  */
 int create_lockfile(const char *program, const char *pidfile)
 {
-    char buf[10];
     FILE *pf;
-    pid_t pid;
     int mask;
   
     if (check_lockfile(program, pidfile) != 0)
index eda69c6c7e078c583e031add2aad2bd89d95d020..c7ef534a7e556567bda75e22eb3048a4616816ae 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <atalk/logger.h>
 #include <atalk/util.h>
+#include <atalk/errchk.h>
 
 static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
 
@@ -79,7 +80,7 @@ int setnonblock(int fd, int cmd)
  * @param lenght          (r)  how many bytes to read
  * @param setnonblocking  (r)  when non-zero this func will enable and disable non blocking
  *                             io mode for the socket
- * @param timeout         (r)  number of seconds to try reading
+ * @param timeout         (r)  number of seconds to try reading, 0 means no timeout
  *
  * @returns number of bytes actually read or -1 on timeout or error
  */
@@ -99,44 +100,50 @@ ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, i
     }
 
     /* Calculate end time */
-    (void)gettimeofday(&now, NULL);
-    end = now;
-    end.tv_sec += timeout;
+    if (timeout) {
+        (void)gettimeofday(&now, NULL);
+        end = now;
+        end.tv_sec += timeout;
+    }
 
     while (stored < length) {
-        len = read(socket, (char *) data + stored, length - stored);
+        len = recv(socket, (char *) data + stored, length - stored, 0);
         if (len == -1) {
             switch (errno) {
             case EINTR:
                 continue;
             case EAGAIN:
                 FD_SET(socket, &rfds);
-                tv.tv_usec = 0;
-                tv.tv_sec  = timeout;
-                        
-                while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+                if (timeout) {
+                    tv.tv_usec = 0;
+                    tv.tv_sec  = timeout;
+                }
+
+                while ((ret = select(socket + 1, &rfds, NULL, NULL, timeout ? &tv : NULL)) < 1) {
                     switch (ret) {
                     case 0:
-                        LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+                        LOG(log_debug, logtype_dsi, "select timeout %d s", timeout);
                         errno = EAGAIN;
                         goto exit;
 
                     default: /* -1 */
                         switch (errno) {
                         case EINTR:
-                            (void)gettimeofday(&now, NULL);
-                            if (now.tv_sec > end.tv_sec
-                                ||
-                                (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
-                                LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
-                                goto exit;
-                            }
-                            if (now.tv_usec > end.tv_usec) {
-                                tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
-                                tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
-                            } else {
-                                tv.tv_usec = end.tv_usec - now.tv_usec;
-                                tv.tv_sec  = end.tv_sec - now.tv_sec;
+                            if (timeout) {
+                                (void)gettimeofday(&now, NULL);
+                                if (now.tv_sec > end.tv_sec
+                                    ||
+                                    (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
+                                    LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+                                    goto exit;
+                                }
+                                if (now.tv_usec > end.tv_usec) {
+                                    tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+                                    tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
+                                } else {
+                                    tv.tv_usec = end.tv_usec - now.tv_usec;
+                                    tv.tv_sec  = end.tv_sec - now.tv_sec;
+                                }
                             }
                             FD_SET(socket, &rfds);
                             continue;
@@ -411,6 +418,91 @@ int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
     return ret;
 }
 
+/*!
+ * Tokenize IP(4/6) addresses with an optional port into address and port
+ *
+ * @param ipurl    (r) IP URL string
+ * @param address  (w) IP address
+ * @param port     (w) IP port
+ *
+ * @returns 0 on success, -1 on failure
+ *
+ * Tokenize IPv4, IPv4:port, IPv6, [IPv6] or [IPv6:port] URL into address and
+ * port and return two allocated strings with the address and the port.
+ *
+ * If the function returns 0, then address point to a newly allocated
+ * valid address string, port may either be NULL or point to a newly
+ * allocated port number.
+ *
+ * If the function returns -1, then the contents of address and port are
+ * undefined.
+ */
+int tokenize_ip_port(const char *ipurl, char **address, char **port)
+{
+    EC_INIT;
+    char *p = NULL;
+    char *s;
+
+    AFP_ASSERT(ipurl && address && port);
+    EC_NULL( p = strdup(ipurl));
+
+    /* Either ipv4, ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+    if (!strchr(p, ':')) {
+        /* IPv4 address without port */
+        *address = p;
+        p = NULL;  /* prevent free() */
+        *port = NULL;
+        EC_EXIT_STATUS(0);
+    }
+
+    /* Either ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+    if (strchr(p, '.')) {
+        /* ipv4:port */
+        *address = p;
+        p = strchr(p, ':');
+        *p = '\0';
+        EC_NULL( *port = strdup(p + 1));
+        p = NULL; /* prevent free() */
+        EC_EXIT_STATUS(0);
+    }
+
+    /* Either ipv6, [ipv6] or [ipv6]:port */
+
+    if (p[0] != '[') {
+        /* ipv6 */
+        *address = p;
+        p = NULL;  /* prevent free() */
+        *port = NULL;
+        EC_EXIT_STATUS(0);
+    }
+
+    /* [ipv6] or [ipv6]:port */
+
+    EC_NULL( *address = strdup(p + 1) );
+
+    if ((s = strchr(*address, ']')) == NULL) {
+        LOG(log_error, logtype_dsi, "tokenize_ip_port: malformed ipv6 address %s\n", ipurl);
+        EC_FAIL;
+    }
+    *s = '\0';
+    /* address now points to the ipv6 address without [] */
+
+    if (s[1] == ':') {
+        /* [ipv6]:port */
+        EC_NULL( *port = strdup(s + 2) );
+    } else {
+        /* [ipv6] */
+        *port = NULL;
+    }
+
+EC_CLEANUP:
+    if (p)
+        free(p);
+    EC_EXIT;
+}
+
 /*!
  * Add a fd to a dynamic pollfd array that is allocated and grown as needed
  *
index 56c3e168eaea05c05705464a0f21830aedf60c03..4572caddd454d36c1b0cd3e45b6b258b7aedadf3 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/time.h>
 #include <time.h>
 #include <sys/wait.h>
+#include <libgen.h>
 
 #include <atalk/adouble.h>
 #include <atalk/ea.h>
@@ -43,6 +44,7 @@
 #include <atalk/unix.h>
 #include <atalk/compat.h>
 #include <atalk/errchk.h>
+#include <atalk/acl.h>
 
 /* close all FDs >= a specified value */
 static void closeall(int fd)
@@ -227,20 +229,107 @@ char *stripped_slashes_basename(char *p)
     return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
 }
 
+/*********************************************************************************
+ * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
+ * Currently the only used options are O_NOFOLLOW, used to switch between symlink
+ * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
+ * called which does special ACL handling depending on the filesytem
+ *********************************************************************************/
+
+int ostat(const char *path, struct stat *buf, int options)
+{
+    if (options & O_NOFOLLOW)
+        return lstat(path, buf);
+    else
+        return stat(path, buf);
+}
+
+int ochown(const char *path, uid_t owner, gid_t group, int options)
+{
+    if (options & O_NOFOLLOW)
+        return lchown(path, owner, group);
+    else
+        return chown(path, owner, group);
+}
+
+/*!
+ * chmod() wrapper for symlink and ACL handling
+ *
+ * @param path       (r) path
+ * @param mode       (r) requested mode
+ * @param sb         (r) stat() of path or NULL
+ * @param option     (r) O_NOFOLLOW | O_NETATALK_ACL
+ *
+ * Options description:
+ * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
+ * O_NETATALK_ACL: call chmod_acl() instead of chmod()
+ * O_IGNORE: ignore chmod() request, directly return 0
+ */
+int ochmod(char *path, mode_t mode, const struct stat *st, int options)
+{
+    struct stat sb;
+
+    if (options & O_IGNORE)
+        return 0;
+
+    if (!st) {
+        if (lstat(path, &sb) != 0)
+            return -1;
+        st = &sb;
+    }
+
+    if (options & O_NOFOLLOW)
+        if (S_ISLNK(st->st_mode))
+            return 0;
+
+    if (options & O_NETATALK_ACL) {
+        return chmod_acl(path, mode);
+    } else {
+        return chmod(path, mode);
+    }
+}
+
+/* 
+ * @brief ostat/fsstatat multiplexer
+ *
+ * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
+ *
+ * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
+ * @param path    (r) pathname
+ * @param st      (rw) pointer to struct stat
+ */
+int ostatat(int dirfd, const char *path, struct stat *st, int options)
+{
+#ifdef HAVE_ATFUNCS
+    if (dirfd == -1)
+        dirfd = AT_FDCWD;
+    return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#else
+    return ostat(path, st, options);
+#endif            
+
+    /* DEADC0DE */
+    return -1;
+}
+
 /*!
  * @brief symlink safe chdir replacement
  *
- * Only chdirs to dir if it doesn't contain symlinks.
+ * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
+ * is disabled
  *
  * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
  */
-int lchdir(const char *dir)
+int ochdir(const char *dir, int options)
 {
     char buf[MAXPATHLEN+1];
     char cwd[MAXPATHLEN+1];
     char *test;
     int  i;
 
+    if (!(options & O_NOFOLLOW))
+        return chdir(dir);
+
     /*
      dir is a canonical path (without "../" "./" "//" )
      but may end with a / 
@@ -342,3 +431,126 @@ int gmem(gid_t gid, int ngroups, gid_t *groups)
     }
     return( 0 );
 }
+
+/*
+ * realpath() replacement that always allocates storage for returned path
+ */
+char *realpath_safe(const char *path)
+{
+    char *resolved_path;
+
+#ifdef REALPATH_TAKES_NULL
+    if ((resolved_path = realpath(path, NULL)) == NULL) {
+        LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+        return NULL;
+    }
+    return resolved_path;
+#else
+    if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
+        return NULL;
+    if (realpath(path, resolved_path) == NULL) {
+        free(resolved_path);
+        LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+        return NULL;
+    }
+    /* Safe some memory */
+    char *tmp;
+    if ((tmp = strdup(resolved_path)) == NULL) {
+        free(resolved_path);
+        return NULL;
+    }
+    free(resolved_path);
+    resolved_path = tmp;
+    return resolved_path;
+#endif
+}
+
+/**
+ * Returns pointer to static buffer with basename of path
+ **/
+const char *basename_safe(const char *path)
+{
+    static char buf[MAXPATHLEN+1];
+    strlcpy(buf, path, MAXPATHLEN);
+    return basename(buf);
+}
+
+/**
+ * extended strtok allows the quoted strings
+ * modified strtok.c in glibc 2.0.6
+ **/
+char *strtok_quote(char *s, const char *delim)
+{
+    static char *olds = NULL;
+    char *token;
+
+    if (s == NULL)
+        s = olds;
+
+    /* Scan leading delimiters.  */
+    s += strspn (s, delim);
+    if (*s == '\0')
+        return NULL;
+
+    /* Find the end of the token.  */
+    token = s;
+
+    if (token[0] == '\"') {
+        token++;
+        s = strpbrk (token, "\"");
+    } else {
+        s = strpbrk (token, delim);
+    }
+
+    if (s == NULL) {
+        /* This token finishes the string.  */
+        olds = strchr (token, '\0');
+    } else {
+        /* Terminate the token and make OLDS point past it.  */
+        *s = '\0';
+        olds = s + 1;
+    }
+    return token;
+}
+
+int set_groups(AFPObj *obj, struct passwd *pwd)
+{
+    if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
+        LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
+
+    if ((obj->ngroups = getgroups(0, NULL)) < 0) {
+        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
+        return -1;
+    }
+
+    if (obj->groups)
+        free(obj->groups);
+    if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
+        LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
+        return -1;
+    }
+
+    if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) {
+        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+#define GROUPSTR_BUFSIZE 1024
+const char *print_groups(int ngroups, gid_t *groups)
+{
+    static char groupsstr[GROUPSTR_BUFSIZE];
+    int i;
+    char *s = groupsstr;
+
+    if (ngroups == 0)
+        return "-";
+
+    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
+        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
+    }
+
+    return groupsstr;
+}
index c936f178d79bbd8bbbafc8c0269a07275ac550a7..027aba1b27a1112abb781138a7cf47189b02a395 100644 (file)
@@ -30,7 +30,7 @@
 #include <atalk/errchk.h>
 #include <atalk/acl.h>
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
 int remove_acl_vfs(const char *name)
@@ -86,7 +86,7 @@ exit:
     return ret;
 }
 
-#endif  /* HAVE_SOLARIS_ACLS */
+#endif  /* HAVE_NFSV4_ACLS */
 
 #ifdef HAVE_POSIX_ACLS
 /*!
@@ -108,7 +108,7 @@ int remove_acl_vfs(const char *name)
 
 
     /* Remove default ACL if it's a dir */
-    EC_ZERO_LOG_ERR(stat(name, &st), AFPERR_MISC);
+    EC_ZERO_ERR(stat(name, &st), AFPERR_MISC);
     if (S_ISDIR(st.st_mode)) {
         EC_NULL_LOG_ERR(acl = acl_init(0), AFPERR_MISC);
         EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, acl), AFPERR_MISC);
@@ -129,6 +129,7 @@ int remove_acl_vfs(const char *name)
     EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
 
 EC_CLEANUP:
+    if (errno == ENOENT) EC_STATUS(0);
     if (acl) acl_free(acl);
 
     EC_EXIT;
index cdc7c82820b32837e1832be46054491a5a88898c..19c23e0672c8ac78ba2287b448f19bffe0b6baf9 100644 (file)
@@ -79,7 +79,7 @@ static char *mtoupath(const struct vol *vol, const char *mpath)
     char         *u;
     size_t       inplen;
     size_t       outlen;
-    uint16_t     flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
+    uint16_t     flags = CONV_ESCAPEHEX;
 
     if (!mpath)
         return NULL;
@@ -906,7 +906,7 @@ int ea_close(struct ea * restrict ea)
             if (ea->ea_count == 0) {
                 /* Check if EA header exists and remove it */
                 eaname = ea_path(ea, NULL, 0);
-                if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
+                if ((statat(ea->dirfd, eaname, &st)) == 0) {
                     if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
                             eaname, strerror(errno));
@@ -1583,7 +1583,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
         }
     }
 
-    if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
+    if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) {
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1600,7 +1600,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((lchown(eaname, uid, gid)) != 0) {
+        if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) {
             switch (errno) {
             case EPERM:
             case EACCES:
@@ -1644,7 +1644,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
     }
 
     /* Set mode on EA header file */
-    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+    if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
@@ -1663,7 +1663,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+        if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
             switch (errno) {
             case EPERM:
@@ -1712,7 +1712,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
     }
 
     /* Set mode on EA header */
-    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+    if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
@@ -1742,7 +1742,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+        if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
             switch (errno) {
             case EPERM:
index a30e6d96b5b40121d3d8cebf434cb53fc8942a47..0b899903ee433a0a70f50a9ffb0cab0cd96df406 100644 (file)
@@ -86,6 +86,8 @@ int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
 
         case ENOATTR:
         case ENOENT:
+            if (vol->v_obj->afp_version >= 34)
+                return AFPERR_NOITEM;
             return AFPERR_MISC;
 
         default:
@@ -161,6 +163,8 @@ int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
             return AFPERR_MISC;
 
         case ENOATTR:
+            if (vol->v_obj->afp_version >= 34)
+                return AFPERR_NOITEM;
             return AFPERR_MISC;
 
         default:
@@ -313,8 +317,13 @@ int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
                 getcwdpath(), uname, attruname);
             return AFPERR_EXIST;
+        case ENOATTR:
+        case ENOENT:
+            if ((attr_flag & XATTR_REPLACE) && (vol->v_obj->afp_version >= 34))
+                return AFPERR_NOITEM;
+            return AFPERR_MISC;
         default:
-            LOG(log_error, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
+            LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
                 getcwdpath(), uname, attruname, attrsize, 
                 oflag & O_CREAT ? "XATTR_CREATE" : "-",
                 oflag & O_TRUNC ? "XATTR_REPLACE" : "-",
@@ -439,6 +448,9 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
                if (!*name)
                        continue;
 
+        if (STRCMP(name, ==, AD_EA_META))
+            continue;
+
         if (sfd != -1) {
             if (fchdir(sfd) == -1) {
                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
index 26da70636ff1a683c19d124a631105798f0d0541..4a338bae5a3869f2417bc2cdbe6f2f03290bf9bc 100644 (file)
@@ -240,17 +240,22 @@ ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t
        return lgetea(path, name, value, size);
 #elif defined(HAVE_EXTATTR_GET_LINK)
        ssize_t retval;
-       if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
-               if(retval > size) {
-                       errno = ERANGE;
-                       return -1;
-               }
-               if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
-                       return retval;
-       }
-       
-       LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
-       return -1;
+
+       retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
+    if (retval == -1) {
+        LOG(log_maxdebug, logtype_default, "extattr_get_link(): %s",
+            strerror(errno));
+        return -1;
+    }
+    if (size == 0)
+        /* Only interested in size of xattr */
+        return retval;
+    if (retval > size) {
+        errno = ERANGE;
+        return -1;
+    }
+    return extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
+
 #elif defined(HAVE_ATTR_GET)
        int retval, flags = ATTR_DONTFOLLOW;
        int valuelength = (int)size;
@@ -876,7 +881,6 @@ static int solaris_attropen(const char *path, const char *attrpath, int oflag, m
             EC_FAIL;
         default:
             LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
-            errno = ENOATTR;
             EC_FAIL;
         }
        }
@@ -888,7 +892,6 @@ static int solaris_attropen(const char *path, const char *attrpath, int oflag, m
             EC_FAIL;
         default:
             LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
-            errno = ENOATTR;
             EC_FAIL;
         }
        }
@@ -912,6 +915,7 @@ static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
         switch (errno) {
         case ENOENT:
         case EEXIST:
+        case EACCES:
             break;
         default:
             LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
index 1c0556d95e2eebc2e9baeccc34227a112ceeed8a..846aa7206933defac01a2efff09db8831bed77d1 100644 (file)
@@ -33,7 +33,7 @@ int dir_rx_set(mode_t mode)
 }
 
 /* --------------------- */
-int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
 {
     struct stat sb;
     mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
@@ -44,12 +44,13 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
         st = &sb;
     }
 
-    if (S_ISLNK(st->st_mode))
-        return 0; /* we don't want to change link permissions */
-    
     mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
 
-    if ( chmod_acl( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
+    if (ochmod((char *)name,
+               mode & ~vol->v_umask,
+               st,
+               vol_syml_opt(vol) | vol_chmod_opt(vol)
+            ) < 0 && errno != EPERM ) {
         return -1;
     }
     return 0;
@@ -77,6 +78,7 @@ int netatalk_rmdir_all_errors(int dirfd, const char *name)
         case ENOENT :
             return AFPERR_NOOBJ;
         case ENOTEMPTY :
+        case EEXIST:
             return AFPERR_DIRNEMPT;
         case EPERM:
         case EACCES :
@@ -169,9 +171,6 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
     int    ret = 0;
     int    sfd = -1;
     int    dfd = -1;
-    ssize_t cc;
-    size_t  buflen;
-    char   filebuf[NETATALK_DIOSZ_STACK];
 
 #ifdef HAVE_ATFUNCS
     if (dirfd == -1)
@@ -334,29 +333,6 @@ int statat(int dirfd, const char *path, struct stat *st)
     return -1;
 }
 
-/* 
- * @brief lstat/fsstatat multiplexer
- *
- * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
- *
- * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
- * @param path    (r) pathname
- * @param st      (rw) pointer to struct stat
- */
-int lstatat(int dirfd, const char *path, struct stat *st)
-{
-#ifdef HAVE_ATFUNCS
-    if (dirfd == -1)
-        dirfd = AT_FDCWD;
-    return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
-#else
-    return (lstat(path, st));
-#endif            
-
-    /* DEADC0DE */
-    return -1;
-}
-
 /* 
  * @brief opendir wrapper for *at semantics support
  *
index 8495d8c22fba19dd88789b82c6822bfb4a4fb3d6..0d1961cfa92c393065c8ab0128e72031fd57e0d0 100644 (file)
@@ -49,11 +49,11 @@ struct perm {
     gid_t gid;
 };
 
-typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t );
+typedef int (*rf_loop)(const struct vol *, struct dirent *, char *, void *, int);
 
 /* ----------------------------- */
 static int 
-for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask)
+for_each_adouble(const char *from, const char *name, rf_loop fn, const struct vol *vol, void *data, int flag)
 {
     char            buf[ MAXPATHLEN + 1];
     char            *m;
@@ -79,7 +79,7 @@ for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int
         }
         
         strlcat(buf, de->d_name, sizeof(buf));
-        if (fn && (ret = fn(de, buf, data, flag, v_umask))) {
+        if (fn && (ret = fn(vol, de, buf, data, flag))) {
            closedir(dp);
            return ret;
         }
@@ -127,7 +127,7 @@ static int RF_renamedir_adouble(VFS_FUNC_ARGS_RENAMEDIR)
 }
 
 /* ----------------- */
-static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask)
+static int deletecurdir_adouble_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     struct stat st;
     int         err;
@@ -150,56 +150,63 @@ static int RF_deletecurdir_adouble(VFS_FUNC_ARGS_DELETECURDIR)
 
     /* delete stray .AppleDouble files. this happens to get .Parent files
        as well. */
-    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask))) 
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, vol, NULL, 1))) 
         return err;
     return netatalk_rmdir(-1, ".AppleDouble" );
 }
 
 /* ----------------- */
-static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+static int adouble_setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
 {
-    return setfilmode(name, ad_hf_mode(mode), st, v_umask);
+    return setfilmode(vol, name, ad_hf_mode(mode), st);
 }
 
 static int RF_setfilmode_adouble(VFS_FUNC_ARGS_SETFILEMODE)
 {
-    return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask);
+    return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st);
 }
 
 /* ----------------- */
 static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE)
 {
     const char *adouble = vol->ad_path(name, ADFLAGS_DIR );
-    int  dropbox = vol->v_flags;
 
     if (dir_rx_set(mode)) {
-        if (chmod_acl(ad_dir(adouble), (DIRBITS | mode) & ~vol->v_umask) < 0 ) 
+        if (ochmod(ad_dir(adouble),
+                   (DIRBITS | mode) & ~vol->v_umask,
+                   st,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
             return -1;
     }
 
-    if (adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0) 
+    if (adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_DIR ), mode, st) < 0) 
         return -1;
 
     if (!dir_rx_set(mode)) {
-        if (chmod_acl(ad_dir(adouble), (DIRBITS | mode) & ~vol->v_umask) < 0 ) 
+        if (ochmod(ad_dir(adouble),
+                   (DIRBITS | mode) & ~vol->v_umask,
+                   st,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
             return  -1 ;
     }
     return 0;
 }
 
 /* ----------------- */
-static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+static int setdirmode_adouble_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag)
 {
     mode_t hf_mode = *(mode_t *)data;
     struct stat st;
 
-    if ( stat( name, &st ) < 0 ) {
+    if (ostat(name, &st, vol_syml_opt(vol)) < 0 ) {
         if (flag)
             return 0;
         LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
     }
     else if (!S_ISDIR(st.st_mode)) {
-        if (setfilmode(name, hf_mode , &st, v_umask) < 0) {
+        if (setfilmode(vol, name, hf_mode, &st) < 0) {
                /* FIXME what do we do then? */
         }
     }
@@ -208,21 +215,28 @@ static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data
 
 static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE)
 {
-    int   dropbox = vol->v_flags;
     mode_t hf_mode = ad_hf_mode(mode);
     const char  *adouble = vol->ad_path(name, ADFLAGS_DIR );
     const char  *adouble_p = ad_dir(adouble);
 
     if (dir_rx_set(mode)) {
-        if (chmod_acl(ad_dir(adouble), (DIRBITS | mode) & ~vol->v_umask) < 0) 
+        if (ochmod(ad_dir(adouble),
+                   (DIRBITS | mode) & ~vol->v_umask,
+                   st,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
             return -1;
     }
 
-    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, 0, vol->v_umask))
+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, vol, &hf_mode, 0))
         return -1;
 
     if (!dir_rx_set(mode)) {
-        if (chmod_acl(ad_dir(adouble), (DIRBITS | mode) & ~vol->v_umask) < 0) 
+        if (ochmod(ad_dir(adouble),
+                   (DIRBITS | mode) & ~vol->v_umask,
+                   st,
+                   vol_syml_opt(vol) | vol_chmod_opt(vol)
+                ) < 0)
             return  -1 ;
     }
     return 0;
@@ -257,7 +271,7 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE)
         if (errno == ENOENT) {
                struct adouble    ad;
 
-            if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+            if (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
                 return 0;
 
             /* We are here  because :
@@ -350,31 +364,24 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 static int RF_solaris_acl(VFS_FUNC_ARGS_ACL)
 {
     static char buf[ MAXPATHLEN + 1];
     struct stat st;
     int len;
 
-    if ((stat(path, &st)) != 0)
-       return -1;
-    if (S_ISDIR(st.st_mode)) {
-       len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-       if (len < 0 || len >=  MAXPATHLEN)
-           return -1;
-       /* set acl on .AppleDouble dir first */
-       if ((acl(buf, cmd, count, aces)) != 0)
-           return -1;
-       /* now set ACL on ressource fork */
-       if ((acl(vol->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0)
-           return -1;
-    } else
-       /* set ACL on ressource fork */
-       if ((acl(vol->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
-           return -1;
-
-    return 0;
+    if ((stat(path, &st)) != 0) {
+        if (errno == ENOENT)
+            return AFP_OK;
+        return AFPERR_MISC;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        /* set ACL on ressource fork */
+        if ((acl(vol->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
+            return AFPERR_MISC;
+    }
+    return AFP_OK;
 }
 
 static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
@@ -383,20 +390,15 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
     static char buf[ MAXPATHLEN + 1];
     int len;
 
-    if (dir) {
-       len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-       if (len < 0 || len >=  MAXPATHLEN)
-           return AFPERR_MISC;
-       /* remove ACL from .AppleDouble/.Parent first */
-       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
-           return ret;
-       /* now remove from .AppleDouble dir */
-       if ((ret = remove_acl_vfs(buf)) != AFP_OK)
-           return ret;
-    } else
-       /* remove ACL from ressource fork */
-       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
-           return ret;
+    if (dir)
+        return AFP_OK;
+
+    /* remove ACL from ressource fork */
+    if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK) {
+        if (errno == ENOENT)
+            return AFP_OK;
+        return ret;
+    }
 
     return AFP_OK;
 }
@@ -406,52 +408,35 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
 static int RF_posix_acl(VFS_FUNC_ARGS_ACL)
 {
     EC_INIT;
-    static char buf[ MAXPATHLEN + 1];
     struct stat st;
-    int len;
 
-    if (S_ISDIR(st.st_mode)) {
-        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-        if (len < 0 || len >=  MAXPATHLEN)
-            EC_FAIL;
-        /* set acl on .AppleDouble dir first */
-        EC_ZERO_LOG(acl_set_file(buf, type, acl));
+    if (stat(path, &st) == -1)
+        EC_FAIL;
 
-        if (type == ACL_TYPE_ACCESS)
-            /* set ACL on ressource fork (".Parent") too */
-            EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_DIR), type, acl));
-    } else {
+    if (!S_ISDIR(st.st_mode)) {
         /* set ACL on ressource fork */
-        EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl));
+        EC_ZERO_ERR( acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl), AFPERR_MISC );
     }
     
 EC_CLEANUP:
-    if (ret != 0)
-        return AFPERR_MISC;
-    return AFP_OK;
+    if (errno == ENOENT)
+        EC_STATUS(AFP_OK);
+    EC_EXIT;
 }
 
 static int RF_posix_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
 {
     EC_INIT;
-    static char buf[ MAXPATHLEN + 1];
-    int len;
 
-    if (dir) {
-        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-        if (len < 0 || len >=  MAXPATHLEN)
-            return AFPERR_MISC;
-        /* remove ACL from .AppleDouble/.Parent first */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR)), AFPERR_MISC);
+    if (dir)
+        EC_EXIT_STATUS(AFP_OK);
 
-        /* now remove from .AppleDouble dir */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(buf), AFPERR_MISC);
-    } else {
-        /* remove ACL from ressource fork */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC);
-    }
+    /* remove ACL from ressource fork */
+    EC_ZERO_ERR( remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC );
 
 EC_CLEANUP:
+    if (errno == ENOENT)
+        EC_STATUS(AFP_OK);
     EC_EXIT;
 }
 #endif
@@ -487,7 +472,7 @@ static int RF_renamedir_ea(VFS_FUNC_ARGS_RENAMEDIR)
 }
 
 /* Returns 1 if the entry is NOT an ._ file */
-static int deletecurdir_ea_osx_chkifempty_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ea_osx_chkifempty_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     if (de->d_name[0] != '.' || de->d_name[0] == '_')
         return 1;
@@ -495,12 +480,18 @@ static int deletecurdir_ea_osx_chkifempty_loop(struct dirent *de, char *name, vo
     return 0;
 }
 
-static int deletecurdir_ea_osx_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ea_osx_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     int ret;
-    
-    if ((ret = netatalk_unlink(name)) != 0)
-        return ret;
+    struct stat sb;
+
+    if (strncmp(name, "._", strlen("._")) == 0) {
+        if (lstat(name, &sb) != 0)
+            return -1;
+        if (S_ISREG(sb.st_mode))
+            if ((ret = netatalk_unlink(name)) != 0)
+                return ret;
+    }
 
     return 0;
 }
@@ -511,20 +502,9 @@ static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR)
 #ifndef HAVE_EAFD
     int err;
     /* delete stray ._AppleDouble files */
-
-    /* first check if there's really no other file besides files starting with ._ */
-    if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
-                                deletecurdir_ea_osx_chkifempty_loop,
-                                NULL, 0, 0)) != 0) {
-        if (err == 1)
-            return AFPERR_DIRNEMPT;
-        return AFPERR_MISC;
-    }
-
-    /* Now delete orphaned ._ files */
     if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
                                 deletecurdir_ea_osx_loop,
-                                NULL, 0, 0)) != 0)
+                                vol, NULL, 0)) != 0)
         return err;
 
 #endif
@@ -542,7 +522,7 @@ static int RF_setdirunixmode_ea(VFS_FUNC_ARGS_SETDIRUNIXMODE)
 static int RF_setfilmode_ea(VFS_FUNC_ARGS_SETFILEMODE)
 {
 #ifndef HAVE_EAFD
-    return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask);
+    return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st);
 #endif
     return 0;
 }
@@ -643,7 +623,7 @@ static int RF_renamefile_ea(VFS_FUNC_ARGS_RENAMEFILE)
         struct stat st;
 
         err = errno;
-        if (errno == ENOENT && lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+        if (errno == ENOENT && ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
             return 0;
         errno = err;
         return -1;
@@ -827,7 +807,7 @@ static struct vfs_ops netatalk_ea_sys = {
  * Tertiary VFS modules for ACLs
  */
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
 static struct vfs_ops netatalk_solaris_acl_adouble = {
     /* validupath:        */ NULL,
     /* rf_chown:          */ NULL,
@@ -895,7 +875,7 @@ void initvol_vfs(struct vol *vol)
     }
 
     /* ACLs */
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_NFSV4_ACLS
     vol->vfs_modules[2] = &netatalk_solaris_acl_adouble;
 #endif
 #ifdef HAVE_POSIX_ACLS
index 09505bd76688e62c073a84b4357b01d0af730fbe..8114ad3645182830e93861e8517c4539b07a7f89 100644 (file)
@@ -86,9 +86,6 @@ if INSTALL_LIBEVENT
 dist_bin_SCRIPTS = event_rpcgen.py
 endif
 
-pkgconfigdir=$(libdir)/pkgconfig
-LIBEVENT_PKGCONFIG=libevent.pc
-
 # These sources are conditionally added by configure.in or conditionally
 # included from other files.
 PLATFORM_DEPENDENT_SRC = \
@@ -108,16 +105,13 @@ EXTRA_DIST = \
 LIBEVENT_LIBS_LA = libevent.la libevent_core.la libevent_extra.la
 if PTHREADS
 LIBEVENT_LIBS_LA += libevent_pthreads.la
-LIBEVENT_PKGCONFIG += libevent_pthreads.pc
 endif
 if OPENSSL
 LIBEVENT_LIBS_LA += libevent_openssl.la
-LIBEVENT_PKGCONFIG += libevent_openssl.pc
 endif
 
 if INSTALL_LIBEVENT
 lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA)
-pkgconfig_DATA = $(LIBEVENT_PKGCONFIG)
 else
 noinst_LTLIBRARIES =  $(LIBEVENT_LIBS_LA)
 endif
@@ -196,7 +190,7 @@ NO_UNDEFINED =
 MAYBE_CORE =
 endif
 
-GENERIC_LDFLAGS = -version-info $(VERSION_INFO) $(RELEASE) $(NO_UNDEFINED)
+GENERIC_LDFLAGS = -static
 
 libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
 libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
@@ -249,5 +243,7 @@ doxygen: FORCE
        doxygen $(srcdir)/Doxyfile
 FORCE:
 
-DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
+DISTCLEANFILES = *~ ./include/event2/event-config.h
+
+install:
 
index 6c541a8fe0e6fb42c6aecd76629029a3978e594b..8e1f9c69fc3e8ec838d054dcf0598267de4aab04 100644 (file)
@@ -1,5 +1,6 @@
 EXTRA_DIST = \
        afs-check.m4            \
+       ax_pthread.m4           \
        cnid-backend.m4         \
        config-checks.m4        \
        db3-check.m4            \
@@ -17,4 +18,3 @@ EXTRA_DIST = \
        summary.m4              \
        tcp-wrappers.m4         \
        util.m4
-
index 799cfa2649e6fefab6fd3ffe3fdfc0c9a1a19571..48925d88ce9aad993a6762a486d8d2f6bc4ff18b 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: afs-check.m4,v 1.4 2005-08-11 20:15:35 didg Exp $
 dnl Autoconf macro to check whether AFS support should be enabled
 
 AC_DEFUN([AC_NETATALK_AFS_CHECK], [
diff --git a/macros/ax_pthread.m4 b/macros/ax_pthread.m4
new file mode 100644 (file)
index 0000000..9ede48e
--- /dev/null
@@ -0,0 +1,309 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   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 the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 18
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            ax_cv_PTHREAD_PRIO_INHERIT, [
+                AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+        if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+        fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
index 1e0366142876fa9dd5c87f69b19f035f21fb4a03..c87e457c598c0832985228832bfeb867e7f57360 100644 (file)
@@ -108,6 +108,39 @@ AC_DEFUN([AC_NETATALK_CNID], [
     fi
     AM_CONDITIONAL(USE_TDB_BACKEND, test x"$use_tdb_backend" = x"yes")
 
+    dnl Check for mysql CNID backend
+    AC_ARG_VAR(MYSQL_CFLAGS, [C compiler flags for MySQL, overriding checks])
+    AC_ARG_VAR(MYSQL_LIBS, [linker flags for MySQL, overriding checks])
+
+    AC_MSG_CHECKING([MySQL library and headers])
+    AC_ARG_WITH(
+        mysql-config,
+        [AS_HELP_STRING([--with-mysql-config=PATH],[Path to mysql-config binary (default: mysql_config)])],
+        MYSQL_CONFIG=$withval
+    )
+
+    if test -z "$MYSQL_CONFIG" -a -z "$MYSQL_CFLAGS" -a -z "$MYSQL_LIBS" ; then
+        AC_PATH_PROG(MYSQL_CONFIG, mysql_config)
+    fi
+
+    if test -x "$MYSQL_CONFIG" ; then
+        AC_MSG_RESULT([$MYSQL_CONFIG])
+        MYSQL_CFLAGS="`$MYSQL_CONFIG --cflags`"
+        MYSQL_LIBS="`$MYSQL_CONFIG --libs`"
+        ac_cv_with_cnid_mysql="yes"
+    elif test -n "$MYSQL_CFLAGS" -a -n "$MYSQL_LIBS" ; then
+        ac_cv_with_cnid_mysql="yes"
+    fi
+
+    if test x"$ac_cv_with_cnid_mysql" = x"yes" ; then
+        compiled_backends="$compiled_backends mysql"
+        AC_DEFINE(CNID_BACKEND_MYSQL, 1, [whether the MySQL CNID module is available])
+    fi
+
+    AC_SUBST(MYSQL_CFLAGS)
+    AC_SUBST(MYSQL_LIBS)
+    AM_CONDITIONAL(USE_MYSQL_BACKEND, test x"$ac_cv_with_cnid_mysql" = x"yes")
+
     dnl Set default DID scheme
     AC_MSG_CHECKING([default DID scheme])
     AC_ARG_WITH(cnid-default-backend,
index 902220befa3581e0a1d6a9dea9bc4a850e4ea599..4135e5ce2801915853e598ef0f8596726b4bcc42 100644 (file)
@@ -155,6 +155,9 @@ if test "x$bdb_required" = "xyes"; then
                         if test x"${atalk_cv_bdb_version}" = x"yes"; then
                             BDB_CFLAGS="-I${bdbdir}/include${subdir}"
                             BDB_LIBS="-L${bdblibdir} ${atalk_cv_lib_db}"
+                            if test x"$need_dash_r" = x"yes"; then
+                                BDB_LIBS="$BDB_LIBS -R${bdblibdir}"
+                            fi
                             BDB_BIN="$bdbbindir"
                             BDB_PATH="$bdbdir"
                             bdbfound=yes
@@ -178,6 +181,9 @@ if test "x$bdb_required" = "xyes"; then
                            if test x"${atalk_cv_bdb_version}" = x"yes"; then
                               BDB_CFLAGS="-I${bdbdir}/include${subdir}"
                               BDB_LIBS="-L${bdblibdir} ${atalk_cv_lib_db}"
+                              if test x"$need_dash_r" = x"yes"; then
+                                 BDB_LIBS="$BDB_LIBS -R${bdblibdir}"
+                              fi
                               BDB_BIN="$bdbbindir"
                               BDB_PATH="$bdbdir"
                               bdbfound=yes
index d2a5544a29f76ed5d800902fa9a54a9087102113..8eb5864d25710d397d0516b529067333199614bc 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of grep
-dnl $Id: grep-check.m4,v 1.2 2002-02-14 18:02:04 jmarcus Exp $
 
 AC_DEFUN([AC_PROG_GREP], [
 AC_REQUIRE([AC_EXEEXT])dnl
index 34dfc08cc6c31128da4503e6e857a4ab6fc1038d..d8482f8d9384359a80d6a52537cf4cefed39916b 100644 (file)
@@ -1,5 +1,132 @@
 dnl Kitchen sink for configuration macros
 
+dnl Check for docbook
+AC_DEFUN(AX_CHECK_DOCBOOK, [
+  # It's just rude to go over the net to build
+  XSLTPROC_FLAGS=--nonet
+  DOCBOOK_ROOT=
+  XSLTPROC_WORKS=no
+
+  AC_ARG_WITH(docbook,
+    AS_HELP_STRING(
+      [--with-docbook],
+      [Path to Docbook XSL directory]
+    ),
+    [DOCBOOK_ROOT=$withval]
+  )
+
+  if test -n "$DOCBOOK_ROOT" ; then
+    AC_CHECK_PROG(XSLTPROC,xsltproc,xsltproc,)
+    if test -n "$XSLTPROC"; then
+      AC_MSG_CHECKING([whether xsltproc works])
+      DB_FILE="$DOCBOOK_ROOT/html/docbook.xsl"
+      $XSLTPROC $XSLTPROC_FLAGS $DB_FILE >/dev/null 2>&1 << END
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="test">
+</book>
+END
+      if test "$?" = 0; then
+        XSLTPROC_WORKS=yes
+      fi
+      AC_MSG_RESULT($XSLTPROC_WORKS)
+    fi
+  fi
+
+  AC_MSG_CHECKING([whether to build Docbook documentation])
+  AC_MSG_RESULT($XSLTPROC_WORKS)
+
+  AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC_WORKS" = x"yes")
+  AC_SUBST(XSLTPROC_FLAGS)
+  AC_SUBST(DOCBOOK_ROOT)
+  AC_SUBST(XSLTPROC)
+])
+
+dnl Check for dtrace
+AC_DEFUN([AC_NETATALK_DTRACE], [
+  AC_ARG_WITH(dtrace,
+    AS_HELP_STRING(
+      [--with-dtrace],
+      [Enable dtrace probes (default: enabled if dtrace found)]
+    ),
+    [WDTRACE=$withval],
+    [WDTRACE=auto]
+  )
+  if test "x$WDTRACE" = "xyes" -o "x$WDTRACE" = "xauto" ; then
+    AC_CHECK_PROG([atalk_cv_have_dtrace], [dtrace], [yes], [no])
+    if test "x$atalk_cv_have_dtrace" = "xno" ; then
+      if test "x$WDTRACE" = "xyes" ; then
+        AC_MSG_FAILURE([dtrace requested but not found])
+      fi
+      WDTRACE="no"
+    else
+      WDTRACE="yes"
+    fi
+  fi
+
+  if test x"$WDTRACE" = x"yes" ; then
+    AC_DEFINE([WITH_DTRACE], [1], [dtrace probes])
+    DTRACE_LIBS=""
+    if test x"$this_os" = x"freebsd" ; then
+      DTRACE_LIBS="-lelf"
+    fi
+    AC_SUBST(DTRACE_LIBS)
+  fi
+  AM_CONDITIONAL(WITH_DTRACE, test "x$WDTRACE" = "xyes")
+])
+
+dnl Check for dbus-glib, for AFP stats
+AC_DEFUN([AC_NETATALK_DBUS_GLIB], [
+  atalk_cv_with_dbus=no
+
+  AC_ARG_WITH(afpstats,
+    AS_HELP_STRING(
+      [--with-afpstats],
+      [Enable AFP statistics via dbus (default: enabled if dbus found)]
+    ),,[withval=auto]
+  )
+
+  if test x"$withval" != x"no" ; then
+    PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
+    PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1, have_dbus_glib=yes, have_dbus_glib=no)
+    PKG_CHECK_MODULES(DBUS_GTHREAD, gthread-2.0, have_dbus_gthread=yes, have_dbus_gthread=no)
+    if test x$have_dbus_glib = xyes -a x$have_dbus = xyes -a x$have_dbus_gthread = xyes ; then
+        saved_CFLAGS=$CFLAGS
+        saved_LIBS=$LIBS
+        CFLAGS="$CFLAGS $DBUS_GLIB_CFLAGS"
+        LIBS="$LIBS $DBUS_GLIB_LIBS"
+        AC_CHECK_FUNC([dbus_g_bus_get_private], [atalk_cv_with_dbus=yes], [atalk_cv_with_dbus=no])
+        CFLAGS="$saved_CFLAGS"
+        LIBS="$saved_LIBS"
+    fi
+  fi
+
+  if test x"$withval" = x"yes" -a x"$atalk_cv_with_dbus" = x"no"; then
+    AC_MSG_ERROR([afpstats requested but dbus-glib not found])
+  fi
+
+  AC_ARG_WITH(
+      dbus-sysconf-dir,
+      [AS_HELP_STRING([--with-dbus-sysconf-dir=PATH],[Path to dbus system bus security configuration directory (default: ${sysconfdir}/dbus-1/system.d/)])],
+      ac_cv_dbus_sysdir=$withval,
+      ac_cv_dbus_sysdir='${sysconfdir}/dbus-1/system.d'
+  )
+  DBUS_SYS_DIR=""
+  if test x$atalk_cv_with_dbus = xyes ; then
+      AC_DEFINE(HAVE_DBUS_GLIB, 1, [Define if support for dbus-glib was found])
+      DBUS_SYS_DIR="$ac_cv_dbus_sysdir"
+  fi
+
+  AC_SUBST(DBUS_SYS_DIR)
+  AC_SUBST(DBUS_CFLAGS)
+  AC_SUBST(DBUS_LIBS)
+  AC_SUBST(DBUS_GLIB_CFLAGS)
+  AC_SUBST(DBUS_GLIB_LIBS)
+  AC_SUBST(DBUS_GTHREAD_CFLAGS)
+  AC_SUBST(DBUS_GTHREAD_LIBS)
+  AM_CONDITIONAL(HAVE_DBUS_GLIB, test x$atalk_cv_with_dbus = xyes)
+])
+
 dnl Whether to enable developer build
 AC_DEFUN([AC_DEVELOPER], [
     AC_MSG_CHECKING([whether to enable developer build])
@@ -13,22 +140,74 @@ AC_DEFUN([AC_DEVELOPER], [
     AM_CONDITIONAL(DEVELOPER, test x"$enable_dev" = x"yes")
 ])
 
+dnl Tracker, for Spotlight
+AC_DEFUN([AC_NETATALK_SPOTLIGHT], [
+    ac_cv_have_tracker=no
+    ac_cv_tracker_pkg_version_default=0.12
+    ac_cv_tracker_pkg_version_min=0.12
+
+    dnl Tracker SPARQL
+    AC_ARG_WITH([tracker-pkgconfig-version],
+      [AS_HELP_STRING([--with-tracker-pkgconfig-version=VERSION],[Version suffix of the Tracker SPARQL and tracker-miner pkg in pkg-config (default: 0.12)])],
+      [ac_cv_tracker_pkg_version=$withval],
+      [ac_cv_tracker_pkg_version=$ac_cv_tracker_pkg_version_default]
+    )
+
+    AC_ARG_WITH([tracker-prefix],
+      [AS_HELP_STRING([--with-tracker-prefix=PATH],[Prefix of Tracker installation (default: none)])],
+      [ac_cv_tracker_prefix=$withval],
+      [ac_cv_tracker_prefix="`pkg-config --variable=prefix tracker-sparql-$ac_cv_tracker_pkg_version`"]
+    )
+
+    AC_ARG_VAR([PKG_CONFIG_PATH], [Path to additional pkg-config packages])
+    PKG_CHECK_MODULES([TRACKER], [tracker-sparql-$ac_cv_tracker_pkg_version >= $ac_cv_tracker_pkg_version_min], [ac_cv_have_tracker_sparql=yes], [ac_cv_have_tracker_sparql=no])
+    PKG_CHECK_MODULES([TRACKER_MINER], [tracker-miner-$ac_cv_tracker_pkg_version >= $ac_cv_tracker_pkg_version_min], [ac_cv_have_tracker_miner=yes], [ac_cv_have_tracker_miner=no])
+
+    if test x"$ac_cv_have_tracker_sparql" = x"no" -o x"$ac_cv_have_tracker_miner" = x"no" ; then
+        if test x"$need_tracker_sparql" = x"yes" ; then
+            AC_MSG_ERROR([$ac_cv_tracker_pkg not found])
+        fi
+    else
+        AC_DEFINE(HAVE_TRACKER, 1, [Define if Tracker is available])
+        AC_DEFINE(HAVE_TRACKER_SPARQL, 1, [Define if Tracker SPARQL is available])
+        AC_DEFINE(HAVE_TRACKER_MINER, 1, [Define if Tracker miner library is available])
+        AC_DEFINE_UNQUOTED(TRACKER_PREFIX, ["$ac_cv_tracker_prefix"], [Path to Tracker])
+        AC_DEFINE([DBUS_DAEMON_PATH], ["/bin/dbus-daemon"], [Path to dbus-daemon])
+    fi
+
+    if test x"$ac_cv_have_tracker_sparql" = x"yes" ; then
+       ac_cv_have_tracker=yes
+    fi
+
+    AC_SUBST(TRACKER_CFLAGS)
+    AC_SUBST(TRACKER_LIBS)
+    AC_SUBST(TRACKER_MINER_CFLAGS)
+    AC_SUBST(TRACKER_MINER_LIBS)
+    AM_CONDITIONAL(HAVE_TRACKER_SPARQL, [test x"$ac_cv_have_tracker_sparql" = x"yes"])
+])
+
 dnl Whether to disable bundled libevent
 AC_DEFUN([AC_NETATALK_LIBEVENT], [
-    use_bundled_libevent=no
-    AC_MSG_CHECKING([whether to use bundled or installed libevent])
+    AC_MSG_CHECKING([whether to use bundled libevent])
+    AC_ARG_WITH(
+        libevent,
+        [AS_HELP_STRING([--with-libevent],[whether to use the bundled libevent (default: yes)])],
+        use_bundled_libevent=$withval,
+        use_bundled_libevent=yes
+    )
     AC_ARG_WITH(
         libevent-header,
         [AS_HELP_STRING([--with-libevent-header],[path to libevent header files])],
-        LIBEVENT_CFLAGS=-I$withval,
-        use_bundled_libevent=yes
+        [use_bundled_libevent=no; LIBEVENT_CFLAGS=-I$withval]
     )
     AC_ARG_WITH(
         libevent-lib,
-        [AS_HELP_STRING([--with-libevent-lib],[path to libevent header library])],
-        LIBEVENT_LDFLAGS=-L$withval,
-        use_bundled_libevent=yes
+        [AS_HELP_STRING([--with-libevent-lib],[path to libevent library])],
+        [use_bundled_libevent=no; LIBEVENT_LDFLAGS=-L$withval]
     )
+    if test x"$LIBEVENT_CFLAGS" = x"-Iyes" -o x"$LIBEVENT_LDFLAGS" = x"-Lyes" ; then
+        AC_MSG_ERROR([--with-libevent requires a path])
+    fi
     AC_MSG_RESULT([$use_bundled_libevent])
     if test x"$use_bundled_libevent" = x"yes" ; then
         AC_CONFIG_SUBDIRS([libevent])
@@ -38,6 +217,31 @@ AC_DEFUN([AC_NETATALK_LIBEVENT], [
     AM_CONDITIONAL(USE_BUILTIN_LIBEVENT, test x"$use_bundled_libevent" = x"yes")
 ])
 
+dnl Whether to disable bundled tdb
+AC_DEFUN([AC_NETATALK_TDB], [
+    AC_ARG_WITH(
+        tdb,
+        [AS_HELP_STRING([--with-tdb],[whether to use the bundled tdb (default: yes)])],
+        use_bundled_tdb=$withval,
+        use_bundled_tdb=yes
+    )
+    AC_MSG_CHECKING([whether to use bundled tdb])
+    AC_MSG_RESULT([$use_bundled_tdb])
+
+    if test x"$use_bundled_tdb" = x"yes" ; then
+        AC_DEFINE(USE_BUILTIN_TDB, 1, [Use internal tbd])
+    else
+        if test -z "$TDB_LIBS" ; then
+            PKG_CHECK_MODULES(TDB, tdb, , [AC_MSG_ERROR([couldn't find tdb with pkg-config])])
+        fi
+        use_bundled_tdb=no
+    fi        
+
+    AC_SUBST(TDB_CFLAGS)
+    AC_SUBST(TDB_LIBS)
+    AM_CONDITIONAL(USE_BUILTIN_TDB, test x"$use_bundled_tdb" = x"yes")
+])
+
 dnl Filesystem Hierarchy Standard (FHS) compatibility
 AC_DEFUN([AC_NETATALK_FHS], [
 AC_MSG_CHECKING([whether to use Filesystem Hierarchy Standard (FHS) compatibility])
@@ -56,13 +260,47 @@ AC_ARG_ENABLE(fhs,
                use_pam_so=yes
                AC_DEFINE(FHS_COMPATIBILITY, 1, [Define if you want compatibily with the FHS])
                AC_MSG_RESULT([yes])
+        atalk_cv_fhs_compat=yes
        else
                AC_MSG_RESULT([no])
+        atalk_cv_fhs_compat=no
        fi
        ],[
                AC_MSG_RESULT([no])
-       ]
-)])
+        atalk_cv_fhs_compat=no
+])])
+
+dnl netatalk lockfile path
+AC_DEFUN([AC_NETATALK_LOCKFILE], [
+    AC_MSG_CHECKING([netatalk lockfile path])
+    AC_ARG_WITH(
+        lockfile,
+        [AS_HELP_STRING([--with-lockfile=PATH],[Path of netatalk lockfile])],
+        ac_cv_netatalk_lock=$withval,
+        ac_cv_netatalk_lock=""
+    )
+    if test -z "$ac_cv_netatalk_lock" ; then
+        ac_cv_netatalk_lock=/var/spool/locks/netatalk
+        if test x"$atalk_cv_fhs_compat" = x"yes" ; then
+            ac_cv_netatalk_lock=/var/run/netatalk.pid
+        else
+            case "$host_os" in
+            *freebsd*)
+                ac_cv_netatalk_lock=/var/spool/lock/netatalk
+                ;;
+            *netbsd*|*openbsd*)
+                ac_cv_netatalk_lock=/var/run/netatalk.pid
+                ;;
+            *linux*)
+                ac_cv_netatalk_lock=/var/lock/netatalk
+                ;;
+            esac
+        fi
+    fi
+    AC_DEFINE_UNQUOTED(PATH_NETATALK_LOCK, ["$ac_cv_netatalk_lock"], [netatalk lockfile path])
+    AC_SUBST(PATH_NETATALK_LOCK, ["$ac_cv_netatalk_lock"])
+    AC_MSG_RESULT([$ac_cv_netatalk_lock])
+])
 
 dnl 64bit platform check
 AC_DEFUN([AC_NETATALK_64BIT_LIBS], [
@@ -245,7 +483,7 @@ AC_ARG_ENABLE(shell-check,
 )
 ])
 
-dnl Check for optional sysv initscript install
+dnl Check for optional initscript install
 AC_DEFUN([AC_NETATALK_INIT_STYLE], [
     AC_ARG_WITH(init-style,
                 [  --with-init-style       use OS specific init config [[redhat-sysv|redhat-systemd|suse-sysv|suse-systemd|gentoo|netbsd|debian|solaris|systemd]]],
@@ -257,36 +495,46 @@ AC_DEFUN([AC_NETATALK_INIT_STYLE], [
         ;;
     "redhat-sysv")
            AC_MSG_RESULT([enabling redhat-style sysv initscript support])
+           ac_cv_init_dir="/etc/rc.d/init.d"
            ;;
     "redhat-systemd")
            AC_MSG_RESULT([enabling redhat-style systemd support])
+           ac_cv_init_dir="/usr/lib/systemd/system"
            ;;
     "suse")
            AC_MSG_ERROR([--with-init-style=suse is obsoleted. Use suse-sysv or suse-systemd.])
         ;;
     "suse-sysv")
            AC_MSG_RESULT([enabling suse-style sysv initscript support])
+           ac_cv_init_dir="/etc/init.d"
            ;;
     "suse-systemd")
            AC_MSG_RESULT([enabling suse-style systemd support (>=openSUSE12.1)])
+           ac_cv_init_dir="/usr/lib/systemd/system"
            ;;
     "gentoo")
            AC_MSG_RESULT([enabling gentoo-style initscript support])
+           ac_cv_init_dir="/etc/init.d"
         ;;
     "netbsd")
            AC_MSG_RESULT([enabling netbsd-style initscript support])
+           ac_cv_init_dir="/etc/rc.d"
         ;;
     "debian")
            AC_MSG_RESULT([enabling debian-style initscript support])
+           ac_cv_init_dir="/etc/init.d"
         ;;
     "solaris")
            AC_MSG_RESULT([enabling solaris-style SMF support])
+           ac_cv_init_dir="/lib/svc/manifest/network/"
         ;;
     "systemd")
            AC_MSG_RESULT([enabling general systemd support])
+           ac_cv_init_dir="/usr/lib/systemd/system"
         ;;
     "none")
            AC_MSG_RESULT([disabling init-style support])
+           ac_cv_init_dir="none"
         ;;
     *)
            AC_MSG_ERROR([illegal init-style])
@@ -301,6 +549,12 @@ AC_DEFUN([AC_NETATALK_INIT_STYLE], [
     AM_CONDITIONAL(USE_SYSTEMD, test x$init_style = xsystemd || test x$init_style = xredhat-systemd || test x$init_style = xsuse-systemd)
     AM_CONDITIONAL(USE_UNDEF, test x$init_style = xnone)
 
+    AC_ARG_WITH(init-dir,
+                [  --with-init-dir=PATH    path to OS specific init directory],
+                ac_cv_init_dir="$withval", ac_cv_init_dir="$ac_cv_init_dir"
+    )
+    INIT_DIR="$ac_cv_init_dir"
+    AC_SUBST(INIT_DIR, ["$ac_cv_init_dir"])
 ])
 
 dnl OS specific configuration
@@ -355,7 +609,7 @@ fi
 dnl ----- Linux specific -----
 if test x"$this_os" = "xlinux"; then 
        AC_MSG_RESULT([ * Linux specific configuration])
-       
+    AC_DEFINE(LINUX, 1, [OS is Linux]) 
        dnl ----- check if we need the quotactl wrapper
     AC_CHECK_HEADERS(linux/dqblk_xfs.h,,
                [AC_CHECK_HEADERS(linux/xqm.h linux/xfs_fs.h)
@@ -402,87 +656,8 @@ if test x"$this_os" = "xsolaris"; then
        AC_DEFINE(SOLARIS, 1, [Solaris compatibility macro])
     AC_DEFINE(_XOPEN_SOURCE, 600, [Solaris compilation environment])
     AC_DEFINE(__EXTENSIONS__,  1, [Solaris compilation environment])
-       CFLAGS="-I\$(top_srcdir)/sys/generic $CFLAGS"
        need_dash_r=yes
        init_style=solaris
-
-       solaris_module=no
-       AC_MSG_CHECKING([if we can build Solaris kernel module])
-       if test -x /usr/ccs/bin/ld && test x"$netatalk_cv_ddp_enabled" = x"yes" ; then
-               solaris_module=yes
-       fi
-       AC_MSG_RESULT([$solaris_module])
-
-       COMPILE_64BIT_KMODULE=no
-       KCFLAGS=""
-       KLDFLAGS=""
-       COMPILE_KERNEL_GCC=no
-
-       if test "$solaris_module" = "yes"; then
-          dnl Solaris kernel module stuff
-           AC_MSG_CHECKING([if we have to build a 64bit kernel module])
-
-          # check for isainfo, if not found it has to be a 32 bit kernel (<=2.6)       
-          if test -x /usr/bin/isainfo; then
-               # check for 64 bit platform
-               if isainfo -kv | grep '^64-bit'; then
-                       COMPILE_64BIT_KMODULE=yes
-               fi
-          fi
-
-          AC_MSG_RESULT([$COMPILE_64BIT_KMODULE])
-
-          if test "${GCC}" = yes; then
-               COMPILE_KERNEL_GCC=yes
-               if test "$COMPILE_64BIT_KMODULE" = yes; then
-               
-                        AC_MSG_CHECKING([if we can build a 64bit kernel module])
-                       
-                        case `$CC --version 2>/dev/null` in
-                       [[12]].* | 3.0.*)
-                               COMPILE_64BIT_KMODULE=no
-                               COMPILE_KERNEL_GCC=no   
-                               solaris_module=no;;
-                       *)
-                               # use for 64 bit
-                               KCFLAGS="-m64"
-                               #KLDFLAGS="-melf64_sparc"
-                               KLDFLAGS="-64";;
-                       esac    
-                       
-                       AC_MSG_RESULT([$COMPILE_64BIT_KMODULE])
-                       
-               else
-                       KCFLAGS=""
-                       KLDFLAGS=""
-               fi
-               KCFLAGS="$KCFLAGS -D_KERNEL -Wall -Wstrict-prototypes"
-           else
-               if test "$COMPILE_64BIT_KMODULE" = yes; then
-                # use Sun CC (for a 64-bit kernel, uncomment " -xarch=v9 -xregs=no%appl ")
-                       KCFLAGS="-xarch=v9 -xregs=no%appl"
-                       KLDFLAGS="-64"
-               else
-                       KCFLAGS=""
-                       KLDFLAGS=""
-               fi
-               KCFLAGS="-D_KERNEL $KCFLAGS -mno-app-regs -munaligned-doubles -fpcc-struct-return"
-          fi
-
-           AC_CACHE_CHECK([for timeout_id_t],netatalk_cv_HAVE_TIMEOUT_ID_T,[
-           AC_LINK_IFELSE([AC_LANG_PROGRAM([[\
-#include <sys/stream.h>
-#include <sys/ddi.h>]], [[\
-timeout_id_t dummy;
-]])],[netatalk_cv_HAVE_TIMEOUT_ID_T=yes],[netatalk_cv_HAVE_TIMEOUT_ID_T=no])])
-
-          AC_DEFINE(HAVE_TIMEOUT_ID_T, test x"$netatalk_cv_HAVE_TIMEOUT_ID" = x"yes", [define for timeout_id_t])
-       fi
-
-       AC_SUBST(COMPILE_KERNEL_GCC)
-       AC_SUBST(COMPILE_64BIT_KMODULE)
-       AC_SUBST(KCFLAGS)
-       AC_SUBST(KLDFLAGS)
 fi
 
 dnl Whether to run ldconfig after installing libraries
@@ -581,7 +756,7 @@ save_CFLAGS="$CFLAGS"
 save_LIBS="$LIBS"
 CFLAGS="$KRB5_CFLAGS"
 LIBS="$KRB5_LIBS"
-AC_CHECK_FUNCS([krb5_free_unparsed_name krb5_free_error_message])
+AC_CHECK_FUNCS([krb5_free_unparsed_name krb5_free_error_message krb5_free_keytab_entry_contents krb5_kt_free_entry])
 CFLAGS="$save_CFLAGS"
 LIBS="$save_LIBS"
 ])
@@ -602,7 +777,7 @@ dnl Check for LDAP support, for client-side ACL visibility
 AC_DEFUN([AC_NETATALK_LDAP], [
 AC_MSG_CHECKING(for LDAP (necessary for client-side ACL visibility))
 AC_ARG_WITH(ldap,
-    [AS_HELP_STRING([--with-ldap],
+    [AS_HELP_STRING([--with-ldap[[=PATH]]],
         [LDAP support (default=auto)])],
         netatalk_cv_ldap=$withval,
         netatalk_cv_ldap=auto
@@ -649,12 +824,13 @@ AC_SUBST(LDAP_CFLAGS)
 AC_SUBST(LDAP_LDFLAGS)
 AC_SUBST(LDAP_LIBS)
 CFLAGS="$save_CFLAGS"
-LDLFLAGS="$save_LDLFLAGS"
+LDFLAGS="$save_LDFLAGS"
 LIBS="$save_LIBS"
 ])
 
 dnl Check for ACL support
 AC_DEFUN([AC_NETATALK_ACL], [
+ac_cv_have_acls=no
 AC_MSG_CHECKING(whether to support ACLs)
 AC_ARG_WITH(acls,
     [AS_HELP_STRING([--with-acls],
@@ -672,112 +848,125 @@ AC_MSG_RESULT($with_acl_support)
 
 if test x"$with_acl_support" = x"no"; then
        AC_MSG_RESULT(Disabling ACL support)
-       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
-else
-    with_acl_support=yes
 fi
 
-if test x"$with_acl_support" = x"yes" ; then
-       AC_MSG_NOTICE(checking whether ACL support is available:)
+# Platform specific checks
+if test x"$with_acl_support" != x"no" ; then
        case "$host_os" in
-       *sysv5*)
-               AC_MSG_NOTICE(Using UnixWare ACLs)
-               AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
-               ;;
        *solaris*)
                AC_MSG_NOTICE(Using solaris ACLs)
-               AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+               AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether Solaris ACLs are available])
+               AC_DEFINE(HAVE_NFSV4_ACLS,1,[Whether NFSv4 ACLs are available])
                ACL_LIBS="$ACL_LIBS -lsec"
+               ac_cv_have_acls=yes
                ;;
-       *hpux*)
-               AC_MSG_NOTICE(Using HPUX ACLs)
-               AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
-               ;;
-       *irix*)
-               AC_MSG_NOTICE(Using IRIX ACLs)
-               AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
-               ;;
-       *aix*)
-               AC_MSG_NOTICE(Using AIX ACLs)
-               AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
-               ;;
-       *osf*)
-               AC_MSG_NOTICE(Using Tru64 ACLs)
-               AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
-               ACL_LIBS="$ACL_LIBS -lpacl"
+       *freebsd*)
+               AC_MSG_NOTICE(checking whether libsunacl is available)
+               sunacl_include_path="/usr/local/include"
+               sunacl_lib_path="/usr/local/lib"
+
+               save_CPPFLAGS=$CPPFLAGS
+               save_LDFLAGS=$LDFLAGS
+               save_LIBS=$LIBS
+
+               CPPFLAGS="-I$sunacl_include_path $CPPFLAGS"
+               AC_CHECK_HEADER([sunacl.h])
+
+               LDFLAGS="-L$sunacl_lib_path $LDFLAGS"
+               AC_CHECK_LIB([sunacl], [acl])
+
+               if test x"$ac_cv_header_sunacl_h" = x"yes" -a x"$ac_cv_lib_sunacl_acl" = x"yes" ; then
+                       AC_MSG_NOTICE([Enabling support for ZFS ACLs using libsunacl])
+                       ac_cv_have_acls=yes
+                       CFLAGS="-I$sunacl_include_path $CFLAGS"
+                       ACL_LIBS="$ACL_LIBS -L$sunacl_lib_path -lsunacl"
+                       AC_DEFINE(HAVE_FREEBSD_SUNACL, 1, [Whether FreeBSD ZFS ACLs with libsunacl are available])
+                       AC_DEFINE(HAVE_NFSV4_ACLS,1,[Whether NFSv4 ACLs are available])
+               else
+                       AC_MSG_NOTICE([libsunacl not found, disabling ZFS ACL support])
+               fi
+
+               CPPFLAGS=$save_CPPFLAGS
+               LDFLAGS=$save_LDFLAGS
+               LIBS=$save_LIBS
                ;;
-       *darwin*)
-               AC_MSG_NOTICE(ACLs on Darwin currently not supported)
-               AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+       esac
+fi
+
+if test x"$with_acl_support" != x"no" -a x"$ac_cv_have_acls" != x"yes" ; then
+       # Runtime checks for POSIX ACLs
+       AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+       case "$host_os" in
+       *linux*)
+               AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
                ;;
-       *)
-               AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
-               case "$host_os" in
-               *linux*)
-                       AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
-                       ;;
-               esac
-               AC_CACHE_CHECK([for POSIX ACL support],netatalk_cv_HAVE_POSIX_ACLS,[
+       esac
+
+       AC_CACHE_CHECK([for POSIX ACL support],netatalk_cv_HAVE_POSIX_ACLS,[
+               acl_LIBS=$LIBS
+               LIBS="$LIBS $ACL_LIBS"
+               AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+                       #include <sys/types.h>
+                       #include <sys/acl.h>
+               ]], [[
+                       acl_t acl;
+                       int entry_id;
+                       acl_entry_t *entry_p;
+                       return acl_get_entry(acl, entry_id, entry_p);
+               ]])],
+                       [netatalk_cv_HAVE_POSIX_ACLS=yes; ac_cv_have_acls=yes],
+                       [netatalk_cv_HAVE_POSIX_ACLS=no; ac_cv_have_acls=no]
+               )
+               LIBS=$acl_LIBS
+       ])
+
+       if test x"$netatalk_cv_HAVE_POSIX_ACLS" = x"yes"; then
+               AC_MSG_NOTICE(Using POSIX ACLs)
+               AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+
+               AC_CACHE_CHECK([for acl_get_perm_np],netatalk_cv_HAVE_ACL_GET_PERM_NP,[
                        acl_LIBS=$LIBS
                        LIBS="$LIBS $ACL_LIBS"
                        AC_LINK_IFELSE([AC_LANG_PROGRAM([[
                                #include <sys/types.h>
                                #include <sys/acl.h>
                        ]], [[
-                               acl_t acl;
-                               int entry_id;
-                               acl_entry_t *entry_p;
-                               return acl_get_entry(acl, entry_id, entry_p);
-                       ]])],[netatalk_cv_HAVE_POSIX_ACLS=yes],[netatalk_cv_HAVE_POSIX_ACLS=no
-                with_acl_support=no])
+                               acl_permset_t permset_d;
+                               acl_perm_t perm;
+                               return acl_get_perm_np(permset_d, perm);
+                       ]])],[netatalk_cv_HAVE_ACL_GET_PERM_NP=yes],[netatalk_cv_HAVE_ACL_GET_PERM_NP=no])
                        LIBS=$acl_LIBS
                ])
-               if test x"$netatalk_cv_HAVE_POSIX_ACLS" = x"yes"; then
-                       AC_MSG_NOTICE(Using POSIX ACLs)
-                       AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
-                       AC_CACHE_CHECK([for acl_get_perm_np],netatalk_cv_HAVE_ACL_GET_PERM_NP,[
-                               acl_LIBS=$LIBS
-                               LIBS="$LIBS $ACL_LIBS"
-                               AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-                                       #include <sys/types.h>
-                                       #include <sys/acl.h>
-                               ]], [[
-                                       acl_permset_t permset_d;
-                                       acl_perm_t perm;
-                                       return acl_get_perm_np(permset_d, perm);
-                               ]])],[netatalk_cv_HAVE_ACL_GET_PERM_NP=yes],[netatalk_cv_HAVE_ACL_GET_PERM_NP=no])
-                               LIBS=$acl_LIBS
-                       ])
-                       if test x"$netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
-                               AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
-                       fi
-
-
-                       AC_CACHE_CHECK([for acl_from_mode], netatalk_cv_HAVE_ACL_FROM_MODE,[
-                               acl_LIBS=$LIBS
-                               LIBS="$LIBS $ACL_LIBS"
-                AC_CHECK_FUNCS(acl_from_mode,
-                               [netatalk_cv_HAVE_ACL_FROM_MODE=yes],
-                               [netatalk_cv_HAVE_ACL_FROM_MODE=no])
-                               LIBS=$acl_LIBS
-                       ])
-                       if test x"netatalk_cv_HAVE_ACL_FROM_MODE" = x"yes"; then
-                               AC_DEFINE(HAVE_ACL_FROM_MODE,1,[Whether acl_from_mode() is available])
-                       fi
+               if test x"$netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+                       AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+               fi
 
-               else
-                       AC_MSG_NOTICE(ACL support is not avaliable)
-                       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+               AC_CACHE_CHECK([for acl_from_mode], netatalk_cv_HAVE_ACL_FROM_MODE,[
+                       acl_LIBS=$LIBS
+                       LIBS="$LIBS $ACL_LIBS"
+                       AC_CHECK_FUNCS(acl_from_mode,
+                               [netatalk_cv_HAVE_ACL_FROM_MODE=yes],
+                               [netatalk_cv_HAVE_ACL_FROM_MODE=no]
+                       )
+                       LIBS=$acl_LIBS
+               ])
+               if test x"netatalk_cv_HAVE_ACL_FROM_MODE" = x"yes"; then
+                  AC_DEFINE(HAVE_ACL_FROM_MODE,1,[Whether acl_from_mode() is available])
                fi
-               ;;
-    esac
+       fi
 fi
 
-if test x"$with_acl_support" = x"yes" ; then
-   AC_CHECK_HEADERS([acl/libacl.h])
-    AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
-    AC_SUBST(ACL_LIBS)
+if test x"$ac_cv_have_acls" = x"no" ; then
+       if test x"$with_acl_support" = x"yes" ; then
+               AC_MSG_ERROR(ACL support requested but not found)
+       else
+               AC_MSG_NOTICE(ACL support is not avaliable)
+       fi
+else
+       AC_CHECK_HEADERS([acl/libacl.h])
+       AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
 fi
+AC_SUBST(ACL_LIBS)
 ])
 
 dnl Check for Extended Attributes support
@@ -975,6 +1164,23 @@ if test x"$netatalk_cv_search_sendfile" = x"yes"; then
 fi
 ])
 
+dnl ------ Check for recvfile() --------
+AC_DEFUN([AC_NETATALK_RECVFILE], [
+case "$host_os" in
+*linux*)
+    AC_CHECK_FUNCS([splice], [atalk_cv_use_recvfile=yes])
+    ;;
+
+*)
+    ;;
+
+esac
+
+if test x"$atalk_cv_use_recvfile" = x"yes"; then
+    AC_DEFINE(WITH_RECVFILE, 1, [Whether recvfile should be used])
+fi
+])
+
 dnl --------------------- Check if realpath() takes NULL
 AC_DEFUN([AC_NETATALK_REALPATH], [
 AC_CACHE_CHECK([if the realpath function allows a NULL argument],
index 6bc40cbad841562161bf2c1a6a1f3cd776cdb5e4..609c92dcae18885775afd8b744b1d6a5a73f9b73 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: pam-check.m4,v 1.6 2010-01-11 13:06:02 franklahm Exp $
 dnl PAM finding macro
 
 AC_DEFUN([AC_NETATALK_PATH_PAM], [
@@ -98,6 +97,13 @@ AC_DEFUN([AC_NETATALK_PATH_PAM], [
            PAM_ACCOUNT=system
            PAM_PASSWORD=system
            PAM_SESSION=system
+        dnl Solaris 11+
+        elif test -f "$pampath/other" ; then
+           PAM_DIRECTIVE=include
+           PAM_AUTH=${PAMDIR}etc/pam.d/other
+           PAM_ACCOUNT=${PAMDIR}etc/pam.d/other
+           PAM_PASSWORD=${PAMDIR}etc/pam.d/other
+           PAM_SESSION=${PAMDIR}etc/pam.d/other
         dnl Fallback
         else
            PAM_DIRECTIVE=required
@@ -132,6 +138,15 @@ AC_DEFUN([AC_NETATALK_PATH_PAM], [
            AC_DEFINE(USE_PAM, 1, [Define to enable PAM support])
        fi
 
+    AC_ARG_WITH(
+        pam-confdir,
+        [AS_HELP_STRING([--with-pam-confdir=PATH],[Path to PAM config dir (default: ${sysconfdir}/pam.d)])],
+        ac_cv_pamdir=$withval,
+        ac_cv_pamdir='${sysconfdir}/pam.d'
+    )
+
+    PAMDIR="$ac_cv_pamdir"
+
     LIB_REMOVE_USR_LIB(PAM_LIBS)
     CFLAGS_REMOVE_USR_INCLUDE(PAM_CFLAGS)
        AC_SUBST(PAMDIR)
index 7ad755b973e52aaf02263c0896337acc54f30eaa..6ae7ea6f901a1e174fef04a88ccca740fa6a944d 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of Perl
-dnl $Id: perl-check.m4,v 1.4 2002-03-12 11:03:49 srittau Exp $
 
 AC_DEFUN([AC_PROG_PERL], [
        AC_REQUIRE([AC_EXEEXT])dnl
index 3f47b85f37ef7f3ee45c4a107e151d7868349e51..75d5c9538553f708b3a3cc89c1d17a4e9f26c28c 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of ps
-dnl $Id: ps-check.m4,v 1.2 2002-02-14 18:02:04 jmarcus Exp $
 
 AC_DEFUN([AC_PROG_PS], [
 AC_REQUIRE([AC_EXEEXT])dnl
index ad135080e91f0facd1610c26190561020741f54a..e2e145e616ed15da19fec87d1729c7ed211f9666 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: quota-check.m4,v 1.6 2005-07-20 23:58:21 didg Exp $
 dnl Autoconf macro to check for quota support
 dnl FIXME: This is in now way complete.
 
index 8d9c1b6c66650e5ca8f8f337057b8a229c69cccc..8a58142be879c1a8402dce97797e67feadee1ab6 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: ssl-check.m4,v 1.14 2008-11-22 12:07:26 didg Exp $
 dnl Autoconf macro to check for SSL or OpenSSL
 
 AC_DEFUN([AC_NETATALK_CRYPT], [
index 94e84ac94ce565f2d103d882c6f1cbc7397ea3e5..de8aaa0cb8fa9493a705e134335f03c55c61d612 100644 (file)
@@ -11,7 +11,8 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        fi
        AC_MSG_RESULT([    AFP:])
        AC_MSG_RESULT([         Extended Attributes: $neta_cv_eas])
-       AC_MSG_RESULT([         ACL support: $with_acl_support])
+       AC_MSG_RESULT([         ACL support: $ac_cv_have_acls])
+       AC_MSG_RESULT([         Spotlight: $ac_cv_have_tracker])
        AC_MSG_RESULT([    CNID:])
        AC_MSG_RESULT([         backends: $compiled_backends])
        AC_MSG_RESULT([    UAMS:])
@@ -53,14 +54,29 @@ dnl AC_MSG_RESULT([         Samba sharemode interop: $neta_cv_have_smbshmd])
        AC_MSG_RESULT([         ACL support:             $with_acl_support])
        AC_MSG_RESULT([         Kerberos support:        $with_kerberos])
        AC_MSG_RESULT([         LDAP support:            $netatalk_cv_ldap])
-       if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
+       AC_MSG_RESULT([         AFP stats via dbus:      $atalk_cv_with_dbus])
+       AC_MSG_RESULT([         dtrace probes:           $WDTRACE])
+       AC_MSG_RESULT([    Paths:])
+       AC_MSG_RESULT([         Netatalk lockfile:       $ac_cv_netatalk_lock])
+       if test "x$init_style" != x"none"; then
+               AC_MSG_RESULT([         init directory:          $ac_cv_init_dir])
+       fi
+       if test x"$atalk_cv_with_dbus" = x"yes"; then
+               AC_MSG_RESULT([         dbus system directory:   $ac_cv_dbus_sysdir])
+       fi
+       if test x"$use_pam_so" = x"yes"; then
+          if test x"$netatalk_cv_install_pam" = x"yes"; then
+               AC_MSG_RESULT([         pam config directory:    $ac_cv_pamdir])
+          else
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
                AC_MSG_WARN([ cannot be installed. Please install the config/netatalk.pamd file manually.])
                AC_MSG_WARN([ If you're running Solaris or BSD you'll have to edit /etc/pam.conf to get PAM working.])
                AC_MSG_WARN([ You can also re-run configure and specify --without-pam to disable PAM support.])
+          fi
        fi
-
+       AC_MSG_RESULT([    Documentation:])
+       AC_MSG_RESULT([         Docbook:                 $XSLTPROC_WORKS])
 ])
 
 
@@ -68,9 +84,18 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        dnl #################################################
        dnl # Display summary of libraries detected
 
-       AC_MSG_RESULT([Using libraries:])
-       AC_MSG_RESULT([    LIBS = $LIBS])
-       AC_MSG_RESULT([    CFLAGS = $CFLAGS])
+       AC_MSG_RESULT([Compilation summary:])
+       AC_MSG_RESULT([    CPPFLAGS       = $CPPFLAGS])
+       AC_MSG_RESULT([    CFLAGS         = $CFLAGS])
+       AC_MSG_RESULT([    LIBS           = $LIBS])
+       AC_MSG_RESULT([    PTHREADS:])
+       AC_MSG_RESULT([        LIBS   = $PTHREAD_LIBS])
+       AC_MSG_RESULT([        CFLAGS = $PTHREAD_CFLAGS])
+       if test x"$ac_cv_have_tracker" = x"yes"; then
+               AC_MSG_RESULT([    TRACKER:])
+               AC_MSG_RESULT([        LIBS   = $TRACKER_LIBS])
+               AC_MSG_RESULT([        CFLAGS = $TRACKER_CFLAGS])
+       fi
        if test x"$neta_cv_have_openssl" = x"yes"; then
                AC_MSG_RESULT([    SSL:])
                AC_MSG_RESULT([        LIBS   = $SSL_LIBS])
@@ -113,7 +138,26 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        fi
        if test x"$netatalk_cv_ldap" = x"yes"; then
                AC_MSG_RESULT([    LDAP:])
-               AC_MSG_RESULT([        LIBS   = $LDAP_LDLFLAGS $LDAP_LIBS])
+               AC_MSG_RESULT([        LIBS   = $LDAP_LDFLAGS $LDAP_LIBS])
                AC_MSG_RESULT([        CFLAGS = $LDAP_CFLAGS])
        fi
+    AC_MSG_RESULT([    LIBEVENT:])
+    if test x"$use_bundled_libevent" = x"yes"; then
+               AC_MSG_RESULT([        bundled])
+    else
+               AC_MSG_RESULT([        LIBS   = $LIBEVENT_CFLAGS])
+               AC_MSG_RESULT([        CFLAGS = $LIBEVENT_LDFLAGS])
+    fi
+    AC_MSG_RESULT([    TDB:])
+    if test x"$use_bundled_tdb" = x"yes"; then
+               AC_MSG_RESULT([        bundled])
+    else
+               AC_MSG_RESULT([        LIBS   = $TDB_LIBS])
+               AC_MSG_RESULT([        CFLAGS = $TDB_CFLAGS])
+    fi
+       if test x"$ac_cv_with_cnid_mysql" = x"yes"; then
+               AC_MSG_RESULT([    MySQL:])
+               AC_MSG_RESULT([        LIBS   = $MYSQL_LIBS])
+               AC_MSG_RESULT([        CFLAGS = $MYSQL_CFLAGS])
+       fi
 ])
index 55b0f8c179428c464634cd6f5aa601b6f6521a9f..76b75a09cd05a85d633c1bdfd20645d83cb86f82 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: tcp-wrappers.m4,v 1.4 2008-08-11 20:44:03 didg Exp $
 
 AC_DEFUN([AC_NETATALK_TCP_WRAPPERS], [
        check=maybe
index 895a5e778b39b68094b7bf7771c67cf63c08dec9..8f1b0d992ec94e80ad60fd4ba1688d5d9900b917 100644 (file)
@@ -1,4 +1,3 @@
 Makefile
 Makefile.in
-.gitignore
 *.o
index c767a967abd49b2fcfcba5f0d80066eb03c9b2a1..0ac9461e8a4ab22a449d068e52155a4879735fd3 100644 (file)
@@ -1,11 +1,3 @@
 Makefile
 Makefile.in
-apple_cp.1
-apple_dump.1
-apple_mv.1
-apple_rm.1
-asip-status.pl.1
-afpldaptest.1
-uniconv.1
-.gitignore
-*.o
+*.1
index fe81a7d762bf3f8831066049d9523bbeef6dd1ee..07f4f9a71dd061aace628bc2598b0847403aef19 100644 (file)
@@ -1,27 +1,15 @@
 # Makefile.am for man/man1/
 
-pkgconfdir = @PKGCONFDIR@
-
-SUFFIXES= .tmpl .
-
-.tmpl:
-       sed -e s@:SBINDIR:@${sbindir}@ \
-           -e s@:BINDIR:@${bindir}@ \
-           -e s@:ETCDIR:@${pkgconfdir}@ \
-           -e s@:LIBDIR:@${libdir}@ \
-           -e s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@ \
-           <$< >$@
-
-GENERATED_MANS = uniconv.1 asip-status.pl.1 afpldaptest.1
-TEMPLATE_FILES = uniconv.1.tmpl asip-status.pl.1.tmpl afpldaptest.1.tmpl
-
-NONGENERATED_MANS      =       ad.1 \
-                               afppasswd.1 \
-                               apple_dump.1 \
-                               dbd.1 \
-                               macusers.1 \
-                               netatalk-config.1
-
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
-CLEANFILES = $(GENERATED_MANS)
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+man_MANS = \
+       ad.1 \
+       afpldaptest.1 \
+       afppasswd.1 \
+       afpstats.1 \
+       apple_dump.1 \
+       asip-status.pl.1 \
+       dbd.1 \
+       macusers.1 \
+       netatalk-config.1 \
+       uniconv.1
+
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man1/ad.1 b/man/man1/ad.1
deleted file mode 100644 (file)
index 4d85e8b..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-'\" t
-.\"     Title: ad
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Sep 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AD" "1" "02 Sep 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-ad \- Netatalk compatible UNIX file utility suite\&.
-.SH "SYNOPSIS"
-.HP \w'\fBad\fR\ 'u
-\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
-.HP \w'\fBad\fR\ 'u
-\fBad\fR {\-v\ |\ \-\-version}
-.SH "DESCRIPTION"
-.PP
-\fBad\fR
-is a UNIX file utlity suite with Netatalk compatibity\&. AppleDouble
-files in
-\&.AppleDouble
-directories and the CNID databases are updated as appropiate\&.
-.SH "AVAILABLE COMMANDS"
-.HP \w'\fBad\ ls\fR\ 'u
-\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
-.PP
-List files and directories\&.
-.HP \w'\fBad\ cp\fR\ 'u
-\fBad cp\fR [\-aipvf] {src_file} {dst_file}
-.HP \w'\fBad\ cp\ \-R\fR\ 'u
-\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
-.PP
-Copy files and directories\&.
-.HP \w'\fBad\ mv\fR\ 'u
-\fBad mv\fR [\-finv] {src_file} {dst_file}
-.HP \w'\fBad\ mv\fR\ 'u
-\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
-.PP
-Move files and directories\&.
-.HP \w'\fBad\ rm\fR\ 'u
-\fBad rm\fR [\-Rv] {file|directory}
-.HP \w'\fBad\ \-v|\-\-version\fR\ 'u
-\fBad \-v|\-\-version\fR
-.PP
-Show version\&.
-.SH "AD LS"
-.PP
-List files and directories\&. Options:
-.PP
-\-d
-.RS 4
-Directories are listed as plain files
-.RE
-.PP
-\-R
-.RS 4
-list subdirectories recursively
-.RE
-.PP
-\-l
-.RS 4
-Long output, list AFP info
-.RE
-.PP
-\-u
-.RS 4
-List UNIX info
-.RE
-.PP
-\fILong output description\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
-
-FinderFlags (valid for (f)iles and/or (d)irectories):
-
-  d = On Desktop                      (f/d)
-  e = Hidden extension                (f/d)
-  m = Shared (can run multiple times) (f)
-  n = No INIT resources               (f)
-  i = Inited                          (f/d)
-  c = Custom icon                     (f/d)
-  t = Stationery                      (f)
-  s = Name locked                     (f/d)
-  b = Bundle                          (f/d)
-  v = Invisible                       (f/d)
-  a = Alias file                      (f/d)
-
-AFP Attributes:
-
-  y = System                          (f/d)
-  w = No write                        (f)
-  p = Needs backup                    (f/d)
-  r = No rename                       (f/d)
-  l = No delete                       (f/d)
-  o = No copy                         (f)
-
-Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
-.fi
-.if n \{\
-.RE
-.\}
-.SH "AD CP"
-.PP
-Copy files and directories\&.
-.PP
-In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
-.PP
-Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targetting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
-.PP
-Options:
-.PP
-\-a
-.RS 4
-Archive mode\&. Same as \-Rp\&.
-.RE
-.PP
-\-f
-.RS 4
-For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
-.RE
-.PP
-\-i
-.RS 4
-Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \'y\' or \'Y\', the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
-.RE
-.PP
-\-n
-.RS 4
-Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
-.RE
-.PP
-\-p
-.RS 4
-Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
-.RE
-.PP
-\-R
-.RS 4
-If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
-.RE
-.PP
-\-v
-.RS 4
-Cause cp to be verbose, showing files as they are copied\&.
-.RE
-.PP
-\-x
-.RS 4
-File system mount points are not traversed\&.
-.RE
-.SH "AD MV"
-.PP
-Move files and directories\&.
-.PP
-Move files around within an AFP volume, updating the CNID database as needed\&. If either:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-source or destination is not an AFP volume
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-source AFP volume != destination AFP volume
-.RE
-.sp
-.RE
-the files are copied and removed from the source\&.
-.PP
-Options:
-.PP
-\-f
-.RS 4
-Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
-.RE
-.PP
-\-i
-.RS 4
-Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\' or `Y\', the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
-.RE
-.PP
-\-n
-.RS 4
-Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
-.RE
-.PP
-\-v
-.RS 4
-Cause mv to be verbose, showing files after they are moved\&.
-.RE
-.SH "AD RM"
-.PP
-Remove files and directories\&.
-.PP
-The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
-.PP
-The options are as follows:
-.PP
-\-R
-.RS 4
-Attempt to remove the file hierarchy rooted in each file argument\&.
-.RE
-.PP
-\-v
-.RS 4
-Be verbose when deleting files, showing them as they are removed\&.
-.RE
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBdbd\fR(1),
-\fBapple_dump\fR(1)\&.
diff --git a/man/man1/ad.1.in b/man/man1/ad.1.in
new file mode 100644 (file)
index 0000000..1a807cf
--- /dev/null
@@ -0,0 +1,250 @@
+'\" t
+.\"     Title: ad
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 02 Sep 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AD" "1" "02 Sep 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+ad \- Netatalk compatible UNIX file utility suite\&.
+.SH "SYNOPSIS"
+.HP \w'\fBad\fR\ 'u
+\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
+.HP \w'\fBad\fR\ 'u
+\fBad\fR {\-v\ |\ \-\-version}
+.SH "DESCRIPTION"
+.PP
+\fBad\fR
+is a UNIX file utility suite with Netatalk compatibility\&. AppleDouble
+files in
+\&.AppleDouble
+directories and the CNID databases are updated as appropriate\&.
+.SH "AVAILABLE COMMANDS"
+.HP \w'\fBad\ ls\fR\ 'u
+\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
+.PP
+List files and directories\&.
+.HP \w'\fBad\ cp\fR\ 'u
+\fBad cp\fR [\-aipvf] {src_file} {dst_file}
+.HP \w'\fBad\ cp\ \-R\fR\ 'u
+\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Copy files and directories\&.
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file} {dst_file}
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Move files and directories\&.
+.HP \w'\fBad\ rm\fR\ 'u
+\fBad rm\fR [\-Rv] {file|directory}
+.HP \w'\fBad\ \-v|\-\-version\fR\ 'u
+\fBad \-v|\-\-version\fR
+.PP
+Show version\&.
+.SH "AD LS"
+.PP
+List files and directories\&. Options:
+.PP
+\-d
+.RS 4
+Directories are listed as plain files
+.RE
+.PP
+\-R
+.RS 4
+list subdirectories recursively
+.RE
+.PP
+\-l
+.RS 4
+Long output, list AFP info
+.RE
+.PP
+\-u
+.RS 4
+List UNIX info
+.RE
+.PP
+\fILong output description\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
+
+  d = On Desktop                      (f/d)
+  e = Hidden extension                (f/d)
+  m = Shared (can run multiple times) (f)
+  n = No INIT resources               (f)
+  i = Inited                          (f/d)
+  c = Custom icon                     (f/d)
+  t = Stationery                      (f)
+  s = Name locked                     (f/d)
+  b = Bundle                          (f/d)
+  v = Invisible                       (f/d)
+  a = Alias file                      (f/d)
+
+AFP Attributes:
+
+  y = System                          (f/d)
+  w = No write                        (f)
+  p = Needs backup                    (f/d)
+  r = No rename                       (f/d)
+  l = No delete                       (f/d)
+  o = No copy                         (f)
+
+Note: any letter appearing in uppercase means the flag is set but it\*(Aqs a directory for which the flag is not allowed\&.
+.fi
+.if n \{\
+.RE
+.\}
+.SH "AD CP"
+.PP
+Copy files and directories\&.
+.PP
+In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
+.PP
+Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targeting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
+.PP
+Options:
+.PP
+\-a
+.RS 4
+Archive mode\&. Same as \-Rp\&.
+.RE
+.PP
+\-f
+.RS 4
+For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \*(Aqy\*(Aq or \*(AqY\*(Aq, the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-p
+.RS 4
+Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
+.RE
+.PP
+\-R
+.RS 4
+If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
+.RE
+.PP
+\-v
+.RS 4
+Cause cp to be verbose, showing files as they are copied\&.
+.RE
+.PP
+\-x
+.RS 4
+File system mount points are not traversed\&.
+.RE
+.SH "AD MV"
+.PP
+Move files and directories\&.
+.PP
+Move files around within an AFP volume, updating the CNID database as needed\&. If either:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source or destination is not an AFP volume
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source AFP volume != destination AFP volume
+.RE
+.sp
+the files are copied and removed from the source\&.
+.PP
+Options:
+.PP
+\-f
+.RS 4
+Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\*(Aq or `Y\*(Aq, the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-v
+.RS 4
+Cause mv to be verbose, showing files after they are moved\&.
+.RE
+.SH "AD RM"
+.PP
+Remove files and directories\&.
+.PP
+The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
+.PP
+The options are as follows:
+.PP
+\-R
+.RS 4
+Attempt to remove the file hierarchy rooted in each file argument\&.
+.RE
+.PP
+\-v
+.RS 4
+Be verbose when deleting files, showing them as they are removed\&.
+.RE
+.SH "REPORTING BUGS"
+.PP
+Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
+.SH "SEE ALSO"
+.PP
+\fBdbd\fR(1),
+\fBapple_dump\fR(1)\&.
diff --git a/man/man1/afpldaptest.1.in b/man/man1/afpldaptest.1.in
new file mode 100644 (file)
index 0000000..94d6bd5
--- /dev/null
@@ -0,0 +1,67 @@
+'\" t
+.\"     Title: afpldaptest
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPLDAPTEST" "1" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpldaptest \- Syntactically check ldap parameters in afp\&.conf
+.SH "SYNOPSIS"
+.HP \w'\fBafpldaptest\fR\fB\fR\fBafpldaptest\fR\fB\fR\ 'u
+\fBafpldaptest\fR\fB\fR {\-u\ \fIUSER\fR | \-g\ \fIGROUP\fR | \-i\ \fIUUID\fR}
+.br
+\fBafpldaptest\fR\fB\fR {\-h | \-? | \-:}
+.SH "DESCRIPTION"
+.PP
+\fBafpldaptest\fR
+is a simple command to syntactically check ldap parameters in @pkgconfdir@/afp\&.conf\&.
+.SH "OPTIONS"
+.PP
+\fB\-u\fR \fIUSER\fR
+.RS 4
+Show uuid for
+\fIUSER\fR\&.
+.RE
+.PP
+\fB\-g\fR \fIGROUP\fR
+.RS 4
+Show uuid for
+\fIGROUP\fR\&.
+.RE
+.PP
+\fB\-i\fR \fIUUID\fR
+.RS 4
+Show user, group or local\-uuid for
+\fIUUID\fR\&.
+.RE
+.PP
+\fB\-h, \-?, \-:\fR
+.RS 4
+Show the help and exit\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5)
diff --git a/man/man1/afpldaptest.1.tmpl b/man/man1/afpldaptest.1.tmpl
deleted file mode 100644 (file)
index 1d4fbd7..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-'\" t
-.\"     Title: afpldaptest
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPLDAPTEST" "1" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afpldaptest \- Syntactically check ldap parameters in afp\&.conf
-.SH "SYNOPSIS"
-.HP \w'\fBafpldaptest\fR\fB\fR\fBafpldaptest\fR\fB\fR\ 'u
-\fBafpldaptest\fR\fB\fR {\-u\ \fIUSER\fR | \-g\ \fIGROUP\fR | \-i\ \fIUUID\fR}
-.br
-\fBafpldaptest\fR\fB\fR {\-h | \-? | \-:}
-.SH "DESCRIPTION"
-.PP
-\fBafpldaptest\fR
-is a simple command to syntactically check ldap parameters in :ETCDIR:/afp\&.conf\&.
-.SH "OPTIONS"
-.PP
-\fB\-u\fR \fIUSER\fR
-.RS 4
-Show uuid for
-\fIUSER\fR\&.
-.RE
-.PP
-\fB\-g\fR \fIGROUP\fR
-.RS 4
-Show uuid for
-\fIGROUP\fR\&.
-.RE
-.PP
-\fB\-i\fR \fIUUID\fR
-.RS 4
-Show user, group or local\-uuid for
-\fIUUID\fR\&.
-.RE
-.PP
-\fB\-h, \-?, \-:\fR
-.RS 4
-Show the help and exit\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafp.conf\fR(5)
diff --git a/man/man1/afppasswd.1 b/man/man1/afppasswd.1
deleted file mode 100644 (file)
index 860eb69..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-'\" t
-.\"     Title: afppasswd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPPASSWD" "1" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afppasswd \- netatalk password maintenance utility
-.SH "SYNOPSIS"
-.HP \w'\fBafppasswd\fR\fB\fR\fB\fR\ 'u
-\fBafppasswd\fR\fB\fR\fB\fR [\-acfn] [\-p\ \fIpasswd\fR\ \fIfile\fR] [\-u\ \fIminimum\fR\ \fIuid\fR]
-.SH "DESCRIPTION"
-.PP
-\fBafppasswd\fR
-allows the maintenance of afppasswd files created by netatalk for use by the uams_randnum\&.so UAM (providing the "Randnum exchange" and "2\-Way Randnum exchange" User Authentication Modules)\&.
-.PP
-\fBafppasswd\fR
-can either be called by root with parameters, or can be called by local system users with no parameters to change their AFP passwords\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" and "DHX2" UAM instead\&. Please compare with the
-Authentication chapter
-inside Netatalk\'s documentation\&.
-.sp .5v
-.RE
-.SH "EXAMPLE"
-.PP
-Local user changing their own password:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example% \fBafppasswd\fR
-Enter NEW AFP password: \fB(hidden)\fR
-Enter NEW AFP password again: \fB(hidden)\fR
-afppasswd: updated password\&.
-.fi
-.if n \{\
-.RE
-.\}
-.SH "OPTIONS"
-.PP
-\fB\-a\fR
-.RS 4
-Add a new user to the
-\fBafppasswd\fR
-file\&.
-.RE
-.PP
-\fB\-c\fR
-.RS 4
-Create and/or initialize
-\fBafppasswd\fR
-file or specific user\&.
-.RE
-.PP
-\fB\-f\fR
-.RS 4
-Force the current action\&.
-.RE
-.PP
-\fB\-p\fR\fI path\fR
-.RS 4
-Path to
-\fBafppasswd\fR
-file\&.
-.RE
-.PP
-\fB\-n\fR
-.RS 4
-If cracklib support is built into
-\fBnetatalk\fR
-this option will cause cracklib checking to be disabled, if the superuser does not want to have the password run against the cracklib dictionary\&.
-.RE
-.PP
-\fB\-u\fR\fI minimum uid\fR
-.RS 4
-This is the minimum
-\fIuser id\fR
-(uid) that
-\fBafppasswd\fR
-will use when creating users\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5)\&.
diff --git a/man/man1/afppasswd.1.in b/man/man1/afppasswd.1.in
new file mode 100644 (file)
index 0000000..6acce22
--- /dev/null
@@ -0,0 +1,122 @@
+'\" t
+.\"     Title: afppasswd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPPASSWD" "1" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afppasswd \- netatalk password maintenance utility
+.SH "SYNOPSIS"
+.HP \w'\fBafppasswd\fR\fB\fR\fB\fR\ 'u
+\fBafppasswd\fR\fB\fR\fB\fR [\-acfn] [\-p\ \fIpasswd\fR\ \fIfile\fR] [\-u\ \fIminimum\fR\ \fIuid\fR]
+.SH "DESCRIPTION"
+.PP
+\fBafppasswd\fR
+allows the maintenance of afppasswd files created by netatalk for use by the uams_randnum\&.so UAM (providing the "Randnum exchange" and "2\-Way Randnum exchange" User Authentication Modules)\&.
+.PP
+\fBafppasswd\fR
+can either be called by root with parameters, or can be called by local system users with no parameters to change their AFP passwords\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" and "DHX2" UAM instead\&. Please compare with the
+Authentication chapter
+inside Netatalk\*(Aqs documentation\&.
+.sp .5v
+.RE
+.SH "EXAMPLE"
+.PP
+Local user changing their own password:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example% \fBafppasswd\fR
+Enter NEW AFP password: \fB(hidden)\fR
+Enter NEW AFP password again: \fB(hidden)\fR
+afppasswd: updated password\&.
+.fi
+.if n \{\
+.RE
+.\}
+.SH "OPTIONS"
+.PP
+\fB\-a\fR
+.RS 4
+Add a new user to the
+\fBafppasswd\fR
+file\&.
+.RE
+.PP
+\fB\-c\fR
+.RS 4
+Create and/or initialize
+\fBafppasswd\fR
+file or specific user\&.
+.RE
+.PP
+\fB\-f\fR
+.RS 4
+Force the current action\&.
+.RE
+.PP
+\fB\-p\fR\fI path\fR
+.RS 4
+Path to
+\fBafppasswd\fR
+file\&.
+.RE
+.PP
+\fB\-n\fR
+.RS 4
+If cracklib support is built into
+\fBnetatalk\fR
+this option will cause cracklib checking to be disabled, if the superuser does not want to have the password run against the cracklib dictionary\&.
+.RE
+.PP
+\fB\-u\fR\fI minimum uid\fR
+.RS 4
+This is the minimum
+\fIuser id\fR
+(uid) that
+\fBafppasswd\fR
+will use when creating users\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5)\&.
diff --git a/man/man1/afpstats.1.in b/man/man1/afpstats.1.in
new file mode 100644 (file)
index 0000000..16dc0b1
--- /dev/null
@@ -0,0 +1,50 @@
+'\" t
+.\"     Title: afpstats
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 24 Mar 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPSTATS" "1" "24 Mar 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpstats \- List AFP statistics
+.SH "SYNOPSIS"
+.HP \w'\fBafpstats\fR\fB\fR\ 'u
+\fBafpstats\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+\fBafpstats\fR
+list AFP statistics via D\-Bus IPC\&.
+.SH "NOTE"
+.PP
+\fBafpd\fR
+must support D\-Bus\&. Check it by "\fBafpd \-V\fR"\&.
+.PP
+"\fBafpstats = yes\fR" must be set in
+@pkgconfdir@/afp\&.conf\&.
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBdbus-daemon\fR(1)
diff --git a/man/man1/apple_dump.1 b/man/man1/apple_dump.1
deleted file mode 100644 (file)
index 37e9f6e..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-'\" t
-.\"     Title: apple_dump
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 16 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "APPLE_DUMP" "1" "16 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_dump \- Dump AppleSingle/AppleDouble format data
-.SH "SYNOPSIS"
-.HP \w'\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\ 'u
-\fBapple_dump\fR\fB\fR [\-a] [\fIFILE\fR | \fIDIR\fR]
-.br
-\fBapple_dump\fR\fB\fR \-e \fIFILE\fR | \fIDIR\fR 
-.br
-\fBapple_dump\fR\fB\fR \-f [\fIFILE\fR]
-.br
-\fBapple_dump\fR\fB\fR \-d [\fIFILE\fR]
-.br
-\fBapple_dump\fR\fB\fR \-h | \-help | \-\-help 
-.br
-\fBapple_dump\fR\fB\fR \-v | \-version | \-\-version 
-.SH "DESCRIPTION"
-.PP
-\fBapple_dump\fR
-is a perl script to dump AppleSingle/AppleDouble format data\&.
-.PP
-This script can dump various AppleSingle/AppleDouble data created by mailer, archiver, Mac OS X, Netatalk and so on\&.
-.PP
-With no
-\fIFILE\fR|\fIDIR\fR, or when
-\fIFILE\fR|\fIDIR\fR
-is \-, read standard input\&.
-.SH "OPTIONS"
-.PP
-\fB\-a\fR [\fIFILE\fR|\fIDIR\fR]
-.RS 4
-This is default\&. Dump a AppleSingle/AppleDouble file for
-\fIFILE\fR
-or
-\fIDIR\fR
-automatically\&. If FILE is not AppleSingle/AppleDouble format, look for extended attribute,
-\fI\&.AppleDouble/FILE\fR
-and
-\fI\&._FILE\fR\&. If
-\fIDIR\fR, look for extended attribute,
-\fIDIR/\&.AppleDouble/\&.Parent\fR
-and
-\fI\&._DIR\fR\&.
-.RE
-.PP
-\fB\-e\fR \fIFILE\fR|\fIDIR\fR
-.RS 4
-Dump extended attribute of\fIFILE\fR
-or
-\fIDIR\fR\&.
-.RE
-.PP
-\fB\-f\fR [\fIFILE\fR]
-.RS 4
-Dump
-\fIFILE\fR\&. Assume FinderInfo to be FileInfo\&.
-.RE
-.PP
-\fB\-d\fR [\fIFILE\fR]
-.RS 4
-Dump
-\fIFILE\fR\&. Assume FinderInfo to be DirInfo\&.
-.RE
-.PP
-\fB\-h, \-help, \-\-help\fR
-.RS 4
-Display the help and exit
-.RE
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version and exit
-.RE
-.SH "NOTE"
-.PP
-There is no way to detect whether FinderInfo is FileInfo or DirInfo\&. By default, apple_dump examins whether file or directory, a parent directory is \&.AppleDouble, filename is \&._*, filename is \&.Parent, and so on\&.
-.PP
-If setting option \-e, \-f or \-d, assume FinderInfo and doesn\'t look for another file\&.
-.SH "SEE ALSO"
-.PP
-\fBad\fR(1),
-\fBgetfattr\fR(1),
-\fBattr\fR(1),
-\fBrunat\fR(1),
-\fBgetextattr\fR(8),
-\fBlsextattr\fR(8)
diff --git a/man/man1/apple_dump.1.in b/man/man1/apple_dump.1.in
new file mode 100644 (file)
index 0000000..c9d7ff6
--- /dev/null
@@ -0,0 +1,114 @@
+'\" t
+.\"     Title: apple_dump
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 16 Jul 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "APPLE_DUMP" "1" "16 Jul 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+apple_dump \- Dump AppleSingle/AppleDouble format data
+.SH "SYNOPSIS"
+.HP \w'\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\ 'u
+\fBapple_dump\fR\fB\fR [\-a] [\fIFILE\fR | \fIDIR\fR]
+.br
+\fBapple_dump\fR\fB\fR \-e \fIFILE\fR | \fIDIR\fR 
+.br
+\fBapple_dump\fR\fB\fR \-f [\fIFILE\fR]
+.br
+\fBapple_dump\fR\fB\fR \-d [\fIFILE\fR]
+.br
+\fBapple_dump\fR\fB\fR \-h | \-help | \-\-help 
+.br
+\fBapple_dump\fR\fB\fR \-v | \-version | \-\-version 
+.SH "DESCRIPTION"
+.PP
+\fBapple_dump\fR
+is a perl script to dump AppleSingle/AppleDouble format data\&.
+.PP
+This script can dump various AppleSingle/AppleDouble data created by mailer, archiver, Mac OS X, Netatalk and so on\&.
+.PP
+With no
+\fIFILE\fR|\fIDIR\fR, or when
+\fIFILE\fR|\fIDIR\fR
+is \-, read standard input\&.
+.SH "OPTIONS"
+.PP
+\fB\-a\fR [\fIFILE\fR|\fIDIR\fR]
+.RS 4
+This is default\&. Dump a AppleSingle/AppleDouble file for
+\fIFILE\fR
+or
+\fIDIR\fR
+automatically\&. If FILE is not AppleSingle/AppleDouble format, look for extended attribute,
+\fI\&.AppleDouble/FILE\fR
+and
+\fI\&._FILE\fR\&. If
+\fIDIR\fR, look for extended attribute,
+\fIDIR/\&.AppleDouble/\&.Parent\fR
+and
+\fI\&._DIR\fR\&.
+.RE
+.PP
+\fB\-e\fR \fIFILE\fR|\fIDIR\fR
+.RS 4
+Dump extended attribute of\fIFILE\fR
+or
+\fIDIR\fR\&.
+.RE
+.PP
+\fB\-f\fR [\fIFILE\fR]
+.RS 4
+Dump
+\fIFILE\fR\&. Assume FinderInfo to be FileInfo\&.
+.RE
+.PP
+\fB\-d\fR [\fIFILE\fR]
+.RS 4
+Dump
+\fIFILE\fR\&. Assume FinderInfo to be DirInfo\&.
+.RE
+.PP
+\fB\-h, \-help, \-\-help\fR
+.RS 4
+Display the help and exit
+.RE
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version and exit
+.RE
+.SH "NOTE"
+.PP
+There is no way to detect whether FinderInfo is FileInfo or DirInfo\&. By default, apple_dump examines whether file or directory, a parent directory is \&.AppleDouble, filename is \&._*, filename is \&.Parent, and so on\&.
+.PP
+If setting option \-e, \-f or \-d, assume FinderInfo and doesn\*(Aqt look for another file\&.
+.SH "SEE ALSO"
+.PP
+\fBad\fR(1),
+\fBgetfattr\fR(1),
+\fBattr\fR(1),
+\fBrunat\fR(1),
+\fBgetextattr\fR(8),
+\fBlsextattr\fR(8)
diff --git a/man/man1/asip-status.pl.1.in b/man/man1/asip-status.pl.1.in
new file mode 100644 (file)
index 0000000..2963617
--- /dev/null
@@ -0,0 +1,135 @@
+'\" t
+.\"     Title: asip-status.pl
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 24 Jul 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "ASIP\-STATUS\&.PL" "1" "24 Jul 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+asip-status.pl \- Queries AFP servers for their capabilities
+.SH "SYNOPSIS"
+.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
+\fBasip\-status\&.pl\fR\fB\fR [\-d] [\-i] [\-x] HOSTNAME[:PORT]
+
+.br
+.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
+\fBasip\-status\&.pl\fR\fB\fR \-v | \-version | \-\-version 
+.SH "DESCRIPTION"
+.PP
+\fBasip\-status\&.pl\fR
+is a perl script that sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and displays the results, namely "Machine type", the server\*(Aqs name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
+.PP
+When you don\*(Aqt supply :PORT, then the default AFP port, 548, will be used\&.
+.SH "OPTIONS"
+.PP
+\fB\-d\fR
+.RS 4
+Enable debug output\&.
+.RE
+.PP
+\fB\-i\fR
+.RS 4
+Show icon if it exists\&.
+.RE
+.PP
+\fB\-x\fR
+.RS 4
+Enable hex dump output\&.
+.RE
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version\&.
+.RE
+.SH "EXAMPLES"
+.PP
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBasip\-status\&.pl\fR 192\&.168\&.1\&.15
+AFP reply from 192\&.168\&.1\&.15:548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Macintosh
+AFP versions: AFPVersion 1\&.1,AFPVersion 2\&.0,AFPVersion 2\&.1,AFP2\&.2
+UAMs: Cleartxt passwrd,Randnum exchange,2\-Way Randnum exchange
+Volume Icon & Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsChgPwd
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSuperClient
+Server name: bookchan
+Signature:
+04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  \&.\&.e#\&.\&.e#\&.\&.e#\&.\&.e#
+                                                  
+Network address: 192\&.168\&.1\&.15:548 (TCP/IP address and port)
+Network address: 65280\&.128 (ddp address)
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBasip\-status\&.pl\fR myserver:10548
+AFP reply from myserver:10548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Netatalk3\&.0
+AFP versions: AFP2\&.2,AFPX03,AFP3\&.1,AFP3\&.2,AFP3\&.3
+UAMs: DHX2,DHCAST128
+Volume Icon & Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSrvrNotifications
+    SupportsOpenDirectory
+    SupportsUTF8Servername
+    SupportsUUIDs
+    SupportsExtSleep
+    SupportsSuperClient
+Server name: myserver
+Signature:
+8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  \&.\&.\&.:\&.\&.\&.>o1\&.\&.\&.\&.p\&.
+                                                  
+Network address: 192\&.168\&.1\&.154:10548 (TCP/IP address and port)
+UTF8 Servername: myserver
+.fi
+.if n \{\
+.RE
+.\}
+.SH "REPORTING BUGS"
+.PP
+Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
diff --git a/man/man1/asip-status.pl.1.tmpl b/man/man1/asip-status.pl.1.tmpl
deleted file mode 100644 (file)
index 4de42c5..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-'\" t
-.\"     Title: asip-status.pl
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 24 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "ASIP\-STATUS\&.PL" "1" "24 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-asip-status.pl \- Queries AFP servers for their capabilities
-.SH "SYNOPSIS"
-.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
-\fBasip\-status\&.pl\fR\fB\fR [\-d] [\-i] [\-x] HOSTNAME[:PORT]
-
-.br
-.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
-\fBasip\-status\&.pl\fR\fB\fR \-v | \-version | \-\-version 
-.SH "DESCRIPTION"
-.PP
-\fBasip\-status\&.pl\fR
-is a perl script that sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and displays the results, namely "Machine type", the server\'s name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
-.PP
-When you don\'t supply :PORT, then the default AFP port, 548, will be used\&.
-.SH "OPTIONS"
-.PP
-\fB\-d\fR
-.RS 4
-Enable debug output\&.
-.RE
-.PP
-\fB\-i\fR
-.RS 4
-Show icon if it exists\&.
-.RE
-.PP
-\fB\-x\fR
-.RS 4
-Enable hex dump output\&.
-.RE
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version\&.
-.RE
-.SH "EXAMPLES"
-.PP
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBasip\-status\&.pl\fR 192\&.168\&.1\&.15
-AFP reply from 192\&.168\&.1\&.15:548
-Flags: 1  Cmd: 3  ID: 57005
-Reply: DSIGetStatus
-Request ID: 57005
-Machine type: Macintosh
-AFP versions: AFPVersion 1\&.1,AFPVersion 2\&.0,AFPVersion 2\&.1,AFP2\&.2
-UAMs: Cleartxt passwrd,Randnum exchange,2\-Way Randnum exchange
-Volume Icon & Mask: Yes
-Flags: 
-    SupportsCopyFile
-    SupportsChgPwd
-    SupportsServerMessages
-    SupportsServerSignature
-    SupportsTCP/IP
-    SupportsSuperClient
-Server name: bookchan
-Signature:
-04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  \&.\&.e#\&.\&.e#\&.\&.e#\&.\&.e#
-                                                  
-Network address: 192\&.168\&.1\&.15:548 (TCP/IP address and port)
-Network address: 65280\&.128 (ddp address)
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBasip\-status\&.pl\fR myserver:10548
-AFP reply from myserver:10548
-Flags: 1  Cmd: 3  ID: 57005
-Reply: DSIGetStatus
-Request ID: 57005
-Machine type: Netatalk3\&.0
-AFP versions: AFP2\&.2,AFPX03,AFP3\&.1,AFP3\&.2,AFP3\&.3
-UAMs: DHX2,DHCAST128
-Volume Icon & Mask: Yes
-Flags: 
-    SupportsCopyFile
-    SupportsServerMessages
-    SupportsServerSignature
-    SupportsTCP/IP
-    SupportsSrvrNotifications
-    SupportsOpenDirectory
-    SupportsUTF8Servername
-    SupportsUUIDs
-    SupportsExtSleep
-    SupportsSuperClient
-Server name: myserver
-Signature:
-8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  \&.\&.\&.:\&.\&.\&.>o1\&.\&.\&.\&.p\&.
-                                                  
-Network address: 192\&.168\&.1\&.154:10548 (TCP/IP address and port)
-UTF8 Servername: myserver
-.fi
-.if n \{\
-.RE
-.\}
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
diff --git a/man/man1/dbd.1 b/man/man1/dbd.1
deleted file mode 100644 (file)
index d36189f..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-'\" t
-.\"     Title: dbd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 14 Sep 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "DBD" "1" "14 Sep 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-dbd \- CNID database maintenance
-.SH "SYNOPSIS"
-.HP \w'\fBdbd\fR\fB\fR\ 'u
-\fBdbd\fR\fB\fR [\-evx] {\-d\ [\-i]  | \-s\ [\-c|\-n]  | \-r\ [\-c|\-f|\-C]  | \-u} \fIvolumepath\fR
-.SH "DESCRIPTION"
-.PP
-\fBdbd\fR
-can dump, scan, reindex and rebuild
-\fINetatalk\fR
-dbd CNID databases\&. It must be run with appropiate permissions i\&.e\&. as root\&.
-\fBdbd\fR
-\fB\-s|\-r\fR
-can be run on active volumes, but
-\fBdbd \-rf\fR, which wipes the db before rebuilding it, checks and enforces that the chosen volume is not in use\&.
-.SH "COMMANDS"
-.PP
-\-d
-.RS 4
-Dump CNID database\&. With
-\fB\-i \fRdump indexes too\&.
-.RE
-.PP
-\-s
-.RS 4
-Scan volume:
-.sp
-Compare CNIDs in database with volume, test if \&.AppleDouble directories exist, test if AppleDouble files exist, report orphaned AppleDouble files, report directories inside \&.AppleDouble directories, check name encoding, heck for orphaned CNIDs in database (requires
-\fB\-e\fR)\&.
-.sp
-Options:
-.PP
-\-c
-.RS 4
-Don\'t check \&.AppleDouble stuff, only check orphaned\&.
-.RE
-.PP
-\-n
-.RS 4
-Don\'t open CNID database, skip CNID checks, only traverse filesystem
-.RE
-.RE
-.PP
-\-r
-.RS 4
-Rebuild volume:
-.sp
-Sync CNIDSs from database with volume, ensure \&.AppleDouble directories exist, ensure AppleDouble files exist, delete orphaned AppleDouble files, report directories inside \&.AppleDouble directories, check name encoding by roundtripping, delete orphaned CNIDs in database (requires
-\fB\-e\fR)\&.
-.sp
-Options:
-.PP
-\-C
-.RS 4
-Converts volume from adouble:v2 to adouble:ea
-.RE
-.PP
-\-c
-.RS 4
-Don\'t create \&.AppleDouble stuff, only cleanup orphaned\&.
-.RE
-.PP
-\-f
-.RS 4
-Wipe database and rebuild from IDs stored in AppleDouble files, only available for volumes without
-\fBnocnidcache\fR
-option\&. Implies
-\fB\-e\fR\&.
-.RE
-.RE
-.PP
-\-u
-.RS 4
-Upgrade:
-.sp
-Opens the database which triggers any necessary upgrades, then closes and exits\&.
-.RE
-.SH "OPTIONS"
-.PP
-\-e
-.RS 4
-Only work on inactive volumes and lock them (exclusive)
-.RE
-.PP
-\-x
-.RS 4
-Rebuild indexes (just for completeness, mostly useless!)
-.RE
-.PP
-\-v
-.RS 4
-verbose
-.RE
-.SH "WARNING"
-.PP
-In order to be able to run
-\fB\-rf\fR
-reconstructing the CNIDs in the database from the
-\fIAppleDouble\fR
-files, make sure you\'ve run a
-\fB\-r\fR
-rebuild sometimes before, where the CNIDs then would have been synched between database and
-\fIAppleDouble\fR
-files\&.
-.PP
-Also be careful about the option
-\fBnocnidcache\fR\&. Avoid this option if at all possible, because if prevents you from being able to use
-\fB\-f\fR\&.
-.SH "CNID BACKGROUND"
-.PP
-The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db will not know and not reflect that change\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. E\&.g\&. if several names in the path to the file or directory have changed, things may go wrong\&.
-.PP
-If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\'t changed\&.
-.SH "SEE ALSO"
-.PP
-\fBcnid_metad\fR(8),
-\fBcnid_dbd\fR(8)
diff --git a/man/man1/dbd.1.in b/man/man1/dbd.1.in
new file mode 100644 (file)
index 0000000..fb60ce9
--- /dev/null
@@ -0,0 +1,86 @@
+'\" t
+.\"     Title: dbd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 28 Dec 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "DBD" "1" "28 Dec 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+dbd \- CNID database maintenance
+.SH "SYNOPSIS"
+.HP \w'\fBdbd\fR\fB\fR\ 'u
+\fBdbd\fR\fB\fR [\-cfFstuvV] \fIvolumepath\fR
+.SH "DESCRIPTION"
+.PP
+\fBdbd\fR
+scans all file and directories of AFP volumes, updating the CNID database of the volume\&. It must be run with appropriate permissions i\&.e\&. as root\&.\&.
+.SH "OPTIONS"
+.PP
+\-c
+.RS 4
+convert from adouble:v2 to adouble:ea
+.RE
+.PP
+\-f
+.RS 4
+delete and recreate CNID database
+.RE
+.PP
+\-F
+.RS 4
+location of the afp\&.conf config file
+.RE
+.PP
+\-s
+.RS 4
+scan volume: treat the volume as read only and don\*(Aqt perform any filesystem modifications
+.RE
+.PP
+\-t
+.RS 4
+show statistics while running
+.RE
+.PP
+\-u
+.RS 4
+username for use with AFP volumes using user variable $u
+.RE
+.PP
+\-v
+.RS 4
+verbose
+.RE
+.PP
+\-V
+.RS 4
+display version info
+.RE
+.SH "CNID BACKGROUND"
+.PP
+The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID database will not reflect that change\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&.
+.SH "SEE ALSO"
+.PP
+\fBcnid_metad\fR(8),
+\fBcnid_dbd\fR(8)
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
diff --git a/man/man1/macusers.1 b/man/man1/macusers.1
deleted file mode 100644 (file)
index ea823a0..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-'\" t
-.\"     Title: macusers
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 13 Oct 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "MACUSERS" "1" "13 Oct 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-macusers \- List the users connecting via AFP
-.SH "SYNOPSIS"
-.HP \w'\fBmacusers\fR\fB\fR\ 'u
-\fBmacusers\fR\fB\fR
-.HP \w'\fBmacusers\fR\fB\fR\ 'u
-\fBmacusers\fR\fB\fR \-v | \-version | \-\-version | \-h | \-help | \-\-help 
-.SH "DESCRIPTION"
-.PP
-\fBmacusers\fR
-list the users connecting via AFP\&.
-.SH "OPTIONS"
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version and exit
-.RE
-.PP
-\fB\-h, \-help, \-\-help\fR
-.RS 4
-Display the help and exit
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8)
diff --git a/man/man1/macusers.1.in b/man/man1/macusers.1.in
new file mode 100644 (file)
index 0000000..ed948b2
--- /dev/null
@@ -0,0 +1,54 @@
+'\" t
+.\"     Title: macusers
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 13 Oct 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "MACUSERS" "1" "13 Oct 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+macusers \- List the users connecting via AFP
+.SH "SYNOPSIS"
+.HP \w'\fBmacusers\fR\fB\fR\ 'u
+\fBmacusers\fR\fB\fR
+.HP \w'\fBmacusers\fR\fB\fR\ 'u
+\fBmacusers\fR\fB\fR \-v | \-version | \-\-version | \-h | \-help | \-\-help 
+.SH "DESCRIPTION"
+.PP
+\fBmacusers\fR
+list the users connecting via AFP\&.
+.SH "OPTIONS"
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version and exit
+.RE
+.PP
+\fB\-h, \-help, \-\-help\fR
+.RS 4
+Display the help and exit
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8)
diff --git a/man/man1/megatron.1 b/man/man1/megatron.1
deleted file mode 100644 (file)
index dfd37d0..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-'\" t
-.\"     Title: megatron
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Sep 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "MEGATRON" "1" "02 Sep 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
-.SH "SYNOPSIS"
-.HP \w'\fBmegatron\fR\fB\fR\fBunbin\fR\fB\fR\fBunhex\fR\fB\fR\fBunsingle\fR\fB\fR\fBhqx2bin\fR\fB\fR\fBsingle2bin\fR\fB\fR\fBmacbinary\fR\fB\fR\ 'u
-\fBmegatron\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunbin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunhex\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunsingle\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBhqx2bin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBsingle2bin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBmacbinary\fR\fB\fR [\fIsourcefile\fR...]
-.SH "DESCRIPTION"
-.PP
-\fBmegatron\fR
-is used to transform files from BinHex, MacBinary, AppleSingle, or
-\fBnetatalk\fR
-style AppleDouble formats into MacBinary or
-\fBnetatalk\fR
-style AppleDouble formats\&. The
-\fBnetatalk\fR
-style AppleDouble format is the file format used by
-\fBafpd,\fR
-the
-\fBnetatalk\fR
-Apple Filing Protocol (AppleShare) server\&. BinHex, MacBinary, and AppleSingle are commonly used formats for transferring Macintosh files between machines via email or file transfer protocols\&.
-\fBmegatron\fR
-uses its name to determine what type of tranformation is being asked of it\&.
-.PP
-If
-\fBmegatron\fR
-is called as
-\fBunhex\fR
-,
-\fBunbin\fR
-or
-\fBunsingle\fR, it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble format\&. BinHex is the format most often used to send Macintosh files by e\-mail\&. Usually these files have an extension of "\&.hqx"\&. MacBinary is the format most often used by terminal emulators "on the fly" when transferring Macintosh files in binary mode\&. MacBinary files often have an extension of "\&.bin"\&. Some Macintosh LAN\-based email packages use uuencoded AppleSingle format to "attach" or "enclose" files in email\&. AppleSingle files don\'t have a standard filename extension\&.
-.PP
-If
-\fBmegatron\fR
-is called as
-\fBhqx2bin\fR,
-\fBsingle2bin\fR, or
-\fBmacbinary\fR, it will try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into MacBinary\&. This last translation may be useful in moving Macintosh files from your
-\fBafpd\fR
-server to some other machine when you can\'t copy them from the server using a Macintosh for some reason\&.
-.PP
-If
-\fBmegatron\fR
-is called with any other name, it uses the default translation, namely
-\fBunhex\fR\&.
-.PP
-If no source file is given, or if
-\fIsourcefile\fR
-is `\fB\-\fR\', and if the conversion is from a BinHex or MacBinary file,
-\fBmegatron\fR
-will read from standard input\&.
-.PP
-The filename used to store any output file is the filename that is encoded in the source file\&. MacBinary files are created with a "\&.bin" extension\&. In the case of conflicts, the old file is overwritten!
-.SH "OPTIONS"
-.PP
-\fB\-v, \-\-version\fR
-.RS 4
-Show version\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8)
diff --git a/man/man1/megatron.1.in b/man/man1/megatron.1.in
new file mode 100644 (file)
index 0000000..9b47c22
--- /dev/null
@@ -0,0 +1,102 @@
+'\" t
+.\"     Title: megatron
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 02 Sep 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "MEGATRON" "1" "02 Sep 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
+.SH "SYNOPSIS"
+.HP \w'\fBmegatron\fR\fB\fR\fBunbin\fR\fB\fR\fBunhex\fR\fB\fR\fBunsingle\fR\fB\fR\fBhqx2bin\fR\fB\fR\fBsingle2bin\fR\fB\fR\fBmacbinary\fR\fB\fR\ 'u
+\fBmegatron\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunbin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunhex\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunsingle\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBhqx2bin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBsingle2bin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBmacbinary\fR\fB\fR [\fIsourcefile\fR...]
+.SH "DESCRIPTION"
+.PP
+\fBmegatron\fR
+is used to transform files from BinHex, MacBinary, AppleSingle, or
+\fBnetatalk\fR
+style AppleDouble formats into MacBinary or
+\fBnetatalk\fR
+style AppleDouble formats\&. The
+\fBnetatalk\fR
+style AppleDouble format is the file format used by
+\fBafpd,\fR
+the
+\fBnetatalk\fR
+Apple Filing Protocol (AppleShare) server\&. BinHex, MacBinary, and AppleSingle are commonly used formats for transferring Macintosh files between machines via email or file transfer protocols\&.
+\fBmegatron\fR
+uses its name to determine what type of transformation is being asked of it\&.
+.PP
+If
+\fBmegatron\fR
+is called as
+\fBunhex\fR
+,
+\fBunbin\fR
+or
+\fBunsingle\fR, it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble format\&. BinHex is the format most often used to send Macintosh files by e\-mail\&. Usually these files have an extension of "\&.hqx"\&. MacBinary is the format most often used by terminal emulators "on the fly" when transferring Macintosh files in binary mode\&. MacBinary files often have an extension of "\&.bin"\&. Some Macintosh LAN\-based email packages use uuencoded AppleSingle format to "attach" or "enclose" files in email\&. AppleSingle files don\*(Aqt have a standard filename extension\&.
+.PP
+If
+\fBmegatron\fR
+is called as
+\fBhqx2bin\fR,
+\fBsingle2bin\fR, or
+\fBmacbinary\fR, it will try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into MacBinary\&. This last translation may be useful in moving Macintosh files from your
+\fBafpd\fR
+server to some other machine when you can\*(Aqt copy them from the server using a Macintosh for some reason\&.
+.PP
+If
+\fBmegatron\fR
+is called with any other name, it uses the default translation, namely
+\fBunhex\fR\&.
+.PP
+If no source file is given, or if
+\fIsourcefile\fR
+is `\fB\-\fR\*(Aq, and if the conversion is from a BinHex or MacBinary file,
+\fBmegatron\fR
+will read from standard input\&.
+.PP
+The filename used to store any output file is the filename that is encoded in the source file\&. MacBinary files are created with a "\&.bin" extension\&. In the case of conflicts, the old file is overwritten!
+.SH "OPTIONS"
+.PP
+\fB\-v, \-\-version\fR
+.RS 4
+Show version\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8)
diff --git a/man/man1/netatalk-config.1 b/man/man1/netatalk-config.1
deleted file mode 100644 (file)
index 43ebcb7..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-'\" t
-.\"     Title: netatalk-config
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 09 June 2001
-.\"    Manual: The Netatalk Project
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "NETATALK\-CONFIG" "1" "09 June 2001" "Netatalk 3.0" "The Netatalk Project"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-netatalk-config \- script to get information about the installed version of netatalk
-.SH "SYNOPSIS"
-.HP \w'\fBnetatalk\-config\fR\fB\fR\ 'u
-\fBnetatalk\-config\fR\fB\fR [\-\-prefix\ [\fI=DIR\fR]] [\-\-exec_prefix\ [\fI=DIR\fR]] [\-\-help] [\-\-version] [\-\-libs] [\-\-libs\-dirs] [\-\-libs\-names] [\-\-cflags] [\-\-macros]
-.SH "DESCRIPTION"
-.PP
-\fBnetatalk\-config\fR
-is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use the
-\fInetatalk\fR
-run\-time libraries\&.
-.SH "OPTIONS"
-.PP
-\fBnetatalk\-config\fR
-accepts the following options:
-.PP
-\fB\-\-help\fR
-.RS 4
-Print a short help for this command and exit\&.
-.RE
-.PP
-\fB\-\-version\fR
-.RS 4
-Print the currently installed version of
-\fInetatalk\fR
-on the standard output\&.
-.RE
-.PP
-\fB\-\-libs\fR
-.RS 4
-Print the linker flags that are necessary to link against the
-\fInetatalk\fR
-run\-time libraries\&.
-.RE
-.PP
-\fB\-\-libs\-dirs\fR
-.RS 4
-Print only the \-l/\-R part of \-\-libs\&.
-.RE
-.PP
-\fB\-\-libs\-names\fR
-.RS 4
-Print only the \-l part of \-\-libs\&.
-.RE
-.PP
-\fB\-\-cflags\fR
-.RS 4
-Print the compiler flags that are necessary to compile a program linked against the
-\fInetatalk\fR
-run\-time libraries\&.
-.RE
-.PP
-\fB\-\-macros\fR
-.RS 4
-Print the
-\fInetatalk\fR
-m4 directory\&.
-.RE
-.PP
-\fB\-\-prefix=PREFIX\fR
-.RS 4
-If specified, use PREFIX instead of the installation prefix that
-\fInetatalk\fR
-was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option is also used for the exec prefix if \-\-exec\-prefix was not specified\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
-.RE
-.PP
-\fB\-\-exec\e_prefix=PREFIX\fR
-.RS 4
-If specified, use PREFIX instead of the installation exec prefix that
-\fInetatalk\fR
-was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
-.RE
-.SH "COPYRIGHT"
-.PP
-Copyright \(co 1998 Owen Taylor
-.PP
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation\&.
-.PP
-Man page adapted for
-\fBnetatalk\-config\fR
-by Sebastian Rittau in 2001\&.
diff --git a/man/man1/netatalk-config.1.in b/man/man1/netatalk-config.1.in
new file mode 100644 (file)
index 0000000..f4947fa
--- /dev/null
@@ -0,0 +1,110 @@
+'\" t
+.\"     Title: netatalk-config
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 09 June 2001
+.\"    Manual: The Netatalk Project
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "NETATALK\-CONFIG" "1" "09 June 2001" "@NETATALK_VERSION@" "The Netatalk Project"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+netatalk-config \- script to get information about the installed version of netatalk
+.SH "SYNOPSIS"
+.HP \w'\fBnetatalk\-config\fR\fB\fR\ 'u
+\fBnetatalk\-config\fR\fB\fR [\-\-prefix\ [\fI=DIR\fR]] [\-\-exec_prefix\ [\fI=DIR\fR]] [\-\-help] [\-\-version] [\-\-libs] [\-\-libs\-dirs] [\-\-libs\-names] [\-\-cflags] [\-\-macros]
+.SH "DESCRIPTION"
+.PP
+\fBnetatalk\-config\fR
+is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use the
+\fInetatalk\fR
+run\-time libraries\&.
+.SH "OPTIONS"
+.PP
+\fBnetatalk\-config\fR
+accepts the following options:
+.PP
+\fB\-\-help\fR
+.RS 4
+Print a short help for this command and exit\&.
+.RE
+.PP
+\fB\-\-version\fR
+.RS 4
+Print the currently installed version of
+\fInetatalk\fR
+on the standard output\&.
+.RE
+.PP
+\fB\-\-libs\fR
+.RS 4
+Print the linker flags that are necessary to link against the
+\fInetatalk\fR
+run\-time libraries\&.
+.RE
+.PP
+\fB\-\-libs\-dirs\fR
+.RS 4
+Print only the \-l/\-R part of \-\-libs\&.
+.RE
+.PP
+\fB\-\-libs\-names\fR
+.RS 4
+Print only the \-l part of \-\-libs\&.
+.RE
+.PP
+\fB\-\-cflags\fR
+.RS 4
+Print the compiler flags that are necessary to compile a program linked against the
+\fInetatalk\fR
+run\-time libraries\&.
+.RE
+.PP
+\fB\-\-macros\fR
+.RS 4
+Print the
+\fInetatalk\fR
+m4 directory\&.
+.RE
+.PP
+\fB\-\-prefix=PREFIX\fR
+.RS 4
+If specified, use PREFIX instead of the installation prefix that
+\fInetatalk\fR
+was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option is also used for the exec prefix if \-\-exec\-prefix was not specified\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
+.RE
+.PP
+\fB\-\-exec\e_prefix=PREFIX\fR
+.RS 4
+If specified, use PREFIX instead of the installation exec prefix that
+\fInetatalk\fR
+was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
+.RE
+.SH "COPYRIGHT"
+.PP
+Copyright \(co 1998 Owen Taylor
+.PP
+Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation\&.
+.PP
+Man page adapted for
+\fBnetatalk\-config\fR
+by Sebastian Rittau in 2001\&.
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
diff --git a/man/man1/uniconv.1.in b/man/man1/uniconv.1.in
new file mode 100644 (file)
index 0000000..1ec2779
--- /dev/null
@@ -0,0 +1,203 @@
+'\" t
+.\"     Title: uniconv
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "UNICONV" "1" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+uniconv \- convert Netatalk volume encoding
+.SH "SYNOPSIS"
+.HP \w'\fBuniconv\fR\fB\fR\ 'u
+\fBuniconv\fR\fB\fR [\-ndv] \-c\ \fIcnidbackend\fR \-f\ \fIfromcode\fR \-t\ \fItocode\fR [\-m\ \fImaccode\fR] \fIvolumepath\fR
+.SH "DESCRIPTION"
+.PP
+\fBuniconv\fR
+converts the volume encoding of
+\fIvolumepath\fR
+from the
+\fIfromcode\fR
+to the
+\fItocode\fR
+encoding\&.
+.SH "OPTIONS"
+.PP
+\-c
+.RS 4
+CNID backend used on this volume, usually cdb or dbd\&. Should match the backend selected with afpd for this volume\&. If not specified, the default CNID backend "@DEFAULT_CNID_SCHEME@" is used
+.RE
+.PP
+\-d
+.RS 4
+don\*(Aqt HEX encode leading dots (:2e), equivalent to
+\fBuse dots = yes\fR
+in
+\fBafp.conf\fR(5)
+.RE
+.PP
+\-f
+.RS 4
+encoding to convert from, use ASCII for HEX encoded volumes
+.RE
+.PP
+\-h
+.RS 4
+display help
+.RE
+.PP
+\-m
+.RS 4
+Macintosh client codepage, required for HEX encoded volumes\&. Defaults to "MAC_ROMAN"
+.RE
+.PP
+\-n
+.RS 4
+"dry run", don\*(Aqt do any real changes
+.RE
+.PP
+\-t
+.RS 4
+volume encoding to convert to, e\&.g\&. UTF8
+.RE
+.PP
+\-v
+.RS 4
+verbose output, use twice for maximum logging\&.
+.RE
+.PP
+\-V
+.RS 4
+print version and exit
+.RE
+.PP
+.SH "WARNING"
+.PP
+Setting the wrong options might render your data unusable!!! Make sure you know what you are doing\&. Always backup your data first\&.
+.PP
+It is
+\fB*strongly*\fR
+recommended to do a "dry run" first and to check the output for conversion errors\&.
+.PP
+\fBafpd\fR(8)
+should
+\fInot\fR
+be running while you change the volume encoding\&. Remember to change
+\fBunix charset\fR
+or
+\fBvol charset\fR
+in
+\fBafp.conf\fR(5)
+to the new codepage, before restarting afpd\&.
+.PP
+In case of
+\fBMacChineseTraditional\fR,
+\fBMacJapanese\fR
+or
+\fBMacKorean\fR, uniconv cannot be used\&.
+.PP
+\fBUSE AT YOUR OWN RISK!!!\fR
+.SH "SELECTABLE CHARSETS"
+.PP
+Netatalk provides internal support for UTF\-8 (pre\- and decomposed) and HEX\&. If you want to use other charsets, they must be provided by
+\fBiconv\fR(1)
+.PP
+\fBuniconv\fR
+also knows iso\-8859\&.adapted, an old style 1\&.x NLS widely used\&. This is only intended for upgrading old volumes,
+\fBafpd\fR(8)
+cannot handle iso\-8859\&.adapted anymore\&.
+.SH "CNID BACKGROUND"
+.PP
+The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db, i\&.e\&. the DIDNAME index, gets inconsistent\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. I\&.e\&. if several names in the path to the file or directory have changed, things may go wrong\&.
+.PP
+If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\*(Aqt changed\&.
+\fBuniconv\fR
+therefore updates the CNID entry for each file/directory directly after it changes the name to avoid inconsistencies\&. The two supported backends for volumes, dbd and cdb, use the same CNID db format\&. Therefore, you
+\fIcould\fR
+use
+\fBuniconv\fR
+with cdb and
+\fBafpd\fR
+with dbd later\&.
+.PP
+\fBWarning\fR: There must not be two processes opening the CNID database using different backends at once! If a volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
+\fBuniconv\fR
+with cdb, the result will be a corrupted CNID database, as the two backends use different locking schemes\&. You might run into additional problems, e\&.g\&. if dbd is compiled with transactions, cdb will not update the transaction logs\&.
+.PP
+In general, it is recommended to use the same backend for
+\fBuniconv\fR
+you are using with
+\fBafpd\fR(8)\&.
+.SH "EXAMPLES"
+.PP
+convert 1\&.x CAP encoded volume to UTF\-8, clients used MacRoman codepage, cnidscheme is dbd:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-c dbd \-f ASCII \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert iso8859\-1 volume to UTF\-8, cnidscheme is cdb:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-c cdb \-f ISO\-8859\-1 \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert 1\&.x volume using iso8859\-1 adapted NLS to HEX encoding:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-f ISO\-8859\-ADAPTED \-t ASCII \-m MAC_ROMAN/path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert UTF\-8 volume to HEX, for MacCyrillic clients:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-f UTF8 \-t ASCII \-m MAC_CYRILLIC /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5),\fBafpd\fR(8),\fBiconv\fR(1),\fBcnid_metad\fR(8),\fBcnid_dbd\fR(8)
diff --git a/man/man1/uniconv.1.tmpl b/man/man1/uniconv.1.tmpl
deleted file mode 100644 (file)
index 729d35e..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-'\" t
-.\"     Title: uniconv
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "UNICONV" "1" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-uniconv \- convert Netatalk volume encoding
-.SH "SYNOPSIS"
-.HP \w'\fBuniconv\fR\fB\fR\ 'u
-\fBuniconv\fR\fB\fR [\-ndv] \-c\ \fIcnidbackend\fR \-f\ \fIfromcode\fR \-t\ \fItocode\fR [\-m\ \fImaccode\fR] \fIvolumepath\fR
-.SH "DESCRIPTION"
-.PP
-\fBuniconv\fR
-converts the volume encoding of
-\fIvolumepath\fR
-from the
-\fIfromcode\fR
-to the
-\fItocode\fR
-encoding\&.
-.SH "OPTIONS"
-.PP
-\-c
-.RS 4
-CNID backend used on this volume, usually cdb or dbd\&. Should match the backend selected with afpd for this volume\&. If not specified, the default CNID backend ":DEFAULT_CNID_SCHEME:" is used
-.RE
-.PP
-\-d
-.RS 4
-don\'t HEX encode leading dots (:2e), equivalent to
-\fBuse dots = yes\fR
-in
-\fBafp.conf\fR(5)
-.RE
-.PP
-\-f
-.RS 4
-encoding to convert from, use ASCII for HEX encoded volumes
-.RE
-.PP
-\-h
-.RS 4
-display help
-.RE
-.PP
-\-m
-.RS 4
-Macintosh client codepage, required for HEX encoded volumes\&. Defaults to "MAC_ROMAN"
-.RE
-.PP
-\-n
-.RS 4
-"dry run", don\'t do any real changes
-.RE
-.PP
-\-t
-.RS 4
-volume encoding to convert to, e\&.g\&. UTF8
-.RE
-.PP
-\-v
-.RS 4
-verbose output, use twice for maximum logging\&.
-.RE
-.PP
-\-V
-.RS 4
-print version and exit
-.RE
-.PP
-.SH "WARNING"
-.PP
-Setting the wrong options might render your data unusable!!! Make sure you know what you are doing\&. Always backup your data first\&.
-.PP
-It is
-\fB*strongly*\fR
-recommended to do a "dry run" first and to check the output for conversion errors\&.
-.PP
-\fBafpd\fR(8)
-should
-\fInot\fR
-be running while you change the volume encoding\&. Remember to change
-\fBvolcodepage\fR
-in
-\fBAppleVolumes.default\fR(5)
-to the new codepage, before restarting afpd\&.
-.PP
-In case of
-\fBMacChineseTraditional\fR,
-\fBMacJapanese\fR
-or
-\fBMacKorean\fR, uniconv cannot be used\&.
-.PP
-\fBUSE AT YOUR OWN RISK!!!\fR
-.SH "SELECTABLE CHARSETS"
-.PP
-Netatalk provides internal support for UTF\-8 (pre\- and decomposed) and HEX\&. If you want to use other charsets, they must be provided by
-\fBiconv\fR(1)
-.PP
-\fBuniconv\fR
-also knows iso\-8859\&.adapted, an old style 1\&.x NLS widely used\&. This is only intended for upgrading old volumes,
-\fBafpd\fR(8)
-cannot handle iso\-8859\&.adapted anymore\&.
-.SH "CNID BACKGROUND"
-.PP
-The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db, i\&.e\&. the DIDNAME index, gets inconsistent\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. I\&.e\&. if several names in the path to the file or directory have changed, things may go wrong\&.
-.PP
-If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\'t changed\&.
-\fBuniconv\fR
-therefore updates the CNID entry for each file/directory directly after it changes the name to avoid inconsistencies\&. The two supported backends for volumes, dbd and cdb, use the same CNID db format\&. Therefore, you
-\fIcould\fR
-use
-\fBuniconv\fR
-with cdb and
-\fBafpd\fR
-with dbd later\&.
-.PP
-\fBWarning\fR: There must not be two processes opening the CNID database using different backends at once! If a volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
-\fBuniconv\fR
-with cdb, the result will be a corrupted CNID database, as the two backends use different locking schemes\&. You might run into additional problems, e\&.g\&. if dbd is compiled with transactions, cdb will not update the transaction logs\&.
-.PP
-In general, it is recommended to use the same backend for
-\fBuniconv\fR
-you are using with
-\fBafpd\fR(8)\&.
-.SH "EXAMPLES"
-.PP
-convert 1\&.x CAP encoded volume to UTF\-8, clients used MacRoman codepage, cnidscheme is dbd:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-c dbd \-f ASCII \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert iso8859\-1 volume to UTF\-8, cnidscheme is cdb:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-c cdb \-f ISO\-8859\-1 \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert 1\&.x volume using iso8859\-1 adapted NLS to HEX encoding:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-f ISO\-8859\-ADAPTED \-t ASCII \-m MAC_ROMAN/path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert UTF\-8 volume to HEX, for MacCyrillic clients:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-f UTF8 \-t ASCII \-m MAC_CYRILLIC /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafp.conf\fR(5),\fBafpd\fR(8),\fBiconv\fR(1),\fBcnid_metad\fR(8),\fBcnid_dbd\fR(8)
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index ba10eeb069f64c047345d71684667225ae54657d..999046039f04554512659561961af530f7e05c69 100644 (file)
@@ -1,4 +1,3 @@
 Makefile
 Makefile.in
 *.5
-*.o
index fd2edf6ca7aa9b84d68dbd9844fdbb39687827fe..0453a03c3311f50aefdbbfdc339dd72d122f4fa8 100644 (file)
@@ -1,25 +1,9 @@
 # Makefile.am for man/man5/
 
-pkgconfdir = @PKGCONFDIR@
+man_MANS = \
+       afp.conf.5 \
+       afp_signature.conf.5 \
+       afp_voluuid.conf.5 \
+       extmap.conf.5
 
-SUFFIXES = .tmpl .
-
-.tmpl:
-       sed -e "s@:SBINDIR:@${sbindir}@g" \
-           -e "s@:BINDIR:@${bindir}@g" \
-           -e "s@:ETCDIR:@${pkgconfdir}@g" \
-           -e "s@:LIBDIR:@${libdir}@g" \
-           -e "s@:STATEDIR:@${localstatedir}@g" \
-           -e "s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@g" \
-           -e "s@:COMPILED_BACKENDS:@${compiled_backends}@g" \
-           <$< >$@
-
-GENERATED_MANS = afp.conf.5 afp_signature.conf.5 afp_voluuid.conf.5
-TEMPLATE_FILES = afp.conf.5.tmpl afp_signature.conf.5.tmpl afp_voluuid.conf.5.tmpl
-NONGENERATED_MANS =
-
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
-
-CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in
new file mode 100644 (file)
index 0000000..588911a
--- /dev/null
@@ -0,0 +1,1377 @@
+'\" t
+.\"     Title: afp.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 05 Jun 2014
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP\&.CONF" "5" "05 Jun 2014" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp.conf \- Netatalk configuration file
+.SH "SYNOPSIS"
+.PP
+The
+afp\&.conf
+file is the configuration file for the
+\fBNetatalk\fR
+AFP file server\&.
+.PP
+All AFP specific configuration and AFP volume definitions are done via this file\&.
+.SH "FILE FORMAT"
+.PP
+The file consists of sections and parameters\&. A section begins with the name of the section in square brackets and continues until the next section begins\&. Sections contain parameters of the form:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+    \fIname\fR = \fIvalue \fR
+    
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+The file is line\-based \- that is, each newline\-terminated line represents either a comment, a section name or a parameter\&.
+.PP
+Section and parameter names are case sensitive\&.
+.PP
+Only the first equals sign in a parameter is significant\&. Whitespace before or after the first equals sign is discarded\&. Leading, trailing and internal whitespace in section and parameter names is irrelevant\&. Leading and trailing whitespace in a parameter value is discarded\&. Internal whitespace within a parameter value is retained verbatim\&.
+.PP
+Any line beginning with a semicolon (\(lq;\(rq) or a hash (\(lq#\(rq) character is ignored, as are lines containing only whitespace\&.
+.PP
+Any line ending in a
+\(lq \e \(rq
+is continued on the next line in the customary UNIX fashion\&.
+.PP
+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, 1/0 or true/false\&. Case is not significant in boolean values, but is preserved in string values\&. Some items such as create masks are numeric\&.
+.PP
+The parameter
+\fBinclude = \fR\fB\fIpath\fR\fR
+allows you to include one config file inside another\&. The file is included literally, as though typed in place\&. Nested includes are not supported\&.
+.SH "SECTION DESCRIPTIONS"
+.PP
+Each section in the configuration file (except for the [Global] section) describes a shared resource (known as a
+\(lqvolume\(rq)\&. The section name is the name of the volume and the parameters within the section define the volume attributes and options\&.
+.PP
+There are two special sections, [Global] and [Homes], which are described under
+\fIspecial sections\fR\&. The following notes apply to ordinary section descriptions\&.
+.PP
+A volume consists of a directory to which access is being given plus a description of the access rights which are granted to the user of the service\&. For volumes the
+\fBpath\fR
+option must specify the directory to share\&.
+.PP
+Any volume section without
+\fBpath\fR
+option is considered a
+\fIvol preset\fR
+which can be selected in other volume sections via the
+\fBvol preset\fR
+option and constitutes defaults for the volume\&. For any option specified both in a preset
+\fIand\fR
+in a volume section the volume section setting completely substitutes the preset option\&.
+.PP
+The access rights granted by the server are masked by the access rights granted to the specified or guest UNIX user by the host system\&. The server does not grant more access than the host system grants\&.
+.PP
+The following sample section defines an AFP volume\&. The user has full access to the path
+/foo/bar\&. The share is accessed via the share name
+baz:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [baz]
+    path = /foo/bar 
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SPECIAL SECTIONS"
+.SS "The [Global] section"
+.PP
+Parameters in this section apply to the server as a whole\&. Parameters denoted by a (G) below are must be set in this section\&.
+.SS "The [Homes] section"
+.PP
+This section enable sharing of the UNIX server user home directories\&. Specifying an optional
+\fBpath\fR
+parameter means that not the whole user home will be shared but the subdirectory
+\fBpath\fR\&. It is necessary to define the
+\fBbasedir regex\fR
+option\&. It should be a regex which matches the parent directory of the user homes\&. Parameters denoted by a (H) belong to volume sections\&. The optional parameter
+\fBhome name\fR
+can be used to change the AFP volume name which
+\fI$u\*(Aqs home\fR
+by default\&. See below under VARIABLE SUBSTITUTIONS\&.
+.PP
+The following example illustrates this\&. Given all user home directories are stored under
+/home:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [Homes]
+      path = afp\-data
+      basedir regex = /home
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+For a user
+\fIjohn\fR
+this results in an AFP home volume with a path of
+/home/john/afp\-data\&.
+.PP
+If
+\fBbasedir regex\fR
+contains symlink, set the canonicalized absolute path\&. When
+/home
+links to
+/usr/home:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [Homes]
+      basedir regex = /usr/home
+.fi
+.if n \{\
+.RE
+.\}
+.SH "PARAMETERS"
+.PP
+Parameters define the specific attributes of sections\&.
+.PP
+Some parameters are specific to the [Global] section (e\&.g\&.,
+\fIlog type\fR)\&. All others are permissible only in volume sections\&. The letter
+\fIG\fR
+in parentheses indicates that a parameter is specific to the [Global] section\&. The letter
+\fIV\fR
+indicates that a parameter can be specified in a volume specific section\&.
+.SH "VARIABLE SUBSTITUTIONS"
+.PP
+You can use variables in volume names\&. The use of variables in paths is limited to $u\&.
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 1.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  1." 4.2
+.\}
+if you specify an unknown variable, it will not get converted\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 2.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  2." 4.2
+.\}
+if you specify a known variable, but that variable doesn\*(Aqt have a value, it will get ignored\&.
+.RE
+.PP
+The variables which can be used for substitutions are:
+.PP
+$b
+.RS 4
+basename
+.RE
+.PP
+$c
+.RS 4
+client\*(Aqs ip address
+.RE
+.PP
+$d
+.RS 4
+volume pathname on server
+.RE
+.PP
+$f
+.RS 4
+full name (contents of the gecos field in the passwd file)
+.RE
+.PP
+$g
+.RS 4
+group name
+.RE
+.PP
+$h
+.RS 4
+hostname
+.RE
+.PP
+$i
+.RS 4
+client\*(Aqs ip, without port
+.RE
+.PP
+$s
+.RS 4
+server name (this can be the hostname)
+.RE
+.PP
+$u
+.RS 4
+user name (if guest, it is the user that guest is running as)
+.RE
+.PP
+$v
+.RS 4
+volume name
+.RE
+.PP
+$$
+.RS 4
+prints dollar sign ($)
+.RE
+.SH "EXPLANATION OF GLOBAL PARAMETERS"
+.SS "Authentication Options"
+.PP
+ad domain = \fIDOMAIN\fR \fB(G)\fR
+.RS 4
+Append @DOMAIN to username when authenticating\&. Useful in Active Directory environments that otherwise would require the user to enter the full user@domain string\&.
+.RE
+.PP
+admin auth user = \fIuser\fR \fB(G)\fR
+.RS 4
+Specifying eg "\fBadmin auth user = root\fR" whenever a normal user login fails, afpd will try to authenticate as the specified
+\fBadmin auth user\fR\&. If this succeeds, a normal session is created for the original connecting user\&. Said differently: if you know the password of
+\fBadmin auth user\fR, you can authenticate as any other user\&.
+.RE
+.PP
+k5 keytab = \fIpath\fR \fB(G)\fR, k5 service = \fIservice\fR \fB(G)\fR, k5 realm = \fIrealm\fR \fB(G)\fR
+.RS 4
+These are required if the server supports the Kerberos 5 authentication UAM\&.
+.RE
+.PP
+nt domain = \fIDOMAIN\fR \fB(G)\fR, nt separator = \fISEPARATOR\fR \fB(G)\fR
+.RS 4
+Use for eg\&. winbind authentication, prepends both strings before the username from login and then tries to authenticate with the result through the available and active UAM authentication modules\&.
+.RE
+.PP
+save password = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Enables or disables the ability of clients to save passwords locally\&.
+.RE
+.PP
+set password = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Enables or disables the ability of clients to change their passwords via chooser or the "connect to server" dialog\&.
+.RE
+.PP
+uam list = \fIuam list\fR \fB(G)\fR
+.RS 4
+Space or comma separated list of UAMs\&. (The default is "uams_dhx\&.so uams_dhx2\&.so")\&.
+.sp
+The most commonly used UAMs are:
+.PP
+uams_guest\&.so
+.RS 4
+allows guest logins
+.RE
+.PP
+uams_clrtxt\&.so
+.RS 4
+(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&. (legacy)
+.RE
+.PP
+uams_randum\&.so
+.RS 4
+allows Random Number and Two\-Way Random Number Exchange for authentication (requires a separate file containing the passwords, either @pkgconfdir@/afppasswd file or the one specified via "\fBpasswd file\fR"\&. See
+\fBafppasswd\fR(1)
+for details\&. (legacy)
+.RE
+.PP
+uams_dhx\&.so
+.RS 4
+(uams_dhx_pam\&.so or uams_dhx_passwd\&.so) Allow Diffie\-Hellman eXchange (DHX) for authentication\&.
+.RE
+.PP
+uams_dhx2\&.so
+.RS 4
+(uams_dhx2_pam\&.so or uams_dhx2_passwd\&.so) Allow Diffie\-Hellman eXchange 2 (DHX2) for authentication\&.
+.RE
+.PP
+uam_gss\&.so
+.RS 4
+Allow Kerberos V for authentication (optional)
+.RE
+.RE
+.PP
+uam path = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the default path for UAMs for this server (default is @libdir@/netatalk)\&.
+.RE
+.SS "Charset Options"
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the big changes was, that AFP3 uses Unicode names encoded as Decomposed UTF\-8 (UTF8\-MAC)\&. Previous AFP/OS versions used charsets like MacRoman, MacCentralEurope, etc\&.
+.PP
+To be able to serve AFP3 and older clients at the same time,
+\fBafpd\fR
+needs to be able to convert between UTF\-8 and Mac charsets\&. Even OS X clients partly still rely on the mac charset\&. As there\*(Aqs no way,
+\fBafpd\fR
+can detect the codepage a pre AFP3 client uses, you have to specify it using the
+\fBmac charset\fR
+option\&. The default is MacRoman, which should be fine for most western users\&.
+.PP
+As
+\fBafpd\fR
+needs to interact with UNIX operating system as well, it need\*(Aqs to be able to convert from UTF8\-MAC / Mac charset to the UNIX charset\&. By default
+\fBafpd\fR
+uses
+\fIUTF8\fR\&. You can set the UNIX charset using the
+\fBunix charset\fR
+option\&. If you\*(Aqre using extended characters in the configuration files for
+\fBafpd\fR, make sure your terminal matches the
+\fBunix charset\fR\&.
+.PP
+mac charset = \fICHARSET\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the Mac clients charset, e\&.g\&.
+\fIMAC_ROMAN\fR\&. This is used to convert strings and filenames to the clients codepage for OS9 and Classic, i\&.e\&. for authentication and AFP messages (SIGUSR2 messaging)\&. This will also be the default for the volumes
+\fBmac charset\fR\&. Defaults to
+\fIMAC_ROMAN\fR\&.
+.RE
+.PP
+unix charset = \fICHARSET\fR \fB(G)\fR
+.RS 4
+Specifies the servers unix charset, e\&.g\&.
+\fIISO\-8859\-15\fR
+or
+\fIEUC\-JP\fR\&. This is used to convert strings to/from the systems locale, e\&.g\&. for authentication, server messages and volume names\&. If
+\fILOCALE\fR
+is set, the systems locale is used\&. Defaults to
+\fIUTF8\fR\&.
+.RE
+.PP
+vol charset = \fICHARSET\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the encoding of the volumes filesystem\&. By default, it is the same as
+\fBunix charset\fR\&.
+.RE
+.SS "Password Options"
+.PP
+passwd file = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the path to the Randnum UAM passwd file for this server (default is @pkgconfdir@/afppasswd)\&.
+.RE
+.PP
+passwd minlen = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the minimum password length, if supported by the UAM
+.RE
+.SS "Network Options"
+.PP
+advertise ssh = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Allows old Mac OS X clients (10\&.3\&.3\-10\&.4) to automagically establish a tunneled AFP connection through SSH\&. If this option is set, the server\*(Aqs answers to client\*(Aqs FPGetSrvrInfo requests contain an additional entry\&. It depends on both client\*(Aqs settings and a correctly configured and running
+\fBsshd\fR(8)
+on the server to let things work\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+Setting this option is not recommended since globally encrypting AFP connections via SSH will increase the server\*(Aqs load significantly\&. On the other hand, Apple\*(Aqs client side implementation of this feature in MacOS X versions prior to 10\&.3\&.4 contained a security flaw\&.
+.sp .5v
+.RE
+.RE
+.PP
+afp interfaces = \fIname [name \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the network interfaces that the server should listens on\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&.
+.RE
+.PP
+afp listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the IP address that the server should advertise
+\fBand\fR
+listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.sp
+IPv6 address + port combination must use URL the format using square brackets [IPv6]:port
+.RE
+.PP
+afp port = \fIport number\fR \fB(G)\fR
+.RS 4
+Allows a different TCP port to be used for AFP\&. The default is 548\&. Also sets the default port applied when none specified in an
+\fBafp listen\fR
+option\&.
+.RE
+.PP
+cnid listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the IP address that the CNID server should listen on\&. The default is
+\fBlocalhost:4700\fR\&.
+.RE
+.PP
+disconnect time = \fInumber\fR \fB(G)\fR
+.RS 4
+Keep disconnected AFP sessions for
+\fInumber\fR
+hours before dropping them\&. Default is 24 hours\&.
+.RE
+.PP
+dsireadbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Scale factor that determines the size of the DSI/TCP readahead buffer, default is 12\&. This is multiplies with the DSI server quantum (default ~300k) to give the size of the buffer\&. Increasing this value might increase throughput in fast local networks for volume to volume copies\&.
+\fINote\fR: This buffer is allocated per afpd child process, so specifying large values will eat up large amount of memory (buffer size * number of clients)\&.
+.RE
+.PP
+fqdn = \fIname:port\fR \fB(G)\fR
+.RS 4
+Specifies a fully\-qualified domain name, with an optional port\&. This is discarded if the server cannot resolve it\&. This option is not honored by AppleShare clients <= 3\&.8\&.3\&. This option is disabled by default\&. Use with caution as this will involve a second name resolution step on the client side\&. Also note that afpd will advertise this name:port combination but not automatically listen to it\&.
+.RE
+.PP
+hostname = \fIname\fR \fB(G)\fR
+.RS 4
+Use this instead of the result from calling hostname for determining which IP address to advertise, therefore the hostname is resolved to an IP which is the advertised\&. This is NOT used for listening and it is also overwritten by
+\fBafp listen\fR\&.
+.RE
+.PP
+max connections = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the maximum number of clients that can simultaneously connect to the server (default is 200)\&.
+.RE
+.PP
+server quantum = \fInumber\fR \fB(G)\fR
+.RS 4
+This specifies the DSI server quantum\&. The default value is 0x100000 (1 MiB)\&. The maximum value is 0xFFFFFFFFF, the minimum is 32000\&. If you specify a value that is out of range, the default value will be set\&. Do not change this value unless you\*(Aqre absolutely sure, what you\*(Aqre doing
+.RE
+.PP
+sleep time = \fInumber\fR \fB(G)\fR
+.RS 4
+Keep sleeping AFP sessions for
+\fInumber\fR
+hours before disconnecting clients in sleep mode\&. Default is 10 hours\&.
+.RE
+.PP
+tcprcvbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Try to set TCP receive buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+tcpsndbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Try to set TCP send buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+recvfile = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to use splice() on Linux for receiving data\&.
+.RE
+.PP
+splice size = \fInumber\fR (default: \fI64k\fR) \fB(G)\fR
+.RS 4
+Maximum number of bytes spliced\&.
+.RE
+.PP
+use sendfile = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to use sendfile
+syscall for sending file data to clients\&.
+.RE
+.PP
+zeroconf = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to use automatic Zeroconf
+service registration if Avahi or mDNSResponder were compiled in\&.
+.RE
+.SS "Miscellaneous Options"
+.PP
+admin group = \fIgroup\fR \fB(G)\fR
+.RS 4
+Allows users of a certain group to be seen as the superuser when they log in\&. This option is disabled by default\&.
+.RE
+.PP
+afp read locks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to apply locks to the byte region read in FPRead calls\&. The AFP spec mandates this, but it\*(Aqs not really in line with UNIX semantics and is a performance hug\&.
+.RE
+.PP
+afpstats = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to provide AFP runtime statistics (connected users, open volumes) via dbus\&.
+.RE
+.PP
+basedir regex = \fIregex\fR \fB(H)\fR
+.RS 4
+Regular expression which matches the parent directory of the user homes\&. If
+\fBbasedir regex\fR
+contains symlink, you must set the canonicalized absolute path\&. In the simple case this is just a path ie
+\fBbasedir regex = /home\fR
+.RE
+.PP
+chmod request = \fIpreserve (default) | ignore | simple\fR \fB(G/V)\fR
+.RS 4
+Advanced permission control that deals with ACLs\&.
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+
+\fBignore\fR
+\- UNIX chmod() requests are completely ignored
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+
+\fBpreserve\fR
+\- preserve ZFS ACEs for named users and groups or POSIX ACL group mask
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+
+\fBsimple\fR
+\- just to a chmod() as requested without any extra steps
+.RE
+.RE
+.PP
+close vol = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to close volumes possibly opened by clients when they\*(Aqre removed from the configuration and the configuration is reloaded\&.
+.RE
+.PP
+cnid mysql host = \fIMySQL server address\fR \fB(G)\fR
+.RS 4
+name or address of a MySQL server for use with the mysql CNID backend\&.
+.RE
+.PP
+cnid mysql user = \fIMySQL user\fR \fB(G)\fR
+.RS 4
+MySQL user for authentication with the server\&.
+.RE
+.PP
+cnid mysql pw = \fIpassword\fR \fB(G)\fR
+.RS 4
+Password for MySQL server\&.
+.RE
+.PP
+cnid mysql db = \fIdatabase name\fR \fB(G)\fR
+.RS 4
+Name of an existing database for which the specified user has full privileges\&.
+.RE
+.PP
+cnid server = \fIipaddress[:port]\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
+.RE
+.PP
+dbus daemon = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the path to dbus\-daemon binary used by Spotlight feature\&. The default is
+/bin/dbus\-daemon\&.
+.RE
+.PP
+dircachesize = \fInumber\fR \fB(G)\fR
+.RS 4
+Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
+.sp
+Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
+.RE
+.PP
+extmap file = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the path to the file which defines file extension type/creator mappings\&. (default is @pkgconfdir@/extmap\&.conf)\&.
+.RE
+.PP
+guest account = \fIname\fR \fB(G)\fR
+.RS 4
+Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.
+.RE
+.PP
+home name = \fIname\fR \fB(H)\fR
+.RS 4
+AFP user home volume name\&. The default is
+\fIuser\*(Aqs home\fR\&.
+.RE
+.PP
+ignored attributes = \fIall | nowrite | nodelete | norename\fR \fB(G)/(V)\fR
+.RS 4
+Speficy a set of file and directory attributes that shall be ignored by the server,
+\fBall\fR
+includes all the other options\&.
+.sp
+In OS X when the Finder sets a lock on a file/directory or you set the BSD uchg flag in the Terminal, all three attributes are used\&. Thus in order to ignore the Finder lock/BSD uchg flag, add set
+\fIignored attributes = all\fR\&.
+.RE
+.PP
+login message = \fImessage\fR \fB(G)/(V)\fR
+.RS 4
+Sets a message to be displayed when clients logon to the server\&. The message should be in
+\fBunix charset\fR
+and should be quoted\&. Extended characters are allowed\&.
+.RE
+.PP
+mimic model = \fImodel\fR \fB(G)\fR
+.RS 4
+Specifies the icon model that appears on clients\&. Defaults to off\&. Note that afpd must support Zeroconf\&. Examples: RackMac (same as Xserve), PowerBook, PowerMac, Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro, AppleTV1,1, AirPort\&.
+.RE
+.PP
+signature = <text> \fB(G)\fR
+.RS 4
+Specify a server signature\&. The maximum length is 16 characters\&. This option is useful for clustered environments, to provide fault isolation etc\&. By default, afpd generate signature and saving it to
+@localstatedir@/netatalk/afp_signature\&.conf
+automatically (based on random number)\&. See also asip\-status\&.pl(1)\&.
+.RE
+.PP
+solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
+.RE
+.PP
+sparql results limit = \fINUMBER\fR (default: \fIUNLIMITED\fR) \fB(G)\fR
+.RS 4
+Impose a limit on the number of results queried from Tracker via SPARQL queries\&.
+.RE
+.PP
+spotlight = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)/(V)\fR
+.RS 4
+Whether to enable Spotlight searches\&. Note: once the global option is enabled, any volume that is not enabled won\*(Aqt be searchable at all\&. See also
+\fIdbus daemon\fR
+option\&.
+.RE
+.PP
+spotlight attributes = \fICOMMA SEPERATED STRING\fR (default: \fIEMPTY\fR) \fB(G)\fR
+.RS 4
+A list of attributes that are allowed to be used in Spotlight searches\&. By default all attributes can be searched, passing a string limits attributes to elements of the string\&. Example:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+spotlight
+            attributes = *,kMDItemTextContent
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.RE
+.PP
+spotlight expr = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to allow the use of logic expression in searches\&.
+.RE
+.PP
+start dbus = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to start a dbus instance for use with Tracker\&.
+.RE
+.PP
+start tracker = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to start Tracker with
+\fItracker\-control \-s\fR\&.
+.RE
+.PP
+veto message = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Send optional AFP messages for vetoed files\&. Then whenever a client tries to access any file or directory with a vetoed name, it will be sent an AFP message indicating the name and the directory\&.
+.RE
+.PP
+vol dbpath = \fIpath\fR \fB(G)/(V)\fR
+.RS 4
+Sets the database information to be stored in path\&. You have to specify a writable location, even if the volume is read only\&. The default is
+@localstatedir@/netatalk/CNID/$v/\&.
+.RE
+.PP
+vol dbnest = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Setting this option to true brings back Netatalk 2 behaviour of storing the CNID database in a folder called \&.AppleDB inside the volume root of each share\&.
+.RE
+.PP
+volnamelen = \fInumber\fR \fB(G)\fR
+.RS 4
+Max length of UTF8\-MAC volume name for Mac OS X\&. Note that Hangul is especially sensitive to this\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ 73: limit of Mac OS X 10\&.1 80: limit of Mac
+            OS X 10\&.4/10\&.5 (default) 255: limit of recent Mac OS
+            X
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Mac OS 9 and earlier are not influenced by this, because Maccharset volume name is always limited to 27 bytes\&.
+.RE
+.PP
+vol preset = \fIname\fR \fB(G)/(V)\fR
+.RS 4
+Use section
+\fBname\fR
+as option preset for all volumes (when set in the [Global] section) or for one volume (when set in that volume\*(Aqs section)\&.
+.RE
+.SS "Logging Options"
+.PP
+log file = \fIlogfile\fR \fB(G)\fR
+.RS 4
+If not specified Netatalk logs to syslogs daemon facility\&. Otherwise it logs to
+\fBlogfile\fR\&.
+.RE
+.PP
+log level = \fItype:level [type:level \&.\&.\&.]\fR \fB(G)\fR, log level = \fItype:level,[type:level, \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specify that any message of a loglevel up to the given
+\fBlog level\fR
+should be logged\&.
+.sp
+By default afpd logs to syslog with a default logging setup equivalent to
+\fBdefault:note\fR
+.sp
+logtypes: default, afpdaemon, logger, uamsdaemon
+.sp
+loglevels: severe, error, warn, note, info, debug, debug6, debug7, debug8, debug9, maxdebug
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+Both logtype and loglevels are case insensitive\&.
+.sp .5v
+.RE
+.RE
+.SS "Filesystem Change Events (FCE)"
+.PP
+Netatalk includes a nifty filesystem change event mechanism where afpd processes notify interested listeners about certain filesystem event by UDP network datagrams\&.
+.PP
+fce listener = \fIhost[:port]\fR \fB(G)\fR
+.RS 4
+Enables sending FCE events to the specified
+\fIhost\fR, default
+\fIport\fR
+is 12250 if not specified\&. Specifying multiple listeners is done by having this option once for each of them\&.
+.RE
+.PP
+fce events = \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR \fB(G)\fR
+.RS 4
+Specifies which FCE events are active, default is
+\fIfmod,fdel,ddel,fcre,dcre\fR\&.
+.RE
+.PP
+fce coalesce = \fIall|delete|create\fR \fB(G)\fR
+.RS 4
+Coalesce FCE events\&.
+.RE
+.PP
+fce holdfmod = \fIseconds\fR \fB(G)\fR
+.RS 4
+This determines the time delay in seconds which is always waited if another file modification for the same file is done by a client before sending an FCE file modification event (fmod)\&. For example saving a file in Photoshop would generate multiple events by itself because the application is opening, modifying and closing a file multiple times for every "save"\&. Default: 60 seconds\&.
+.RE
+.SS "Debug Parameters"
+.PP
+These options are useful for debugging only\&.
+.PP
+tickleval = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the tickle timeout interval (in seconds)\&. Defaults to 30\&.
+.RE
+.PP
+timeout = \fInumber\fR \fB(G)\fR
+.RS 4
+Specify the number of tickles to send before timing out a connection\&. The default is 4, therefore a connection will timeout after 2 minutes\&.
+.RE
+.PP
+client polling = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+With this option enabled, afpd won\*(Aqt advertise that it is capable of server notifications, so that connected clients poll the server every 10 seconds to detect changes in opened server windows\&.
+\fINote\fR: Depending on the number of simultaneously connected clients and the network\*(Aqs speed, this can lead to a significant higher load on your network!
+.sp
+Do not use this option any longer as present Netatalk correctly supports server notifications, allowing connected clients to update folder listings in case another client changed the contents\&.
+.RE
+.SS "Options for ACL handling"
+.PP
+By default, the effective permission of the authenticated user are only mapped to the mentioned UARights permission structure, not the UNIX mode\&. You can adjust this behaviour with the configuration option
+\fBmac acls\fR:
+.PP
+map acls = \fInone|rights|mode\fR \fB(G)\fR
+.RS 4
+.PP
+none
+.RS 4
+no mapping of ACLs
+.RE
+.PP
+rights
+.RS 4
+effective permissions are mapped to UARights structure\&. This is the default\&.
+.RE
+.PP
+mode
+.RS 4
+ACLs are additionally mapped to the UNIX mode of the filesystem object\&.
+.RE
+.RE
+.PP
+If you want to be able to display ACLs on the client, you must setup both client and server as part on a authentication domain (directory service, eg LDAP, Open Directory, Active Directory)\&. The reason is, in OS X ACLs are bound to UUIDs, not just uid\*(Aqs or gid\*(Aqs\&. Therefor Netatalk must be able to map every filesystem uid and gid to a UUID so that it can return the server side ACLs which are bound to UNIX uid and gid mapped to OS X UUIDs\&.
+.PP
+Netatalk can query a directory server using LDAP queries\&. Either the directory server already provides an UUID attribute for user and groups (Active Directory, Open Directory) or you reuse an unused attribute (or add a new one) to you directory server (eg OpenLDAP)\&.
+.PP
+The following LDAP options must be configured for Netatalk:
+.PP
+ldap auth method = \fInone|simple|sasl\fR \fB(G)\fR
+.RS 4
+Authentication method:
+\fBnone | simple | sasl\fR
+.PP
+none
+.RS 4
+anonymous LDAP bind
+.RE
+.PP
+simple
+.RS 4
+simple LDAP bind
+.RE
+.PP
+sasl
+.RS 4
+SASL\&. Not yet supported !
+.RE
+.RE
+.PP
+ldap auth dn = \fIdn\fR \fB(G)\fR
+.RS 4
+Distinguished Name of the user for simple bind\&.
+.RE
+.PP
+ldap auth pw = \fIpassword\fR \fB(G)\fR
+.RS 4
+Distinguished Name of the user for simple bind\&.
+.RE
+.PP
+ldap server = \fIhost\fR \fB(G)\fR
+.RS 4
+Name or IP address of your LDAP Server\&. This is only needed for explicit ACL support in order to be able to query LDAP for UUIDs\&.
+.sp
+You can use
+\fBafpldaptest\fR(1)
+to syntactically check your config\&.
+.RE
+.PP
+ldap userbase = \fIbase dn\fR \fB(G)\fR
+.RS 4
+DN of the user container in LDAP\&.
+.RE
+.PP
+ldap userscope = \fIscope\fR \fB(G)\fR
+.RS 4
+Search scope for user search:
+\fBbase | one | sub\fR
+.RE
+.PP
+ldap groupbase = \fIbase dn\fR \fB(G)\fR
+.RS 4
+DN of the group container in LDAP\&.
+.RE
+.PP
+ldap groupscope = \fIscope\fR \fB(G)\fR
+.RS 4
+Search scope for user search:
+\fBbase | one | sub\fR
+.RE
+.PP
+ldap uuid attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the UUIDs\&.
+.sp
+Note: this is used both for users and groups\&.
+.RE
+.PP
+ldap name attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the users short name\&.
+.RE
+.PP
+ldap group attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the groups short name\&.
+.RE
+.PP
+ldap uuid string = \fISTRING\fR \fB(G)\fR
+.RS 4
+Format of the uuid string in the directory\&. A series of x and \-, where every x denotes a value 0\-9a\-f and every \- is a separator\&.
+.sp
+Default: xxxxxxxx\-xxxx\-xxxx\-xxxx\-xxxxxxxxxxxx
+.RE
+.PP
+ldap uuid encoding = \fIstring | ms\-guid (default: string)\fR \fB(G)\fR
+.RS 4
+Format of the UUID of the LDAP attribute, allows usage of the binary objectGUID fields from Active Directory\&. If left unspecified, string is the default, which passes through the ASCII UUID returned by most other LDAP stores\&. If set to ms\-guid, the internal UUID representation is converted to and from the binary format used in the objectGUID attribute found on objects in Active Directory when interacting with the server\&.
+.sp
+See also the options
+\fBldap user filter\fR
+and
+\fBldap group filter\fR\&.
+.PP
+string
+.RS 4
+UUID is a string, use with eg OpenDirectory\&.
+.RE
+.PP
+ms\-guid
+.RS 4
+Binary objectGUID from Active Directory
+.RE
+.RE
+.PP
+ldap user filter = \fISTRING (default: unused)\fR \fB(G)\fR
+.RS 4
+Optional LDAP filter that matches user objects\&. This is necessary for Active Directory environments where users and groups are stored in the same directory subtree\&.
+.sp
+Recommended setting for Active Directory:
+\fIobjectClass=user\fR\&.
+.RE
+.PP
+ldap group filter = \fISTRING (default: unused)\fR \fB(G)\fR
+.RS 4
+Optional LDAP filter that matches group objects\&. This is necessary for Active Directory environments where users and groups are stored in the same directory subtree\&.
+.sp
+Recommended setting for Active Directory:
+\fIobjectClass=group\fR\&.
+.RE
+.SH "EXPLANATION OF VOLUME PARAMETERS"
+.SS "Parameters"
+.PP
+The section name defines the volume name\&. No two volumes may have the same name\&. The volume name cannot contain the
+\*(Aq:\*(Aq
+character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
+.PP
+path = \fIPATH\fR \fB(V)\fR
+.RS 4
+The path name must be a fully qualified path name\&.
+.RE
+.PP
+appledouble = \fIea|v2\fR \fB(V)\fR
+.RS 4
+Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble v2, the new default format is
+\fBea\fR\&.
+.RE
+.PP
+vol size limit = \fIsize in MiB\fR \fB(V)\fR
+.RS 4
+Useful for Time Machine: limits the reported volume size, thus preventing Time Machine from using the whole real disk space for backup\&. Example: "vol size limit = 1000" would limit the reported disk space to 1 GB\&.
+\fBIMPORTANT: \fR
+This is an approximated calculation taking into account the contents of Time Machine sparsebundle images\&. Therefor you MUST NOT use this volume to store other content when using this option, because it would NOT be accounted\&. The calculation works by reading the band size from the Info\&.plist XML file of the sparsebundle, reading the bands/ directory counting the number of band files, and then multiplying one with the other\&.
+.RE
+.PP
+valid users = \fIuser @group\fR \fB(V)\fR
+.RS 4
+The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by spaces or commas\&. Groups are designated by a @ prefix\&. Names may be quoted in order to allow for spaces in names\&. Example:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+valid users = user "user 2" @group \(lq@group 2"
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
+invalid users = \fIusers/groups\fR \fB(V)\fR
+.RS 4
+The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the "valid users" option\&.
+.RE
+.PP
+hosts allow = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
+.RS 4
+Only listed hosts and networks are allowed, all others are rejected\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.sp
+Example: hosts allow = 10\&.1\&.0\&.0/16 10\&.2\&.1\&.100 2001:0db8:1234::/48
+.RE
+.PP
+hosts deny = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
+.RS 4
+Listed hosts and nets are rejected, all others are allowed\&.
+.sp
+Example: hosts deny = 192\&.168\&.100/24 10\&.1\&.1\&.1 2001:db8::1428:57ab
+.RE
+.PP
+cnid scheme = \fIbackend\fR \fB(V)\fR
+.RS 4
+set the CNID backend to be used for the volume, default is [@DEFAULT_CNID_SCHEME@] available schemes: [@compiled_backends@]
+.RE
+.PP
+ea = \fInone|auto|sys|ad\fR \fB(V)\fR
+.RS 4
+Specify how Extended Attributes
+are stored\&.
+\fBauto\fR
+is the default\&.
+.PP
+auto
+.RS 4
+Try
+\fBsys\fR
+(by setting an EA on the shared directory itself), fallback to
+\fBad\fR\&. Requires writable volume for performing test\&. "\fBread only = yes\fR" overwrites
+\fBauto\fR
+with
+\fBnone\fR\&. Use explicit "\fBea = sys|ad\fR" for read\-only volumes where appropriate\&.
+.RE
+.PP
+sys
+.RS 4
+Use filesystem Extended Attributes\&.
+.RE
+.PP
+ad
+.RS 4
+Use files in
+\fI\&.AppleDouble\fR
+directories\&.
+.RE
+.PP
+none
+.RS 4
+No Extended Attributes support\&.
+.RE
+.RE
+.PP
+mac charset = \fICHARSET\fR \fB(V)\fR
+.RS 4
+specifies the Mac client charset for this Volume, e\&.g\&.
+\fIMAC_ROMAN\fR,
+\fIMAC_CYRILLIC\fR\&. If not specified the global setting is applied\&. This setting is only required if you need volumes, where the Mac charset differs from the one globally set in the [Global] section\&.
+.RE
+.PP
+casefold = \fBoption\fR \fB(V)\fR
+.RS 4
+The casefold option handles, if the case of filenames should be changed\&. The available options are:
+.sp
+\fBtolower\fR
+\- Lowercases names in both directions\&.
+.sp
+\fBtoupper\fR
+\- Uppercases names in both directions\&.
+.sp
+\fBxlatelower\fR
+\- Client sees lowercase, server sees uppercase\&.
+.sp
+\fBxlateupper\fR
+\- Client sees uppercase, server sees lowercase\&.
+.RE
+.PP
+password = \fIpassword\fR \fB(V)\fR
+.RS 4
+This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
+.RE
+.PP
+file perm = \fImode\fR \fB(V)\fR, directory perm = \fImode\fR \fB(V)\fR
+.RS 4
+Add(or) with the client requested permissions:
+\fBfile perm\fR
+is for files only,
+\fBdirectory perm\fR
+is for directories only\&. Don\*(Aqt use with "\fBunix priv = no\fR"\&.
+.PP
+\fBExample.\ \&Volume for a collaborative workgroup\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+file perm = 0660 directory perm =
+              0770
+.fi
+.if n \{\
+.RE
+.\}
+
+.RE
+.PP
+umask = \fImode\fR \fB(V)\fR
+.RS 4
+set perm mask\&. Don\*(Aqt use with "\fBunix priv = no\fR"\&.
+.RE
+.PP
+preexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
+postexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run when the volume is closed, ignored for user defined volumes
+.RE
+.PP
+root preexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run as root when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
+root postexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run as root when the volume is closed, ignored for user defined volumes
+.RE
+.PP
+rolist = \fBusers/groups\fR \fB(V)\fR
+.RS 4
+Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
+.RE
+.PP
+rwlist = \fIusers/groups\fR \fB(V)\fR
+.RS 4
+Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
+.RE
+.PP
+veto files = \fIvetoed names\fR \fB(V)\fR
+.RS 4
+hide files and directories,where the path matches one of the \*(Aq/\*(Aq delimited vetoed names\&. The veto string must always be terminated with a \*(Aq/\*(Aq, eg\&. "veto files = veto1/", "veto files = veto1/veto2/"\&.
+.RE
+.SS "Volume options"
+.PP
+Boolean volume options\&.
+.PP
+acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to flag volumes as supporting ACLs\&. If ACL support is compiled in, this is yes by default\&.
+.RE
+.PP
+cnid dev = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to use the device number in the CNID backends\&. Helps when the device number is not constant across a reboot, eg cluster, \&.\&.\&.
+.RE
+.PP
+convert appledouble = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether automatic conversion from
+\fBappledouble = v2\fR
+to
+\fBappledouble = ea\fR
+is performed when accessing filesystems from clients\&. This is generally useful, but costs some performance\&. It\*(Aqs recommendable to run
+\fBdbd\fR
+on volumes and do the conversion with that\&. Then this option can be set to no\&.
+.RE
+.PP
+delete veto files = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+This option is used when Netatalk is attempting to delete a directory that contains one or more vetoed files or directories (see the veto files option)\&. If this option is set to no (the default) then if a directory contains any non\-vetoed files or directories then the directory delete will fail\&. This is usually what you want\&.
+.sp
+If this option is set to yes, then Netatalk will attempt to recursively delete any files and directories within the vetoed directory\&.
+.RE
+.PP
+follow symlinks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+The default setting is false thus symlinks are not followed on the server\&. This is the same behaviour as OS X\*(Aqs AFP server\&. Setting the option to true causes afpd to follow symlinks on the server\&. symlinks may point outside of the AFP volume, currently afpd doesn\*(Aqt do any checks for "wide symlinks"\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+This option will subtly break when the symlinks point across filesystem boundaries\&.
+.sp .5v
+.RE
+.RE
+.PP
+invisible dots = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+make dot files invisible\&. WARNING: enabling this option will lead to unwanted sideeffects were OS X applications when saving files to a temporary file starting with a dot first, then renaming the temp file to its final name, result in the saved file being invisible\&. The only thing this option is useful for is making files that start with a dot invisible on Mac OS 9\&. It\*(Aqs completely useless on Mac OS X, as both in Finder and in Terminal files starting with a dot are hidden anyway\&.
+.RE
+.PP
+network ids = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether the server support network ids\&. Setting this to
+\fIno\fR
+will result in the client not using ACL AFP functions\&.
+.RE
+.PP
+preexec close = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+A non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+read only = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Specifies the share as being read only for all users\&. Overwrites
+\fBea = auto\fR
+with
+\fBea = none\fR
+.RE
+.PP
+root preexec close= \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+A non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+search db = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
+.RE
+.PP
+stat vol = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
+.RE
+.PP
+time machine = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Whether to enable Time Machine support for this volume\&.
+.RE
+.PP
+unix priv = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to use AFP3 UNIX privileges\&. This should be set for OS X clients\&. See also:
+\fBfile perm\fR,
+\fBdirectory perm\fR
+and
+\fBumask\fR\&.
+.RE
+.SH "CNID BACKENDS"
+.PP
+The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\*(Aqs in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
+@localstatedir@/netatalk/CNID/(volumename)/\&.AppleDB/
+directory\&.
+.PP
+cdb
+.RS 4
+"Concurrent database", backend is based on Oracle Berkley DB\&. With this backend several
+\fBafpd\fR
+daemons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
+\fBafpd\fR
+process is active for a volume\&. The drawback is, that the crash of a single
+\fBafpd\fR
+process might corrupt the database\&.
+.RE
+.PP
+dbd
+.RS 4
+Access to the CNID database is restricted to the
+\fBcnid_metad\fR
+daemon process\&.
+\fBafpd\fR
+processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
+\fBcdb\fR
+.RE
+.PP
+last
+.RS 4
+This backend is an exception, in terms of ID persistency\&. ID\*(Aqs are only valid for the current session\&. This is basically what
+\fBafpd\fR
+did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&. Starting with Netatalk 3\&.0, it becomes the
+\fIread only mode\fR
+automatically\&.
+.sp
+\fBWarning\fR: It is
+\fINOT\fR
+recommended to use this backend for volumes anymore, as
+\fBafpd\fR
+now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
+.RE
+.PP
+Even though
+\fB\&./configure \-\-help\fR
+might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\*(Aqt use them unless you know what you\*(Aqre doing, they may be removed without further notice from future versions\&.
+.SH "CHARSET OPTIONS"
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
+.PP
+\fBafpd\fR
+needs a way to preserve extended Macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0xF0) was saved as
+:f0\&. Some special characters will be converted as to :xx notation as well\&. \*(Aq/\*(Aq will be encoded to
+:2f, if
+\fBusedots\fR
+is not specified, a leading dot \*(Aq\&.\*(Aq will be encoded as
+:2e\&.
+.PP
+This version now uses UTF\-8 as the default encoding for names\&. \*(Aq/\*(Aq will be converted to \*(Aq:\*(Aq\&.
+.PP
+The
+\fBvol charset\fR
+option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be vol charset ISO\-8859\-15\&.
+\fBafpd\fR
+will accept any
+\fBiconv\fR(1)
+provided charset\&. If a character cannot be converted from the
+\fBmac charset\fR
+to the selected
+\fBvol charset\fR, afpd will save it as a CAP encoded character\&. For AFP3 clients,
+\fBafpd\fR
+will convert the UTF\-8
+character to
+\fBmac charset\fR
+first\&. If this conversion fails, you\*(Aqll receive a \-50 error on the mac\&.
+.PP
+\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafppasswd\fR(5),
+\fBafp_signature.conf\fR(5),
+\fBextmap.conf\fR(5),
+\fBcnid_metad\fR(8)
diff --git a/man/man5/afp.conf.5.tmpl b/man/man5/afp.conf.5.tmpl
deleted file mode 100644 (file)
index 50e6f81..0000000
+++ /dev/null
@@ -1,1117 +0,0 @@
-'\" t
-.\"     Title: afp.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 25 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP\&.CONF" "5" "25 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp.conf \- Netatalk configuration file
-.SH "SYNOPSIS"
-.PP
-The
-afp\&.conf
-file is the configuration file for the
-\fBNetatalk\fR
-AFP file server\&.
-.PP
-All AFP specific configuration and AFP volume definitions are done via this file\&.
-.SH "FILE FORMAT"
-.PP
-The file consists of sections and parameters\&. A section begins with the name of the section in square brackets and continues until the next section begins\&. Sections contain parameters of the form:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIname\fR = \fIvalue \fR
-      
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-The file is line\-based \- that is, each newline\-terminated line represents either a comment, a section name or a parameter\&.
-.PP
-Section and parameter names are case sensitive\&.
-.PP
-Only the first equals sign in a parameter is significant\&. Whitespace before or after the first equals sign is discarded\&. Leading, trailing and internal whitespace in section and parameter names is irrelevant\&. Leading and trailing whitespace in a parameter value is discarded\&. Internal whitespace within a parameter value is retained verbatim\&.
-.PP
-Any line beginning with a semicolon (\(lq;\(rq) or a hash (\(lq#\(rq) character is ignored, as are lines containing only whitespace\&.
-.PP
-Any line ending in a
-\(lq\e\(rq
-is continued on the next line in the customary UNIX fashion\&.
-.PP
-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, 1/0 or true/false\&. Case is not significant in boolean values, but is preserved in string values\&. Some items such as create masks are numeric\&.
-.PP
-The parameter
-\fBinclude = \fR\fB\fIpath\fR\fR
-allows you to include one config file inside another\&. The file is included literally, as though typed in place\&. Nested includes are not supported\&.
-.SH "SECTION DESCRIPTIONS"
-.PP
-Each section in the configuration file (except for the [Global] section) describes a shared resource (known as a
-\(lqvolume\(rq)\&. The section name is the name of the volume and the parameters within the section define the volume attributes and options\&.
-.PP
-There are two special sections, [Global] and [Homes], which are described under
-\fIspecial sections\fR\&. The following notes apply to ordinary section descriptions\&.
-.PP
-A volume consists of a directory to which access is being given plus a description of the access rights which are granted to the user of the service\&. For volumes the
-\fBpath\fR
-option must specify the directory to share\&.
-.PP
-Any volume section without
-\fBpath\fR
-option is considered a
-\fIvol preset\fR
-which can be selected in other volume sections via the
-\fBvol preset\fR
-option and constitutes defaults for the volume\&. For any option speficied both in a preset
-\fIand\fR
-in a volume section the volume section setting completly substitutes the preset option\&.
-.PP
-The access rights granted by the server are masked by the access rights granted to the specified or guest UNIX user by the host system\&. The server does not grant more access than the host system grants\&.
-.PP
-The following sample section defines an AFP volume\&. The user has full access to the path
-/foo/bar\&. The share is accessed via the share name
-baz:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-[baz]
-path = /foo/bar
-      
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SPECIAL SECTIONS"
-.SS "The [Global] section"
-.PP
-Parameters in this section apply to the server as a whole\&. Parameters denoted by a (G) below are must be set in this section\&.
-.SS "The [Homes] section"
-.PP
-This section enable sharing of the UNIX server user home directories\&. Specifying an optional
-\fBpath\fR
-parameter means that not the whole user home will be shared but the subdirectory
-\fBpath\fR\&. It is neccessary to define the
-\fBbasedir regex\fR
-option\&. It should be a regex which matches the parent directory of the user homes\&. Parameters denoted by a (H) belong to volume sections\&. The optional parameter
-\fBhome name\fR
-can be used to change the AFP volume name which
-\fI$u\'s home\fR
-by default\&. See below under VARIABLE SUBSTITUTIONS\&.
-.PP
-The following example illustrates this\&. Given all user home directories are stored under
-/home:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-[Homes]
-path = afp\-data
-basedir regex = /home
-      
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For a user
-\fIjohn\fR
-this results in an AFP home volume with a path of
-/home/john/afp\-data\&.
-.SH "PARAMETERS"
-.PP
-Parameters define the specific attributes of sections\&.
-.PP
-Some parameters are specific to the [Global] section (e\&.g\&.,
-\fIlog type\fR)\&. All others are permissible only in volume sections\&. The letter
-\fIG\fR
-in parentheses indicates that a parameter is specific to the [Global] section\&. The letter
-\fIV\fR
-indicates that a parameter can be specified in a volume specific section\&.
-.SH "VARIABLE SUBSTITUTIONS"
-.PP
-You can use variables in volume names\&. The use of variables in paths is not supported for now\&.
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 1.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  1." 4.2
-.\}
-if you specify an unknown variable, it will not get converted\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 2.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  2." 4.2
-.\}
-if you specify a known variable, but that variable doesn\'t have a value, it will get ignored\&.
-.RE
-.PP
-The variables which can be used for substitutions are:
-.PP
-$b
-.RS 4
-basename
-.RE
-.PP
-$c
-.RS 4
-client\'s ip address
-.RE
-.PP
-$d
-.RS 4
-volume pathname on server
-.RE
-.PP
-$f
-.RS 4
-full name (contents of the gecos field in the passwd file)
-.RE
-.PP
-$g
-.RS 4
-group name
-.RE
-.PP
-$h
-.RS 4
-hostname
-.RE
-.PP
-$i
-.RS 4
-client\'s ip, without port
-.RE
-.PP
-$s
-.RS 4
-server name (this can be the hostname)
-.RE
-.PP
-$u
-.RS 4
-user name (if guest, it is the user that guest is running as)
-.RE
-.PP
-$v
-.RS 4
-volume name
-.RE
-.PP
-$$
-.RS 4
-prints dollar sign ($)
-.RE
-.SH "EXPLANATION OF GLOBAL PARAMETERS"
-.SS "Authentication Options"
-.PP
-admin auth user = \fIuser\fR \fB(G)\fR
-.RS 4
-Specifying eg "\fBadmin auth user = root\fR" whenever a normal user login fails, afpd will try to authenticate as the specified
-\fBadmin auth user\fR\&. If this succeeds, a normal session is created for the original connecting user\&. Said differently: if you know the password of
-\fBadmin auth user\fR, you can authenticate as any other user\&.
-.RE
-.PP
-k5 keytab = \fIpath\fR \fB(G)\fR, k5 service = \fIservice\fR \fB(G)\fR, k5 realm = \fIrealm\fR \fB(G)\fR
-.RS 4
-These are required if the server supports the Kerberos 5 authentication UAM\&.
-.RE
-.PP
-ldap auth method = \fInone|simple|sasl\fR \fB(G)\fR
-.RS 4
-Authentication method:
-\fBnone | simple | sasl\fR
-.PP
-none
-.RS 4
-anonymous LDAP bind
-.RE
-.PP
-simple
-.RS 4
-simple LDAP bind
-.RE
-.PP
-sasl
-.RS 4
-SASL\&. Not yet supported !
-.RE
-.RE
-.PP
-ldap auth dn = \fIdn\fR \fB(G)\fR
-.RS 4
-Distinguished Name of the user for simple bind\&.
-.sp
-.RE
-.PP
-ldap auth pw = \fIpassword\fR \fB(G)\fR
-.RS 4
-Distinguished Name of the user for simple bind\&.
-.sp
-.RE
-.PP
-ldap server = \fIhost\fR \fB(G)\fR
-.RS 4
-Name or IP address of your LDAP Server\&. This is only needed for explicit ACL support in order to be able to query LDAP for UUIDs\&.
-.sp
-You can use
-\fBafpldaptest\fR(1)
-to syntactically check your config\&.
-.RE
-.PP
-ldap userbase = \fIbase dn\fR \fB(G)\fR
-.RS 4
-DN of the user container in LDAP\&.
-.sp
-.RE
-.PP
-ldap userscope = \fIscope\fR \fB(G)\fR
-.RS 4
-Search scope for user search:
-\fBbase | one | sub\fR
-.sp
-.RE
-.PP
-ldap groupbase = \fIbase dn\fR \fB(G)\fR
-.RS 4
-DN of the group container in LDAP\&.
-.sp
-.RE
-.PP
-ldap groupscope = \fIscope\fR \fB(G)\fR
-.RS 4
-Search scope for user search:
-\fBbase | one | sub\fR
-.sp
-.RE
-.PP
-ldap uuid attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the UUIDs\&.
-.sp
-Note: this is used both for users and groups\&.
-.sp
-.RE
-.PP
-ldap name attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the users short name\&.
-.sp
-.RE
-.PP
-ldap uuid string = \fISTRING\fR \fB(G)\fR
-.RS 4
-Format of the uuid string in the directory\&. A series of x and \-, where every x denotes a value 0\-9a\-f and every \- is a seperator\&.
-.sp
-Default: xxxxxxxx\-xxxx\-xxxx\-xxxx\-xxxxxxxxxxxx
-.RE
-.PP
-ldap uuid encoding = \fIstring | ms\-guid (default: string)\fR \fB(G)\fR
-.RS 4
-Format of the UUID of the LDAP attribute, allows usage of the binary objectGUID fields from Active Directory\&. If left unspecified, string is the default, which passes through the ASCII UUID returned by most other LDAP stores\&. If set to ms\-guid, the internal UUID representation is converted to and from the binary format used in the objectGUID attribute found on objects in Active Directory when interacting with the server\&.
-.PP
-string
-.RS 4
-UUID is a string, use with eg OpenDirectory\&.
-.RE
-.PP
-ms\-guid
-.RS 4
-Binary objectGUID from Active Directory
-.RE
-.RE
-.PP
-ldap group attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the groups short name\&.
-.sp
-.RE
-.PP
-nt domain = \fIDOMAIN\fR \fB(G)\fR, nt separator = \fISEPERATOR\fR \fB(G)\fR
-.RS 4
-Use for eg\&. winbind authentication, prepends both strings before the username from login and then tries to authenticate with the result through the availabel and active UAM authentication modules\&.
-.RE
-.PP
-save password = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Enables or disables the ability of clients to save passwords locally\&.
-.RE
-.PP
-set password = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Enables or disables the ability of clients to change their passwords via chooser or the "connect to server" dialog\&.
-.RE
-.PP
-uam list = \fIuam list\fR \fB(G)\fR
-.RS 4
-Space or comma separated list of UAMs\&. (The default is "uams_dhx\&.so uams_dhx2\&.so")\&.
-.sp
-The most commonly used UAMs are:
-.PP
-uams_guest\&.so
-.RS 4
-allows guest logins
-.RE
-.PP
-uams_clrtxt\&.so
-.RS 4
-(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&. (legacy)
-.RE
-.PP
-uams_randum\&.so
-.RS 4
-allows Random Number and Two\-Way Random Number Exchange for authentication (requires a separate file containing the passwords, either :ETCDIR:/afppasswd file or the one specified via "\fBpasswd file\fR"\&. See
-\fBafppasswd\fR(1)
-for details\&. (legacy)
-.RE
-.PP
-uams_dhx\&.so
-.RS 4
-(uams_dhx_pam\&.so or uams_dhx_passwd\&.so) Allow Diffie\-Hellman eXchange (DHX) for authentication\&.
-.RE
-.PP
-uams_dhx2\&.so
-.RS 4
-(uams_dhx2_pam\&.so or uams_dhx2_passwd\&.so) Allow Diffie\-Hellman eXchange 2 (DHX2) for authentication\&.
-.RE
-.PP
-uam_gss\&.so
-.RS 4
-Allow Kerberos V for authentication (optional)
-.RE
-.RE
-.PP
-uam path = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the default path for UAMs for this server (default is :LIBDIR:/netatalk)\&.
-.RE
-.SS "Charset Options"
-.PP
-With OS X Apple introduced the AFP3 protocol\&. One of the big changes was, that AFP3 uses Unicode names encoded as Decomposed UTF\-8 (UTF8\-MAC)\&. Previous AFP/OS versions used charsets like MacRoman, MacCentralEurope, etc\&.
-.PP
-To be able to serve AFP3 and older clients at the same time,
-\fBafpd\fR
-needs to be able to convert between UTF\-8 and Mac charsets\&. Even OS X clients partly still rely on the mac charset\&. As there\'s no way,
-\fBafpd\fR
-can detect the codepage a pre AFP3 client uses, you have to specify it using the
-\fBmac charset\fR
-option\&. The default is MacRoman, which should be fine for most western users\&.
-.PP
-As
-\fBafpd\fR
-needs to interact with UNIX operating system as well, it need\'s to be able to convert from UTF8\-MAC / Mac charset to the UNIX charset\&. By default
-\fBafpd\fR
-uses
-\fIUTF8\fR\&. You can set the UNIX charset using the
-\fBunix charset\fR
-option\&. If you\'re using extended characters in the configuration files for
-\fBafpd\fR, make sure your terminal matches the
-\fBunix charset\fR\&.
-.PP
-mac charset = \fICHARSET\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the Mac clients charset, e\&.g\&.
-\fIMAC_ROMAN\fR\&. This is used to convert strings and filenames to the clients codepage for OS9 and Classic, i\&.e\&. for authentication and AFP messages (SIGUSR2 messaging)\&. This will also be the default for the volumes
-\fBmac charset\fR\&. Defaults to
-\fIMAC_ROMAN\fR\&.
-.RE
-.PP
-unix charset = \fICHARSET\fR \fB(G)\fR
-.RS 4
-Specifies the servers unix charset, e\&.g\&.
-\fIISO\-8859\-15\fR
-or
-\fIEUC\-JP\fR\&. This is used to convert strings to/from the systems locale, e\&.g\&. for authenthication, server messages and volume names\&. If
-\fILOCALE\fR
-is set, the systems locale is used\&. Defaults to
-\fIUTF8\fR\&.
-.RE
-.PP
-vol charset = \fICHARSET\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the encoding of the volumes filesystem\&. By default, it is the same as
-\fBunix charset\fR\&.
-.RE
-.SS "Password Options"
-.PP
-passwd file = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the path to the Randnum UAM passwd file for this server (default is :ETCDIR:/afppasswd)\&.
-.RE
-.PP
-passwd minlen = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the minimum password length, if supported by the UAM
-.RE
-.SS "Network Options"
-.PP
-advertise ssh = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Allows old Mac OS X clients (10\&.3\&.3\-10\&.4) to automagically establish a tunneled AFP connection through SSH\&. If this option is set, the server\'s answers to client\'s FPGetSrvrInfo requests contain an additional entry\&. It depends on both client\'s settings and a correctly configured and running
-\fBsshd\fR(8)
-on the server to let things work\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-Setting this option is not recommended since globally encrypting AFP connections via SSH will increase the server\'s load significantly\&. On the other hand, Apple\'s client side implementation of this feature in MacOS X versions prior to 10\&.3\&.4 contained a security flaw\&.
-.sp .5v
-.RE
-.RE
-.PP
-afp listen = \fIip address[:port] [ip adress[:port] \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specifies the IP address that the server should advertise
-\fBand\fR
-listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
-.RE
-.PP
-afp port = \fIport number\fR \fB(G)\fR
-.RS 4
-Allows a different TCP port to be used for AFP\&. The default is 548\&. Also sets the default port applied when none specified in an
-\fBafp listen\fR
-option\&.
-.RE
-.PP
-cnid listen = \fIip address[:port] [ip adress[:port] \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specifies the IP address that the CNID server should listen on\&. The default is
-\fBlocalhost:4700\fR\&.
-.RE
-.PP
-disconnect time = \fInumber\fR \fB(G)\fR
-.RS 4
-Keep disconnected AFP sessions for
-\fInumber\fR
-hours before dropping them\&. Default is 24 hours\&.
-.RE
-.PP
-dsireadbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Scale factor that determines the size of the DSI/TCP readahead buffer, default is 12\&. This is multiplies with the DSI server quantum (default ~300k) to give the size of the buffer\&. Increasing this value might increase throughput in fast local networks for volume to volume copies\&.
-\fINote\fR: This buffer is allocated per afpd child process, so specifying large values will eat up large amount of memory (buffer size * number of clients)\&.
-.RE
-.PP
-fqdn = \fIname:port\fR \fB(G)\fR
-.RS 4
-Specifies a fully\-qualified domain name, with an optional port\&. This is discarded if the server cannot resolve it\&. This option is not honored by AppleShare clients <= 3\&.8\&.3\&. This option is disabled by default\&. Use with caution as this will involve a second name resolution step on the client side\&. Also note that afpd will advertise this name:port combination but not automatically listen to it\&.
-.RE
-.PP
-hostname = \fIname\fR \fB(G)\fR
-.RS 4
-Use this instead of the result from calling hostname for dertermening which IP address to advertise, therfore the hostname is resolved to an IP which is the advertised\&. This is NOT used for listening and it is also overwritten by
-\fBafp listen\fR\&.
-.RE
-.PP
-max connections = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the maximum number of clients that can simultaneously connect to the server (default is 200)\&.
-.RE
-.PP
-server quantum = \fInumber\fR \fB(G)\fR
-.RS 4
-This specifies the DSI server quantum\&. The default value is 303840\&. The maximum value is 0xFFFFFFFFF, the minimum is 32000\&. If you specify a value that is out of range, the default value will be set\&. Do not change this value unless you\'re absolutely sure, what you\'re doing
-.RE
-.PP
-sleep time = \fInumber\fR \fB(G)\fR
-.RS 4
-Keep sleeping AFP sessions for
-\fInumber\fR
-hours before disconnecting clients in sleep mode\&. Default is 10 hours\&.
-.RE
-.PP
-tcprcvbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Try to set TCP receive buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
-.RE
-.PP
-tcpsndbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Try to set TCP send buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
-.RE
-.PP
-use sendfile = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to use sendfile
-syscall for sending file data to clients\&.
-.RE
-.PP
-zeroconf = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to use automatic Zeroconf
-service registration if Avahi or mDNSResponder were compiled in\&.
-.RE
-.SS "Miscellaneous Options"
-.PP
-admin group = \fIgroup\fR \fB(G)\fR
-.RS 4
-Allows users of a certain group to be seen as the superuser when they log in\&. This option is disabled by default\&.
-.RE
-.PP
-afp read locks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Whether to apply locks to the byte region read in FPRead calls\&. The AFP spec mandates this, but it\'s not really in line with UNIX semantics and is a performance hug\&.
-.RE
-.PP
-basedir regex = \fIregex\fR \fB(H)\fR
-.RS 4
-Regular expression which matches the parent directory of the user homes\&. In the simple case this is just a path ie
-\fBbasedir regex = /home\fR
-.RE
-.PP
-close vol = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Whether to close volumes possibly opened by clients when they\'re removed from the configuration and the configuration is reloaded\&.
-.RE
-.PP
-cnid server = \fIipaddress[:port]\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
-.RE
-.PP
-dircachesize = \fInumber\fR \fB(G)\fR
-.RS 4
-Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
-.sp
-Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
-.RE
-.PP
-fce listener = \fIhost[:port]\fR \fB(G)\fR
-.RS 4
-Enables sending FCE events to the specified
-\fIhost\fR, default
-\fIport\fR
-is 12250 if not specified\&. Specifying mutliple listeners is done by having this option once for each of them\&.
-.RE
-.PP
-fce events = \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR \fB(G)\fR
-.RS 4
-Speficies which FCE events are active, default is
-\fIfmod,fdel,ddel,fcre,dcre\fR\&.
-.RE
-.PP
-fce coalesce = \fIall|delete|create\fR \fB(G)\fR
-.RS 4
-Coalesce FCE events\&.
-.RE
-.PP
-fce holdfmod = \fIseconds\fR \fB(G)\fR
-.RS 4
-This determines the time delay in seconds which is always waited if another file modification for the same file is done by a client before sending an FCE file modification event (fmod)\&. For example saving a file in Photoshop would generate multiple events by itself because the application is opening, modifying and closing a file mutliple times for every "save"\&. Defautl: 60 seconds\&.
-.RE
-.PP
-guest account = \fIname\fR \fB(G)\fR
-.RS 4
-Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.
-.RE
-.PP
-home name = \fIname\fR \fB(H)\fR
-.RS 4
-AFP user home volume name\&. The default is
-\fIusers\'s home\fR\&.
-.RE
-.PP
-keep sessions = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Enable "Continuous AFP Service"\&. This means restarting AFP and CNID service daemons master processes, but keeping the AFP session processes\&. This can be used to install (most) updates to Netatalk without interruping active AFP sessions\&. Existing AFP sessions will still run the version from before updating, but new AFP sessions will run the updated code\&. After enabling this option when sending SIGQUIT to the
-\fInetatalk\fR
-service controller process, the AFP and CNID daemons will exit and then the service controller will restart them\&. AFP session processes are notified of the master afpd shutdown, they will then sleep 15\-20 seconds and then try to reconnect their IPC channel to the master afpd process\&. The IPC channel between the AFP master service daemon and the AFP session child is used for keeping session state of AFP sessions in the AFP master process\&. The session state is needed when AFP clients experience eg network outages and try to reconnect to the AFP server\&.
-.RE
-.PP
-login message = \fImessage\fR \fB(G)/(V)\fR
-.RS 4
-Sets a message to be displayed when clients logon to the server\&. The message should be in
-\fBunix charset\fR
-and should be quoted\&. Extended characters are allowed\&.
-.RE
-.PP
-map acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to map filesystem ACLs to effective permissions\&.
-.RE
-.PP
-mimic model = \fImodel\fR \fB(G)\fR
-.RS 4
-Specifies the icon model that appears on clients\&. Defaults to off\&. Examples: RackMac (same as Xserve), PowerBook, PowerMac, Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro, AppleTV1,1, AirPort\&.
-.RE
-.PP
-signature = <text> \fB(G)\fR
-.RS 4
-Specify a server signature\&. The maximum length is 16 characters\&. This option is useful for clustered environments, to provide fault isolation etc\&. By default, afpd generate signature and saving it to
-:STATEDIR:/netatalk/afp_signature\&.conf
-automatically (based on random number)\&. See also asip\-status\&.pl(1)\&.
-.RE
-.PP
-solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
-.RE
-.PP
-vol dbpath = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the database information to be stored in path\&. You have to specifiy a writable location, even if the volume is read only\&. The default is
-:STATEDIR:/netatalk/CNID/\&.
-.RE
-.PP
-volnamelen = \fInumber\fR \fB(G)\fR
-.RS 4
-Max length of UTF8\-MAC volume name for Mac OS X\&. Note that Hangul is especially sensitive to this\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-73:  limit of Mac OS X 10\&.1
-80:  limit of Mac OS X 10\&.4/10\&.5 (default)
-255: limit of recent Mac OS X
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Mac OS 9 and earlier are not influenced by this, because Maccharset volume name is always limitted to 27 bytes\&.
-.RE
-.PP
-vol preset = \fIname\fR \fB(G)/(V)\fR
-.RS 4
-Use section
-\fBname\fR
-as option preset for all volumes (when set in the [Global] section) or for one volume (when set in that volume\'s section)\&.
-.RE
-.SS "Logging Options"
-.PP
-log file = \fIlogfile\fR \fB(G)\fR
-.RS 4
-If not specified Netatalk logs to syslogs daemon facilify\&. Otherwise it logs to
-\fBlogfile\fR\&.
-.RE
-.PP
-log level = \fItype:level [type:level \&.\&.\&.]\fR \fB(G)\fR, log level = \fItype:level,[type:level, \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specify that any message of a loglevel up to the given
-\fBlog level\fR
-should be logged\&.
-.sp
-By default afpd logs to syslog with a default logging setup equivalent to
-\fBdefault:note\fR
-.sp
-logtypes: default, afpdaemon, logger, uamsdaemon
-.sp
-loglevels: severe, error, warn, note, info, debug, debug6, debug7, debug8, debug9, maxdebug
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-Both logtype and loglevels are case insensitive\&.
-.sp .5v
-.RE
-.RE
-.SS "Debug Parameters"
-.PP
-These options are useful for debugging only\&.
-.PP
-tickleval = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the tickle timeout interval (in seconds)\&. Defaults to 30\&.
-.RE
-.PP
-timeout = \fInumber\fR \fB(G)\fR
-.RS 4
-Specify the number of tickles to send before timing out a connection\&. The default is 4, therefore a connection will timeout after 2 minutes\&.
-.RE
-.PP
-client polling = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-With this option enabled, afpd won\'t advertise that it is capable of server notifications, so that connected clients poll the server every 10 seconds to detect changes in opened server windows\&.
-\fINote\fR: Depending on the number of simultaneously connected clients and the network\'s speed, this can lead to a significant higher load on your network!
-.sp
-Do not use this option any longer as present Netatalk correctly supports server notifications, allowing connected clients to update folder listings in case another client changed the contents\&.
-.RE
-.SH "EXPLANATION OF VOLUME PARAMETERS"
-.SS "Parameters"
-.PP
-The section name defines the volume name which is the name that appears in the Chooser or the "connect to server" dialog on Macintoshes to represent the appropriate share\&. No two volumes may have the same name\&. The volume name cannot contain the
-\':\'
-character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
-.PP
-path = \fIPATH\fR \fB(V)\fR
-.RS 4
-The path name must be a fully qualified path name, or a path name using either the ~ shell shorthand or any of the substitution variables, which are listed below\&.
-.sp
-The volume name is the name that appears in the Chooser ot the "connect to server" dialog on Macintoshes to represent the appropriate share\&. If volumename is unspecified, the last component of pathname is used\&. No two volumes may have the same name\&. If there are spaces in the name, it should be in quotes (i\&.e\&. "File Share")\&. The volume name cannot contain the
-\':\'
-character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
-.RE
-.PP
-appledouble = \fIea|v2\fR \fB(V)\fR
-.RS 4
-Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble v2, the new default format is
-\fBea\fR\&.
-.RE
-.PP
-vol size limit = \fIsize in MiB\fR \fB(V)\fR
-.RS 4
-Useful for Time Machine: limits the reported volume size, thus preventing Time Machine from using the whole real disk space for backup\&. Example: "vol size limit = 1000" would limit the reported disk space to 1 GB\&.
-\fBIMPORTANT: \fR
-This is an approimated calculation taking into accout the contents of Time Machine sparsebundle images\&. Therefor you MUST NOT use this volume to store other content when using this option, because it would NOT be accounted\&. The calculation works by reading the band size from the Info\&.plist XML file of the sparsebundle, reading the bands/ directory counting the number of band files, and then multiplying one with the other\&.
-.RE
-.PP
-valid users = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by spaces or commas\&. Groups are designated by a @ prefix\&. Example: "valid users = user1 user2 @group"
-.RE
-.PP
-invalid users = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the "valid users" option\&.
-.RE
-.PP
-hosts allow = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
-.RS 4
-Only listed hosts and networks are allowed, all others are rejected\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
-.sp
-Example: hosts allow = 10\&.1\&.0\&.0/16 10\&.2\&.1\&.100 2001:0db8:1234::/48
-.RE
-.PP
-hosts deny = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
-.RS 4
-Listed hosts and nets are rejected, all others are allowed\&.
-.sp
-Example: hosts deny = 192\&.168\&.100/24 10\&.1\&.1\&.1 2001:db8::1428:57ab
-.RE
-.PP
-cnid scheme = \fIbackend\fR \fB(V)\fR
-.RS 4
-set the CNID backend to be used for the volume, default is [:DEFAULT_CNID_SCHEME:] available schemes: [:COMPILED_BACKENDS:]
-.RE
-.PP
-ea = \fInone|auto|sys|ad\fR
-.RS 4
-Specify how Extended Attributes
-are stored\&.
-\fBauto\fR
-is the default\&.
-.PP
-auto
-.RS 4
-Try
-\fBsys\fR
-(by setting an EA on the shared directory itself), fallback to
-\fBad\fR\&. Requires writeable volume for perfoming test\&. "\fBread only = yes\fR" overwrites
-\fBauto\fR
-with
-\fBnone\fR\&. Use explicit "\fBea = sys|ad\fR" for read\-only volumes where appropiate\&.
-.RE
-.PP
-sys
-.RS 4
-Use filesystem Extended Attributes\&.
-.RE
-.PP
-ad
-.RS 4
-Use files in
-\fI\&.AppleDouble\fR
-directories\&.
-.RE
-.PP
-none
-.RS 4
-No Extended Attributes support\&.
-.RE
-.RE
-.PP
-mac charset = \fICHARSET\fR \fB(V)\fR
-.RS 4
-specifies the Mac client charset for this Volume, e\&.g\&.
-\fIMAC_ROMAN\fR,
-\fIMAC_CYRILLIC\fR\&. If not specified the global setting is applied\&. This setting is only required if you need volumes, where the Mac charset differs from the one globally set in the [Global] section\&.
-.RE
-.PP
-casefold = \fBoption\fR
-.RS 4
-The casefold option handles, if the case of filenames should be changed\&. The available options are:
-.sp
-\fBtolower\fR
-\- Lowercases names in both directions\&.
-.sp
-\fBtoupper\fR
-\- Uppercases names in both directions\&.
-.sp
-\fBxlatelower\fR
-\- Client sees lowercase, server sees uppercase\&.
-.sp
-\fBxlateupper\fR
-\- Client sees uppercase, server sees lowercase\&.
-.RE
-.PP
-password = \fIpassword\fR \fB(V)\fR
-.RS 4
-This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
-.RE
-.PP
-file perm = \fImode\fR \fB(V)\fR, directory perm = \fImode\fR \fB(V)\fR
-.RS 4
-Add(or) with the client requested permissions:
-\fBfile perm\fR
-is for files only,
-\fBdirectory perm\fR
-is for directories only\&. Don\'t use with "\fBunix priv = no\fR"\&.
-.PP
-\fBExample.\ \&Volume for a collaborative workgroup\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-file perm = 0660
-directory perm = 0770
-.fi
-.if n \{\
-.RE
-.\}
-
-.RE
-.PP
-umask = \fImode\fR \fB(V)\fR
-.RS 4
-set perm mask\&. Don\'t use with "\fBunix priv = no\fR"\&.
-.RE
-.PP
-preexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run when the volume is mounted, ignored for user defined volumes
-.RE
-.PP
-postexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run when the volume is closed, ignored for user defined volumes
-.RE
-.PP
-root preexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run as root when the volume is mounted, ignored for user defined volumes
-.RE
-.PP
-root postexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run as root when the volume is closed, ignored for user defined volumes
-.RE
-.PP
-rolist = \fBusers/groups\fR \fB(V)\fR
-.RS 4
-Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
-.RE
-.PP
-rwlist = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
-.RE
-.PP
-veto files = \fIvetoed names\fR \fB(V)\fR
-.RS 4
-hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. The veto string must always be terminated with a \'/\', eg\&. "veto1/", "veto1/veto2/"\&.
-.RE
-.SS "Volume options"
-.PP
-Boolean volume options\&.
-.PP
-acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to flag volumes as supporting ACLs\&. If ACL support is compiled in, this is yes by default\&.
-.RE
-.PP
-cnid dev = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to use the device number in the CNID backends\&. Helps when the device number is not constant across a reboot, eg cluster, \&.\&.\&.
-.RE
-.PP
-convert appledouble = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether automatic conversion from
-\fBappledouble = v2\fR
-to
-\fBappledouble = ea\fR
-is performed when accessing filesystems from clients\&. This is generally useful, but costs some performance\&. It\'s recommdable to run
-\fBdbd\fR
-on volumes and do the conversion with that\&. Then this option can be set to no\&.
-.RE
-.PP
-invisible dots = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-make dot files invisible\&.
-.RE
-.PP
-network ids = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether the server support network ids\&. Setting this to
-\fIno\fR
-will result in the client not using ACL AFP functions\&.
-.RE
-.PP
-preexec close = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-A non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
-.RE
-.PP
-read only = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Specifies the share as being read only for all users\&. Overwrites
-\fBea = auto\fR
-with
-\fBea = none\fR
-.RE
-.PP
-root preexec close= \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-A non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
-.RE
-.PP
-search db = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
-.RE
-.PP
-stat vol = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
-.RE
-.PP
-time machine = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Whether to enable Time Machine suport for this volume\&.
-.RE
-.PP
-unix priv = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to use AFP3 UNIX privileges\&. This should be set for OS X clients\&. See also:
-\fBfile perm\fR,
-\fBdirectory perm\fR
-and
-\fBumask\fR\&.
-.RE
-.SH "CNID BACKENDS"
-.PP
-The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\'s in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
-:STATEDIR:/netatalk/CNID/(volumename)/\&.AppleDB/
-directory\&.
-.PP
-cdb
-.RS 4
-"Concurrent database", backend is based on Oracle Berkely DB\&. With this backend several
-\fBafpd\fR
-deamons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
-\fBafpd\fR
-process is active for a volume\&. The drawback is, that the crash of a single
-\fBafpd\fR
-process might corrupt the database\&.
-.RE
-.PP
-dbd
-.RS 4
-Access to the CNID database is restricted to the
-\fBcnid_metad\fR
-daemon process\&.
-\fBafpd\fR
-processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
-\fBcdb\fR
-.RE
-.PP
-last
-.RS 4
-This backend is an exception, in terms of ID persistency\&. ID\'s are only valid for the current session\&. This is basically what
-\fBafpd\fR
-did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&. Starting with Netatalk 3\&.0, it becomes the
-\fIread only mode\fR
-automatically\&.
-.sp
-\fBWarning\fR: It is
-\fINOT\fR
-recommended to use this backend for volumes anymore, as
-\fBafpd\fR
-now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
-.RE
-.PP
-Even though
-\fB\&./configure \-\-help\fR
-might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\'t use them unless you know what you\'re doing, they may be removed without further notice from future versions\&.
-.SH "CHARSET OPTIONS"
-.PP
-With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
-.PP
-\fBafpd\fR
-needs a way to preserve extended macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0xF0) was saved as
-:f0\&. Some special characters will be converted as to :xx notation as well\&. \'/\' will be encoded to
-:2f, if
-\fBusedots\fR
-is not specified, a leading dot \'\&.\' will be encoded as
-:2e\&.
-.PP
-This version now uses UTF\-8 as the default encoding for names\&. \'/\' will be converted to \':\'\&.
-.PP
-The
-\fBvol charset\fR
-option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be vol charset ISO\-8859\-15\&.
-\fBafpd\fR
-will accept any
-\fBiconv\fR(1)
-provided charset\&. If a character cannot be converted from the
-\fBmac charset\fR
-to the selected
-\fBvol charset\fR, afpd will save it as a CAP encoded character\&. For AFP3 clients,
-\fBafpd\fR
-will convert the UTF\-8
-character to
-\fBmac charset\fR
-first\&. If this conversion fails, you\'ll receive a \-50 error on the mac\&.
-.PP
-\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafppasswd\fR(5),
-\fBafp_signature.conf\fR(5),
-\fBcnid_metad\fR(8)
diff --git a/man/man5/afp_signature.conf.5.in b/man/man5/afp_signature.conf.5.in
new file mode 100644 (file)
index 0000000..c620ef0
--- /dev/null
@@ -0,0 +1,86 @@
+'\" t
+.\"     Title: afp_signature.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP_SIGNATURE\&.CONF" "5" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp_signature.conf \- Configuration file used by afpd(8) to specify server signature
+.SH "DESCRIPTION"
+.PP
+@localstatedir@/netatalk/afp_signature\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify server signature automagically\&. The configuration lines are composed like:
+.PP
+\fI"server name"\fR
+\fIhexa\-string\fR
+.PP
+The first field is server name\&. Server names must be quoted if they contain spaces\&. The second field is the hexadecimal string of 32 characters for 16\-bytes server signature\&.
+.PP
+The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+Server Signature is unique 16\-bytes identifier used to prevent logging on to the same server twice\&.
+.PP
+Netatalk 2\&.0 and earlier generated server signature by using gethostid()\&. There was a problem that another servers have the same signature because the hostid is not unique enough\&.
+.PP
+Current netatalk generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
+.PP
+This file should not be thoughtlessly edited and be copied onto another server\&. If it wants to set the signature intentionally, use the option "signature =" in afp\&.conf\&. In this case, afp_signature\&.conf is not used\&.
+.sp .5v
+.RE
+.PP
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&afp_signature.conf\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# This is a comment\&.
+"My Server" 74A0BB94EC8C13988B2E75042347E528
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBasip-status.pl\fR(1)
diff --git a/man/man5/afp_signature.conf.5.tmpl b/man/man5/afp_signature.conf.5.tmpl
deleted file mode 100644 (file)
index 54e2267..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-'\" t
-.\"     Title: afp_signature.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP_SIGNATURE\&.CONF" "5" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_signature.conf \- Configuration file used by afpd(8) to specify server signature
-.SH "DESCRIPTION"
-.PP
-:STATEDIR:/netatalk/afp_signature\&.conf
-is the configuration file used by
-\fBafpd\fR
-to specify server signature automagically\&. The configuration lines are composed like:
-.PP
-\fI"server name"\fR
-\fIhexa\-string\fR
-.PP
-The first field is server name\&. Server names must be quoted if they contain spaces\&. The second field is the hexadecimal string of 32 characters for 16\-bytes server signature\&.
-.PP
-The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-Server Signature is unique 16\-bytes identifier used to prevent logging on to the same server twice\&.
-.PP
-Netatalk 2\&.0 and earlier generated server signature by using gethostid()\&. There was a problem that another servers have the same signature because the hostid is not unique enough\&.
-.PP
-Current netatalk generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
-.PP
-This file should not be thoughtlessly edited and be copied onto another server\&. If it wants to set the signature intentionally, use the option "signature =" in afp\&.conf\&. In this case, afp_signature\&.conf is not used\&.
-.sp .5v
-.RE
-.PP
-.SH "EXAMPLES"
-.PP
-\fBExample.\ \&afp_signature.conf\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-# This is a comment\&.
-"My Server" 74A0BB94EC8C13988B2E75042347E528
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5),
-\fBasip-status.pl\fR(1)
diff --git a/man/man5/afp_voluuid.conf.5.in b/man/man5/afp_voluuid.conf.5.in
new file mode 100644 (file)
index 0000000..21612dd
--- /dev/null
@@ -0,0 +1,87 @@
+'\" t
+.\"     Title: afp_voluuid.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP_VOLUUID\&.CONF" "5" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp_voluuid.conf \- Configuration file used by afpd(8) to specify UUID for Time Machine volume
+.SH "DESCRIPTION"
+.PP
+@localstatedir@/netatalk/afp_voluuid\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify UUID of Time Machine volume automagically\&. The configuration lines are composed like:
+.PP
+\fI"volume name"\fR
+\fIuuid\-string\fR
+.PP
+The first field is volume name\&. Volume names must be quoted if they contain spaces\&. The second field is the 36 character hexadecimal ASCII string representation of a UUID\&.
+.PP
+The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+This UUID is advertised by Zeroconf in order to provide robust disambiguation of Time Machine volume\&.
+.PP
+The afpd generates the UUID from random numbers and saves it into afp_voluuid\&.conf, only when setting "time machine = yes" option in afp\&.conf\&.
+.PP
+This file should not be thoughtlessly edited and be copied onto another server\&.
+.sp .5v
+.RE
+.PP
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&afp_voluuid.conf three TM volumes on one netatalk\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# This is a comment\&.
+"Backup for John Smith" 1573974F\-0ABD\-69CC\-C40A\-8519B681A0E1
+"bob" 39A487F4\-55AA\-8240\-E584\-69AA01800FE9
+mary 6331E2D1\-446C\-B68C\-3066\-D685AADBE911
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBavahi-daemon\fR(8),
+\fBmDNSResponder\fR(8)
diff --git a/man/man5/afp_voluuid.conf.5.tmpl b/man/man5/afp_voluuid.conf.5.tmpl
deleted file mode 100644 (file)
index 8d3626d..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-'\" t
-.\"     Title: afp_voluuid.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP_VOLUUID\&.CONF" "5" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_voluuid.conf \- Configuration file used by afpd(8) to specify UUID for Time Machine volume
-.SH "DESCRIPTION"
-.PP
-:STATEDIR:/netatalk/afp_voluuid\&.conf
-is the configuration file used by
-\fBafpd\fR
-to specify UUID of Time Machine volume automagically\&. The configuration lines are composed like:
-.PP
-\fI"volume name"\fR
-\fIuuid\-string\fR
-.PP
-The first field is volume name\&. Volume names must be quoted if they contain spaces\&. The second field is the 36 character hexadecimal ASCII string representation of a UUID\&.
-.PP
-The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-This UUID is advertised by Zeroconf in order to provide robust disambiguation of Time Machine volume\&.
-.PP
-The afpd generates the UUID from random numbers and saves it into afp_voluuid\&.conf, only when setting "time machine = yes" option in afp\&.conf\&.
-.PP
-This file should not be thoughtlessly edited and be copied onto another server\&.
-.sp .5v
-.RE
-.PP
-.SH "EXAMPLES"
-.PP
-\fBExample.\ \&afp_voluuid.conf three TM volumes on one netatalk\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-# This is a comment\&.
-"Backup for John Smith" 1573974F\-0ABD\-69CC\-C40A\-8519B681A0E1
-"bob" 39A487F4\-55AA\-8240\-E584\-69AA01800FE9
-mary 6331E2D1\-446C\-B68C\-3066\-D685AADBE911
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5),
-\fBavahi-daemon\fR(8),
-\fBmDNSResponder\fR(8)
diff --git a/man/man5/extmap.conf.5.in b/man/man5/extmap.conf.5.in
new file mode 100644 (file)
index 0000000..9e0abfa
--- /dev/null
@@ -0,0 +1,77 @@
+'\" t
+.\"     Title: extmap.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "EXTMAP\&.CONF" "5" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+extmap.conf \- Configuration file used by afpd(8) to specify file name extension mappings\&.
+.SH "SYNOPSIS"
+.HP \w'\fB@pkgconfdir@/extmap\&.conf\fR\fB\fR\ 'u
+\fB@pkgconfdir@/extmap\&.conf\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+
+@pkgconfdir@/extmap\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify file name extension mappings\&.
+.PP
+The configuration lines are composed like:
+.PP
+\&.extension
+\fI[ type [ creator ] ]\fR
+.PP
+Any line beginning with a hash (\(lq#\(rq) character is ignored\&. The leading\-dot lines specify file name extension mappings\&. The extension \*(Aq\&.\*(Aq sets the default creator and type for otherwise untyped Unix files\&.
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&Extension is jpg. Type is "JPEG". Creator is "ogle".\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\&.jpg "JPEG" "ogle"
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+\fBExample.\ \&Extension is lzh. Type is "LHA ". Creator is not defined.\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\&.lzh "LHA "
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5),
+\fBafpd\fR(8)
index 711b8c366feeaa990bf572e2d9538b5f97a361d0..29043a09f888f3ffa7478bd8196c7134074373b5 100644 (file)
@@ -1,12 +1,3 @@
 Makefile
 Makefile.in
-afpd.8
-afp_acls.8
-atalkd.8
-cnid_dbd.8
-cnid_metad.8
-netatalk.8
-papd.8
-papstatus.8
-psf.8
-*.o
+*.8
index eb112ab3d7445ee15c824cea5b2df46e1d74d485..fdc6eaf190cfbcb2ec46753500fcfcde09ebf6cb 100644 (file)
@@ -1,24 +1,9 @@
 ## Makefile.am for man/man8/
 
-pkgconfdir = @PKGCONFDIR@
+man_MANS = \
+       afpd.8 \
+       cnid_dbd.8 \
+       cnid_metad.8 \
+       netatalk.8
 
-SUFFIXES = .tmpl .
-
-.tmpl:
-       sed -e s@:SBINDIR:@${sbindir}@ \
-           -e s@:BINDIR:@${bindir}@ \
-           -e s@:ETCDIR:@${pkgconfdir}@ \
-           -e s@:LIBDIR:@${libdir}@ \
-           -e s@:LIBEXECDIR:@${libexecdir}@ \
-           -e "s@:STATEDIR:@${localstatedir}@g" \
-           -e s@:NETATALK_VERSION:@${NETATALK_VERSION}@ \
-           <$< >$@
-
-GENERATED_MANS    = afpd.8 cnid_dbd.8 cnid_metad.8 netatalk.8
-TEMPLATE_FILES    = afpd.8.tmpl cnid_dbd.8.tmpl cnid_metad.8.tmpl netatalk.8.tmpl
-
-man_MANS = $(GENERATED_MANS)
-
-CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES)
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man8/afpd.8.in b/man/man8/afpd.8.in
new file mode 100644 (file)
index 0000000..ab40fa3
--- /dev/null
@@ -0,0 +1,171 @@
+'\" t
+.\"     Title: afpd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPD" "8" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpd \- Apple Filing Protocol daemon
+.SH "SYNOPSIS"
+.HP \w'\fBafpd\fR\fB\fR\fBafpd\fR\fB\fR\ 'u
+\fBafpd\fR\fB\fR [\-d] [\-F\ \fIconfigfile\fR]
+.br
+\fBafpd\fR\fB\fR \-v | \-V | \-h 
+.SH "DESCRIPTION"
+.PP
+\fBafpd\fR
+provides an Apple Filing Protocol (AFP) interface to the Unix file system\&. It is normally started at boot time by
+\fBnetatalk\fR(8)\&.
+.PP
+@pkgconfdir@/afp\&.conf
+is the configuration file used by
+\fBafpd\fR
+to determine the behavior and configuration of a file server\&.
+.SH "OPTIONS"
+.PP
+\-d
+.RS 4
+Specifies that the daemon should not fork\&.
+.RE
+.PP
+\-v
+.RS 4
+Print version information and exit\&.
+.RE
+.PP
+\-V
+.RS 4
+Print verbose information and exit\&.
+.RE
+.PP
+\-h
+.RS 4
+Print help and exit\&.
+.RE
+.PP
+\-F \fIconfigfile\fR
+.RS 4
+Specifies the configuration file to use\&. (Defaults to
+@pkgconfdir@/afp\&.conf\&.)
+.RE
+.SH "SIGNALS"
+.PP
+To shut down a user\*(Aqs
+\fBafpd\fR
+process it is recommended that
+\fBSIGKILL (\-9)\fR
+\fINOT\fR
+be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
+\fBafpd\fR
+is to send it a
+\fBSIGTERM (\-15)\fR
+signal and wait for it to die on its own\&.
+.PP
+SIGTERM and SIGUSR1 signals that are sent to the main
+\fBafpd\fR
+process are propagated to the children, so all will be affected\&.
+.PP
+SIGTERM
+.RS 4
+Clean exit\&. Propagates from master to childs\&.
+.RE
+.PP
+SIGQUIT
+.RS 4
+Send this to the master
+\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
+.RE
+.PP
+SIGHUP
+.RS 4
+Sending a
+\fBSIGHUP\fR
+to afpd will cause it to reload its configuration files\&.
+.RE
+.PP
+SIGINT
+.RS 4
+Sending a
+\fBSIGINT\fR
+to a child
+\fBafpd\fR
+enables
+\fImax_debug\fR
+logging for this process\&. The log is sent to the file
+/tmp/afpd\&.PID\&.XXXXXX\&. Sending another
+\fBSIGINT\fR
+will revert to the original log settings\&.
+.RE
+.PP
+SIGUSR1
+.RS 4
+The
+\fBafpd\fR
+process will send the message "The server is going down for maintenance\&." to the client and shut itself down in 5 minutes\&. New connections are not allowed\&. If this is sent to a child afpd, the other children are not affected\&. However, the main process will still exit, disabling all new connections\&.
+.RE
+.PP
+SIGUSR2
+.RS 4
+The
+\fBafpd\fR
+process will look in the message directory configured at build time for a file named message\&.pid\&. For each one found, a the contents will be sent as a message to the associated AFP client\&. The file is removed after the message is sent\&. This should only be sent to a child
+\fBafpd\fR\&.
+.RE
+.SH "FILES"
+.PP
+@pkgconfdir@/afp\&.conf
+.RS 4
+configuration file used by afpd
+.RE
+.PP
+@localstatedir@/netatalk/afp_signature\&.conf
+.RS 4
+list of server signature
+.RE
+.PP
+@localstatedir@/netatalk/afp_voluuid\&.conf
+.RS 4
+list of UUID for Time Machine volume
+.RE
+.PP
+@pkgconfdir@/extmap\&.conf
+.RS 4
+file name extension mapping
+.RE
+.PP
+@pkgconfdir@/msg/message\&.pid
+.RS 4
+contains messages to be sent to users\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBnetatalk\fR(8),
+\fBhosts_access\fR(5),
+\fBafp.conf\fR(5),
+\fBafp_signature.conf\fR(5),
+\fBafp_voluuid.conf\fR(5),
+\fBextmap.conf\fR(5),
+\fBdbd\fR(1)\&.
diff --git a/man/man8/afpd.8.tmpl b/man/man8/afpd.8.tmpl
deleted file mode 100644 (file)
index 3a91392..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-'\" t
-.\"     Title: afpd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPD" "8" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afpd \- Apple Filing Protocol daemon
-.SH "SYNOPSIS"
-.HP \w'\fBafpd\fR\fB\fR\fBafpd\fR\fB\fR\ 'u
-\fBafpd\fR\fB\fR [\-d] [\-F\ \fIconfigfile\fR]
-.br
-\fBafpd\fR\fB\fR \-v | \-V | \-h 
-.SH "DESCRIPTION"
-.PP
-\fBafpd\fR
-provides an Apple Filing Protocol (AFP) interface to the Unix file system\&. It is normally started at boot time by
-\fBnetatalk\fR(8)\&.
-.PP
-:ETCDIR:/afp\&.conf
-is the configuration file used by
-\fBafpd\fR
-to determine the behavior and configuration of a file server\&.
-.SH "OPTIONS"
-.PP
-\-d
-.RS 4
-Specifies that the daemon should not fork\&.
-.RE
-.PP
-\-v
-.RS 4
-Print version information and exit\&.
-.RE
-.PP
-\-V
-.RS 4
-Print verbose information and exit\&.
-.RE
-.PP
-\-h
-.RS 4
-Print help and exit\&.
-.RE
-.PP
-\-F \fIconfigfile\fR
-.RS 4
-Specifies the configuration file to use\&. (Defaults to
-:ETCDIR:/afp\&.conf\&.)
-.RE
-.SH "SIGNALS"
-.PP
-To shut down a user\'s
-\fBafpd\fR
-process it is recommended that
-\fBSIGKILL (\-9)\fR
-\fINOT\fR
-be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
-\fBafpd\fR
-is to send it a
-\fBSIGTERM (\-15)\fR
-signal and wait for it to die on its own\&.
-.PP
-SIGTERM and SIGUSR1 signals that are sent to the main
-\fBafpd\fR
-process are propagated to the children, so all will be affected\&.
-.PP
-SIGTERM
-.RS 4
-Clean exit\&. Propagates from master to childs\&.
-.RE
-.PP
-SIGQUIT
-.RS 4
-Send this to the master
-\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
-.RE
-.PP
-SIGHUP
-.RS 4
-Sending a
-\fBSIGHUP\fR
-to afpd will cause it to reload its configuration files\&.
-.RE
-.PP
-SIGINT
-.RS 4
-Sending a
-\fBSIGINT\fR
-to a child
-\fBafpd\fR
-enables
-\fImax_debug\fR
-logging for this process\&. The log is sent to fhe file
-/tmp/afpd\&.PID\&.XXXXXX\&. Sending another
-\fBSIGINT\fR
-will revert to the original log settings\&.
-.RE
-.PP
-SIGUSR1
-.RS 4
-The
-\fBafpd\fR
-process will send the message "The server is going down for maintenance\&." to the client and shut itself down in 5 minutes\&. New connections are not allowed\&. If this is sent to a child afpd, the other children are not affected\&. However, the main process will still exit, disabling all new connections\&.
-.RE
-.PP
-SIGUSR2
-.RS 4
-The
-\fBafpd\fR
-process will look in the message directory configured at build time for a file named message\&.pid\&. For each one found, a the contents will be sent as a message to the associated AFP client\&. The file is removed after the message is sent\&. This should only be sent to a child
-\fBafpd\fR\&.
-.RE
-.SH "FILES"
-.PP
-:ETCDIR:/afp\&.conf
-.RS 4
-configuration file used by afpd
-.RE
-.PP
-:STATEDIR:/netatalk/afp_signature\&.conf
-.RS 4
-list of server signature
-.RE
-.PP
-:STATEDIR:/netatalk/afp_voluuid\&.conf
-.RS 4
-list of UUID for Time Machine volume
-.RE
-.PP
-:ETCDIR:/msg/message\&.pid
-.RS 4
-contains messages to be sent to users\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBnetatalk\fR(8),
-\fBhosts_access\fR(5),
-\fBafp.conf\fR(5),
-\fBafp_signature.conf\fR(5),
-\fBafp_voluuid.conf\fR(5),
-\fBdbd\fR(1)\&.
diff --git a/man/man8/cnid_dbd.8.in b/man/man8/cnid_dbd.8.in
new file mode 100644 (file)
index 0000000..3b5b68a
--- /dev/null
@@ -0,0 +1,249 @@
+'\" t
+.\"     Title: cnid_dbd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 01 Jan 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "CNID_DBD" "8" "01 Jan 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+cnid_dbd \- implement access to CNID databases through a dedicated daemon process
+.SH "SYNOPSIS"
+.HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\fBcnid_dbd\fR\fB\fR\ 'u
+\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR
+.br
+\fBcnid_dbd\fR\fB\fR \-v | \-V 
+.SH "DESCRIPTION"
+.PP
+\fBcnid_dbd\fR
+provides an interface for storage and retrieval of catalog node IDs (CNIDs) and related information to the
+\fBafpd\fR
+daemon\&. CNIDs are a component of Macintosh based file systems with semantics that map not easily onto Unix file systems\&. This makes separate storage in a database necessary\&.
+\fBcnid_dbd\fR
+is part of the
+\fBCNID backend\fR
+framework of
+\fBafpd\fR
+and implements the
+\fBdbd\fR
+backend\&.
+.PP
+\fBcnid_dbd\fR
+is never started via the command line or system startup scripts but only by the
+\fBcnid_metad\fR
+daemon\&. There is one instance of
+\fBcnid_dbd\fR
+per netatalk volume\&.
+.PP
+\fBcnid_dbd\fR
+uses the
+\fBBerkeley DB\fR
+database library and uses transactionally protected updates\&. The
+\fBdbd\fR
+backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
+.PP
+\fBcnid_dbd\fR
+inherits the effective userid and groupid from
+\fBcnid_metad\fR
+on startup, which is normally caused by
+\fBafpd\fR
+serving a netatalk volume to a client\&. It changes to the
+\fBBerkeley DB\fR
+database home directory
+\fIdbdir\fR
+that is associated with the volume\&. If the userid inherited from
+\fBcnid_metad\fR
+is 0 (root),
+\fBcnid_dbd\fR
+will change userid and groupid to the owner and group of the database home directory\&. Otherwise, it will continue to use the inherited values\&.
+\fBcnid_dbd\fR
+will then attempt to open the database and start serving requests using filedescriptor
+\fIclntfd\fR\&. Subsequent instances of
+\fBafpd\fR
+that want to access the same volume are redirected to the running
+\fBcnid_dbd\fR
+process by
+\fBcnid_metad\fR
+via the filedescriptor
+\fIctrlfd\fR\&.
+.PP
+\fBcnid_dbd\fR
+can be configured to run forever or to exit after a period of inactivity\&. If
+\fBcnid_dbd\fR
+receives a TERM or an INT signal it will exit cleanly after flushing dirty database buffers to disk and closing
+\fBBerkeley DB\fR
+database environments\&. It is safe to terminate
+\fBcnid_dbd\fR
+this way, it will be restarted when necessary\&. Other signals are not handled and will cause an immediate exit, possibly leaving the CNID database in an inconsistent state (no transactions) or losing recent updates during recovery (transactions)\&.
+.PP
+The
+\fBBerkeley DB\fR
+database subsystem will create files named log\&.xxxxxxxxxx in the database home directory
+\fIdbdir\fR, where xxxxxxxxxx is a monotonically increasing integer\&. These files contain the transactional database changes\&. They will be removed regularly, unless the
+\fBlogfile_autoremove\fR
+option is specified in the
+\fIdb_param\fR
+configuration file (see below) with a value of 0 (default 1)\&.
+.SH "OPTIONS"
+.PP
+\fB\-v, \-V\fR
+.RS 4
+Show version and exit\&.
+.RE
+.SH "CONFIGURATION"
+.PP
+\fBcnid_dbd\fR
+reads configuration information from the file
+\fIdb_param\fR
+in the database directory
+\fIdbdir\fR
+on startup\&. If the file does not exist or a parameter is not listed, suitable default values are used\&. The format for a single parameter is the parameter name, followed by one or more spaces, followed by the parameter value, followed by a newline\&. The following parameters are currently recognized:
+.PP
+\fBlogfile_autoremove\fR
+.RS 4
+If set to 0, unused Berkeley DB transactional logfiles (log\&.xxxxxxxxxx in the database home directory) are not removed on startup of
+\fBcnid_dbd\fR
+and on a regular basis\&. Default: 1\&.
+.RE
+.PP
+\fBcachesize\fR
+.RS 4
+Determines the size of the Berkeley DB cache in kilobytes\&. Default: 8192\&. Each
+\fBcnid_dbd\fR
+process grabs that much memory on top of its normal memory footprint\&. It can be used to tune database performance\&. The
+\fBdb_stat\fR
+utility with the
+\fB\-m\fR
+option that comes with Berkley DB can help you determine ether you need to change this value\&. The default is pretty conservative so that a large percentage of requests should be satisfied from the cache directly\&. If memory is not a bottleneck on your system you might want to leave it at that value\&. The
+\fBBerkeley DB Tutorial and Reference Guide\fR
+has a section
+\fBSelecting a cache size\fR
+that gives more detailed information\&.
+.RE
+.PP
+\fBflush_frequency\fR, \fBflush_interval\fR
+.RS 4
+\fIflush_frequency\fR
+(Default: 1000) and
+\fIflush_interval\fR
+(Default: 1800) control how often changes to the database are checkpointed\&. Both of these operations are performed if either i) more than
+\fIflush_frequency\fR
+requests have been received or ii) more than
+\fIflush_interval\fR
+seconds have elapsed since the last save/checkpoint\&. Be careful to check your harddisk configuration for on disk cache settings\&. Many IDE disks just cache writes as the default behaviour, so even flushing database files to disk will not have the desired effect\&.
+.RE
+.PP
+\fBfd_table_size\fR
+.RS 4
+is the maximum number of connections (filedescriptors) that can be open for
+\fBafpd\fR
+client processes in
+\fBcnid_dbd\&.\fR
+Default: 512\&. If this number is exceeded, one of the existing connections is closed and reused\&. The affected
+\fBafpd\fR
+process will transparently reconnect later, which causes slight overhead\&. On the other hand, setting this parameter too high could affect performance in
+\fBcnid_dbd\fR
+since all descriptors have to be checked in a
+\fBselect()\fR
+system call, or worse, you might exceed the per process limit of open file descriptors on your system\&. It is safe to set the value to 1 on volumes where only one
+\fBafpd\fR
+client process is expected to run, e\&.g\&. home directories\&.
+.RE
+.PP
+\fBidle_timeout\fR
+.RS 4
+is the number of seconds of inactivity before an idle
+\fBcnid_dbd\fR
+exits\&. Default: 600\&. Set this to 0 to disable the timeout\&.
+.RE
+.SH "UPDATING"
+.PP
+Note that the first version to appear
+\fIafter\fR
+Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\*(Aqt upgrade a 2\&.0\&.x version because that one didn\*(Aqt prepare the database\&.
+.PP
+In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Stop the to be upgraded old version of Netatalk
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Using the old BerkeleyDB utilities run
+\fBdb_recover \-h <path to \&.AppleDB>\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Using the new BerkeleyDB utilities run
+\fBdb_upgrade \-v \-h <path to \&.AppleDB> \-f cnid2\&.db\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Again using the new BerkeleyDB utilities run
+\fBdb_checkpoint \-1 \-h <path to \&.AppleDB>\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Start the the new version of Netatalk
+.RE
+.SH "SEE ALSO"
+.PP
+\fBcnid_metad\fR(8),
+\fBafpd\fR(8),
+\fBdbd\fR(1)
diff --git a/man/man8/cnid_dbd.8.tmpl b/man/man8/cnid_dbd.8.tmpl
deleted file mode 100644 (file)
index 347921e..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-'\" t
-.\"     Title: cnid_dbd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 01 Jan 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "CNID_DBD" "8" "01 Jan 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-cnid_dbd \- implement access to CNID databases through a dedicated daemon process
-.SH "SYNOPSIS"
-.HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\fBcnid_dbd\fR\fB\fR\ 'u
-\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR
-.br
-\fBcnid_dbd\fR\fB\fR \-v | \-V 
-.SH "DESCRIPTION"
-.PP
-\fBcnid_dbd\fR
-provides an interface for storage and retrieval of catalog node IDs (CNIDs) and related information to the
-\fBafpd\fR
-daemon\&. CNIDs are a component of Macintosh based file systems with semantics that map not easily onto Unix file systems\&. This makes separate storage in a database necessary\&.
-\fBcnid_dbd\fR
-is part of the
-\fBCNID backend\fR
-framework of
-\fBafpd\fR
-and implements the
-\fBdbd\fR
-backend\&.
-.PP
-\fBcnid_dbd\fR
-is never started via the command line or system startup scripts but only by the
-\fBcnid_metad\fR
-daemon\&. There is one instance of
-\fBcnid_dbd\fR
-per netatalk volume\&.
-.PP
-\fBcnid_dbd\fR
-uses the
-\fBBerkleley DB\fR
-database library and uses transactionally protected updates\&. The
-\fBdbd\fR
-backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
-.PP
-\fBcnid_dbd\fR
-inherits the effective userid and groupid from
-\fBcnid_metad\fR
-on startup, which is normally caused by
-\fBafpd\fR
-serving a netatalk volume to a client\&. It changes to the
-\fBBerkleley DB\fR
-database home directory
-\fIdbdir\fR
-that is associated with the volume\&. If the userid inherited from
-\fBcnid_metad\fR
-is 0 (root),
-\fBcnid_dbd\fR
-will change userid and groupid to the owner and group of the database home directory\&. Otherwise, it will continue to use the inherited values\&.
-\fBcnid_dbd\fR
-will then attempt to open the database and start serving requests using filedescriptor
-\fIclntfd\fR\&. Subsequent instances of
-\fBafpd\fR
-that want to access the same volume are redirected to the running
-\fBcnid_dbd\fR
-process by
-\fBcnid_metad\fR
-via the filedescriptor
-\fIctrlfd\fR\&.
-.PP
-\fBcnid_dbd\fR
-can be configured to run forever or to exit after a period of inactivity\&. If
-\fBcnid_dbd\fR
-receives a TERM or an INT signal it will exit cleanly after flushing dirty database buffers to disk and closing
-\fBBerkleley DB\fR
-database environments\&. It is safe to terminate
-\fBcnid_dbd\fR
-this way, it will be restarted when necessary\&. Other signals are not handled and will cause an immediate exit, possibly leaving the CNID database in an inconsistent state (no transactions) or losing recent updates during recovery (transactions)\&.
-.PP
-The
-\fBBerkleley DB\fR
-database subsystem will create files named log\&.xxxxxxxxxx in the database home directory
-\fIdbdir\fR, where xxxxxxxxxx is a monotonically increasing integer\&. These files contain ithe transactional database changes\&. They will be removed regularily, unless the
-\fBlogfile_autoremove\fR
-option is specified in the
-\fIdb_param\fR
-configuration file (see below) with a value of 0 (default 1)\&.
-.PP
-Do not use
-\fBcnid_dbd\fR
-for databases on NFS mounted file systems\&. It makes the whole point of securing database changes properly moot\&. Use the dbdir: Option in the appropriate
-\fBAppleVolumes\fR
-configuration file to put the database onto a local disk\&.
-.SH "OPTIONS"
-.PP
-\fB\-v, \-V\fR
-.RS 4
-Show version and exit\&.
-.RE
-.SH "CONFIGURATION"
-.PP
-\fBcnid_dbd\fR
-reads configuration information from the file
-\fIdb_param\fR
-in the database directory
-\fIdbdir\fR
-on startup\&. If the file does not exist or a parameter is not listed, suitable default values are used\&. The format for a single parameter is the parameter name, followed by one or more spaces, followed by the parameter value, followed by a newline\&. The following parameters are currently recognized:
-.PP
-\fBlogfile_autoremove\fR
-.RS 4
-If set to 0, unused Berkeley DB transactional logfiles (log\&.xxxxxxxxxx in the database home directory) are not removed on startup of
-\fBcnid_dbd\fR
-and on a reqular basis\&. Default: 1\&.
-.RE
-.PP
-\fBcachesize\fR
-.RS 4
-Determines the size of the Berkeley DB cache in kilobytes\&. Default: 8192\&. Each
-\fBcnid_dbd\fR
-process grabs that much memory on top of its normal memory footprint\&. It can be used to tune database performance\&. The
-\fBdb_stat\fR
-utility with the
-\fB\-m\fR
-option that comes with Berkely DB can help you determine wether you need to change this value\&. The default is pretty conservative so that a large percentage of requests should be satisfied from the cache directly\&. If memory is not a bottleneck on your system you might want to leave it at that value\&. The
-\fBBerkeley DB Tutorial and Reference Guide\fR
-has a section
-\fBSelecting a cache size\fR
-that gives more detailed information\&.
-.RE
-.PP
-\fBflush_frequency\fR, \fBflush_interval\fR
-.RS 4
-\fIflush_frequency\fR
-(Default: 1000) and
-\fIflush_interval\fR
-(Default: 1800) control how often changes to the database are checkpointed\&. Both of these operations are performed if either i) more than
-\fIflush_frequency\fR
-requests have been received or ii) more than
-\fIflush_interval\fR
-seconds have elapsed since the last save/checkpoint\&. Be careful to check your harddisk configuration for on disk cache settings\&. Many IDE disks just cache writes as the default behaviour, so even flushing database files to disk will not have the desired effect\&.
-.RE
-.PP
-\fBfd_table_size\fR
-.RS 4
-is the maximum number of connections (filedescriptors) that can be open for
-\fBafpd\fR
-client processes in
-\fBcnid_dbd\&.\fR
-Default: 512\&. If this number is exceeded, one of the existing connections is closed and reused\&. The affected
-\fBafpd\fR
-process will transparently reconnect later, which causes slight overhead\&. On the other hand, setting this parameter too high could affect performance in
-\fBcnid_dbd\fR
-since all descriptors have to be checked in a
-\fBselect()\fR
-system call, or worse, you might exceed the per process limit of open file descriptors on your system\&. It is safe to set the value to 1 on volumes where only one
-\fBafpd\fR
-client process is expected to run, e\&.g\&. home directories\&.
-.RE
-.PP
-\fBidle_timeout\fR
-.RS 4
-is the number of seconds of inactivity before an idle
-\fBcnid_dbd\fR
-exits\&. Default: 600\&. Set this to 0 to disable the timeout\&.
-.RE
-.SH "UPDATING"
-.PP
-Note that the first version to appear
-\fIafter\fR
-Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
-.PP
-In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Stop the to be upgraded old version of Netatalk
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Using the old BerkeleyDB utilities run
-\fBdb_recover \-h <path to \&.AppleDB>\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Using the new BerkeleyDB utilities run
-\fBdb_upgrade \-v \-h <path to \&.AppleDB> \-f cnid2\&.db\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Again using the new BerkeleyDB utilities run
-\fBdb_checkpoint \-1 \-h <path to \&.AppleDB>\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Start the the new version of Netatalk
-.RE
-.SH "SEE ALSO"
-.PP
-\fBcnid_metad\fR(8),
-\fBafpd\fR(8),
-\fBdbd\fR(1)
diff --git a/man/man8/cnid_metad.8.in b/man/man8/cnid_metad.8.in
new file mode 100644 (file)
index 0000000..0a0d559
--- /dev/null
@@ -0,0 +1,90 @@
+'\" t
+.\"     Title: cnid_metad
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "CNID_METAD" "8" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+cnid_metad \- start cnid_dbd daemons on request
+.SH "SYNOPSIS"
+.HP \w'\fBcnid_metad\fR\fB\fR\fBcnid_metad\fR\fB\fR\ 'u
+\fBcnid_metad\fR\fB\fR [\-d] [\-F\ \fIconfiguration\ file\fR]
+.br
+\fBcnid_metad\fR\fB\fR \-v | \-V 
+.SH "DESCRIPTION"
+.PP
+\fBcnid_metad\fR
+waits for requests from
+\fBafpd\fR
+to start up instances of the
+\fBcnid_dbd\fR
+daemon\&. It keeps track of the status of a
+\fBcnid_dbd\fR
+instance once started and will restart it if necessary\&.
+\fBcnid_metad\fR
+is normally started at boot time by
+\fBnetatalk\fR(8) and runs until shutdown\&.
+.SH "OPTIONS"
+.PP
+\fB\-d\fR
+.RS 4
+\fBcnid_metad will remain in the foreground and\fR
+will also leave the standard input, standard output and standard error file descriptors open\&. Useful for debugging\&.
+.RE
+.PP
+\fB\-F\fR \fIconfiguration file\fR
+.RS 4
+Use
+\fIconfiguration file\fR
+as the configuration file\&. The default is
+\fI@pkgconfdir@/afp\&.conf\fR\&.
+.RE
+.PP
+\fB\-v, \-V\fR
+.RS 4
+Show version and exit\&.
+.RE
+.SH "CAVEATS"
+.PP
+\fBcnid_metad\fR
+does not block or catch any signals apart from SIGPIPE\&. It will therefore exit on most signals received\&. This will also cause all instances of
+\fBcnid_dbd\*(Aqs\fR
+started by that
+\fBcnid_metad\fR
+to exit gracefully\&. Since state about and IPC access to the subprocesses is only maintained in memory by
+\fBcnid_metad\fR
+this is desired behaviour\&. As soon as
+\fBcnid_metad\fR
+is restarted
+\fBafpd\fR
+processes will transparently reconnect\&.
+.SH "SEE ALSO"
+.PP
+\fBnetatalk\fR(8),
+\fBcnid_dbd\fR(8),
+\fBafpd\fR(8),
+\fBdbd\fR(1),
+\fBafp.conf\fR(5)
diff --git a/man/man8/cnid_metad.8.tmpl b/man/man8/cnid_metad.8.tmpl
deleted file mode 100644 (file)
index f96778f..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-'\" t
-.\"     Title: cnid_metad
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "CNID_METAD" "8" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-cnid_metad \- start cnid_dbd daemons on request
-.SH "SYNOPSIS"
-.HP \w'\fBcnid_metad\fR\fB\fR\fBcnid_metad\fR\fB\fR\ 'u
-\fBcnid_metad\fR\fB\fR [\-d] [\-F\ \fIconfiguration\ file\fR]
-.br
-\fBcnid_metad\fR\fB\fR \-v | \-V 
-.SH "DESCRIPTION"
-.PP
-\fBcnid_metad\fR
-waits for requests from
-\fBafpd\fR
-to start up instances of the
-\fBcnid_dbd\fR
-daemon\&. It keeps track of the status of a
-\fBcnid_dbd\fR
-instance once started and will restart it if necessary\&.
-\fBcnid_metad\fR
-is normally started at boot time by
-\fBnetatalk\fR(8) and runs until shutdown\&.
-.SH "OPTIONS"
-.PP
-\fB\-d\fR
-.RS 4
-\fBcnid_metad will remain in the foreground and\fR
-will also leave the standard input, standard output and standard error file descriptors open\&. Useful for debugging\&.
-.RE
-.PP
-\fB\-F\fR \fIconfiguration file\fR
-.RS 4
-Use
-\fIconfiguration file\fR
-as the configuration file\&. The default is
-\fI:ETCDIR:/afp\&.conf\fR\&.
-.RE
-.PP
-\fB\-v, \-V\fR
-.RS 4
-Show version and exit\&.
-.RE
-.SH "CAVEATS"
-.PP
-\fBcnid_metad\fR
-does not block or catch any signals apart from SIGPIPE\&. It will therefore exit on most signals received\&. This will also cause all instances of
-\fBcnid_dbd\'s\fR
-started by that
-\fBcnid_metad\fR
-to exit gracefully\&. Since state about and IPC access to the subprocesses is only maintained in memory by
-\fBcnid_metad\fR
-this is desired behaviour\&. As soon as
-\fBcnid_metad\fR
-is restarted
-\fBafpd\fR
-processes will transparently reconnect\&.
-.SH "SEE ALSO"
-.PP
-\fBnetatalk\fR(8),
-\fBcnid_dbd\fR(8),
-\fBafpd\fR(8),
-\fBdbd\fR(1),
-\fBafp.conf\fR(5)
diff --git a/man/man8/netatalk.8.in b/man/man8/netatalk.8.in
new file mode 100644 (file)
index 0000000..39790ff
--- /dev/null
@@ -0,0 +1,67 @@
+'\" t
+.\"     Title: netatalk
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "NETATALK" "8" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+netatalk \- Netatalk AFP server service controller daemon
+.SH "SYNOPSIS"
+.HP \w'\fBnetatalk\fR\fB\fR\fBnetatalk\fR\fB\fR\ 'u
+\fBnetatalk\fR\fB\fR
+.br
+\fBnetatalk\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+\fBnetatalk\fR
+is the service controller daemon responsible for starting and restarting the AFP daemon
+\fBafpd\fR
+and the CNID daemon
+\fBcnid_metad\fR\&. It is normally started at boot time from /etc/rc\&.
+.SH "SIGNALS"
+.PP
+SIGTERM
+.RS 4
+Stop Netatalk service, AFP and CNID daemons
+.RE
+.PP
+SIGHUP
+.RS 4
+Sending a
+\fBSIGHUP\fR
+will cause the AFP daemon reload its configuration file\&.
+.RE
+.SH "FILES"
+.PP
+@pkgconfdir@/afp\&.conf
+.RS 4
+configuration file used by afpd and cnid_metad
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBcnid_metad\fR(8),
+\fBafp.conf\fR(5)\&.
diff --git a/man/man8/netatalk.8.tmpl b/man/man8/netatalk.8.tmpl
deleted file mode 100644 (file)
index 61cfffe..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-'\" t
-.\"     Title: netatalk
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "NETATALK" "8" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-netatalk \- Netatalk AFP server service controller daemon
-.SH "SYNOPSIS"
-.HP \w'\fBnetatalk\fR\fB\fR\fBnetatalk\fR\fB\fR\ 'u
-\fBnetatalk\fR\fB\fR
-.br
-\fBnetatalk\fR\fB\fR
-.SH "DESCRIPTION"
-.PP
-\fBnetatalk\fR
-is the service controller daemon responsible for starting and restarting the AFP daemon
-\fBafpd\fR
-and the CNID daemon
-\fBcnid_metad\fR\&. It is normally started at boot time from /etc/rc\&.
-.SH "SIGNALS"
-.PP
-SIGTERM
-.RS 4
-Stop Netatalk service, AFP and CNID daemons
-.RE
-.PP
-SIGQUIT
-.RS 4
-Restart AFP and CNID master daemons, but keep all session AFP processes running\&. Can be used to implement
-AFP service without downtime\&.
-.RE
-.PP
-SIGHUP
-.RS 4
-Sending a
-\fBSIGHUP\fR
-will cause the AFP daemon reload its configuration file\&.
-.RE
-.SH "FILES"
-.PP
-:ETCDIR:/afp\&.conf
-.RS 4
-configuration file used by afpd and cnid_metad
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBcnid_metad\fR(8),
-\fBafp.conf\fR(5)\&.
index 033c9e1d3784c3b4c0651100d0243c2aa02bf84f..f5109b8dd3f0658fa192e4c6f5cef851174335d4 100644 (file)
@@ -31,7 +31,6 @@ test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
                                $(top_srcdir)/etc/afpd/file.c \
                                $(top_srcdir)/etc/afpd/filedir.c \
                                $(top_srcdir)/etc/afpd/fork.c \
-                               $(top_srcdir)/etc/afpd/gettok.c \
                                $(top_srcdir)/etc/afpd/hash.c \
                                $(top_srcdir)/etc/afpd/mangle.c \
                                $(top_srcdir)/etc/afpd/messages.c \
@@ -39,6 +38,8 @@ test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
                                $(top_srcdir)/etc/afpd/ofork.c \
                                $(top_srcdir)/etc/afpd/quota.c \
                                $(top_srcdir)/etc/afpd/status.c \
+                               $(top_srcdir)/etc/afpd/spotlight.c \
+                               $(top_srcdir)/etc/afpd/spotlight_marshalling.c \
                                $(top_srcdir)/etc/afpd/switch.c \
                                $(top_srcdir)/etc/afpd/uam.c \
                                $(top_srcdir)/etc/afpd/unix.c \
@@ -62,6 +63,15 @@ test_CFLAGS = \
 
 test_LDADD = \
        $(top_builddir)/libatalk/libatalk.la \
-       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@
+       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
 
 test_LDFLAGS = -export-dynamic
+
+if WITH_DTRACE
+DTRACE_OBJ = test-afp_dsi.o test-fork.o test-appl.o test-catsearch.o test-directory.o test-enumerate.o test-file.o test-filedir.o
+afp_dtrace.o: $(top_srcdir)/include/atalk/afp_dtrace.d $(DTRACE_OBJ)
+       if test -f afp_dtrace.o ; then rm -f afp_dtrace.o ; fi
+       $(LIBTOOL) --mode=execute dtrace -G -s $(top_srcdir)/include/atalk/afp_dtrace.d -o afp_dtrace.o $(DTRACE_OBJ)
+test_LDADD += afp_dtrace.o @DTRACE_LIBS@
+CLEANFILES += afp_dtrace.o
+endif
index 4387bdfca7e852f7cb45e4f943cd352501c69df5..92347f1cf824da1bec05ae7ef4eacc74ad4bcee1 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index 2a0fcd23c43ec6a6ff12acefe613767e6a4a0a35..933f7f61dc85f570dd5ac69bc33f7c45d04ea0c3 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: afpfunc_helpers.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index ee32144a51682eeaa2d0af8358133274406fce7b..2a190eae0acff3b986392b1ae9f7dc711b510732 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index 1731ddc141fc4ad1ec27b9b9dc8541be7d4d5bdc..56a86e81d1037174e49c20d175e13e161179ac5c 100644 (file)
@@ -67,7 +67,7 @@ int main(int argc, char **argv)
     TEST_int( afp_config_parse(&obj, NULL), 0);
     TEST_int( configinit(&obj), 0);
     TEST( cnid_init() );
-    TEST( load_volumes(&obj, NULL) );
+    TEST( load_volumes(&obj, lv_all) );
     TEST_int( dircache_init(8192), 0);
     obj.afp_version = 32;
 
index 382a5bd5bfaaf1502b7626cbdb7192b265f2c05b..98d0c1ba9e7b0691e996e539479f668f247fb450 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify