# PaCkAgE DaTaStReAm git 1 172354 # end of header 07070100055e7c000081a40000000000000000000000014c5c5c81000001090000010000010036ffffffffffffffff0000000c00000000git/pkginfoPKG=git NAME=git 2.5.35 SPARC 64bit Solaris 10 VERSION=2.5.35 PSTAMP=6th August 2010 VENDOR=The Git Project EMAIL=http://git-scm.com/ DESC=Git distributed version control system ARCH=sparc CATEGORY=utility CLASSES=none BASEDIR=/ ISTATES=S s 1 2 3 RSTATES=S s 1 2 3 07070100055e7b000081a40000000000000000000000014c5c5c81000044620000010000010036ffffffffffffffff0000000b00000000git/pkgmap: 1 172354 1 d none /usr ? ? ? 1 d none /usr/local ? ? ? 1 d none /usr/local/bin 0755 root root 1 f none /usr/local/bin/git 0755 root root 10626384 43010 1281121079 1 f none /usr/local/bin/git-cvsserver 0755 root root 120933 53695 1281121081 1 f none /usr/local/bin/git-receive-pack 0755 root root 10626384 43010 1281121080 1 f none /usr/local/bin/git-shell 0755 root root 4145088 14238 1281121081 1 f none /usr/local/bin/git-upload-archive 0755 root root 10626384 43010 1281121081 1 f none /usr/local/bin/git-upload-pack 0755 root root 4258392 36127 1281121080 1 d none /usr/local/lib 0755 root root 1 d none /usr/local/lib/python2.7 0755 root root 1 d none /usr/local/lib/python2.7/site-packages 0755 root root 1 d none /usr/local/lib/python2.7/site-packages/git_remote_helpers 0755 root root 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers-0.1.0-py2.7.egg-info 0644 root root 271 24136 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.py 0644 root root 512 46352 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.pyc 0644 root root 661 54946 1281121083 1 d none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git 0755 root root 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.py 0644 root root 0 0 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.pyc 0644 root root 160 9737 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.py 0644 root root 1629 63362 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyc 0644 root root 2306 14527 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.py 0644 root root 24708 34032 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyc 0644 root root 28807 42580 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.py 0644 root root 1076 16877 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyc 0644 root root 1732 39539 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.py 0644 root root 1899 10290 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyc 0644 root root 2728 18086 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.py 0644 root root 1915 12391 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyc 0644 root root 3072 38839 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/util.py 0644 root root 6333 33126 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyc 0644 root root 8007 31568 1281121083 1 d none /usr/local/lib/site_perl 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git 0755 root root 1 f none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git/.packlist 0644 root root 140 12698 1281121081 1 f none /usr/local/lib/site_perl/Error.pm 0444 root root 19088 24604 1280336623 1 f none /usr/local/lib/site_perl/Git.pm 0444 root root 35938 36881 1280336623 1 d none /usr/local/lib/sun4-solaris-64int 0755 root root 1 f none /usr/local/lib/sun4-solaris-64int/perllocal.pod 0644 root root 208 15008 1281121082 1 d none /usr/local/libexec 0755 root root 1 d none /usr/local/libexec/git-core 0755 root root 1 l none /usr/local/libexec/git-core/git=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-add=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-add--interactive 0755 root root 35717 12137 1281121079 1 f none /usr/local/libexec/git-core/git-am 0755 root root 19578 34161 1281121079 1 l none /usr/local/libexec/git-core/git-annotate=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-apply=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-archimport 0755 root root 36955 38417 1281121079 1 l none /usr/local/libexec/git-core/git-archive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-bisect 0755 root root 10023 34081 1281121079 1 l none /usr/local/libexec/git-core/git-bisect--helper=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-blame=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-branch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-bundle=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cat-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-check-attr=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-check-ref-format=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-checkout=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-checkout-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cherry=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cherry-pick=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-clean=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-clone=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-commit=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-commit-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-config=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-count-objects=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-cvsexportcommit 0755 root root 12731 39197 1281121079 1 f none /usr/local/libexec/git-core/git-cvsimport 0755 root root 29279 34142 1281121079 1 f none /usr/local/libexec/git-core/git-cvsserver 0755 root root 120933 53695 1281121079 1 f none /usr/local/libexec/git-core/git-daemon 0755 root root 4197624 8994 1281121079 1 l none /usr/local/libexec/git-core/git-describe=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-files=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-difftool 0755 root root 2704 6909 1281121079 1 f none /usr/local/libexec/git-core/git-difftool--helper 0755 root root 1747 10379 1281121079 1 l none /usr/local/libexec/git-core/git-fast-export=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-fast-import 0755 root root 4364752 19278 1281121078 1 l none /usr/local/libexec/git-core/git-fetch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fetch-pack=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-filter-branch 0755 root root 12176 38679 1281121079 1 l none /usr/local/libexec/git-core/git-fmt-merge-msg=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-for-each-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-format-patch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fsck=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fsck-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-gc=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-get-tar-commit-id=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-grep=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-hash-object=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-help=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-http-backend 0755 root root 4207000 32211 1281121078 1 f none /usr/local/libexec/git-core/git-http-fetch 0755 root root 4428792 16999 1281121078 1 f none /usr/local/libexec/git-core/git-http-push 0755 root root 4458352 18655 1281121078 1 f none /usr/local/libexec/git-core/git-imap-send 0755 root root 4314896 14343 1281121078 1 l none /usr/local/libexec/git-core/git-index-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-init=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-init-db=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-instaweb 0755 root root 16945 50807 1281121079 1 l none /usr/local/libexec/git-core/git-log=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-lost-found 0755 root root 556 41720 1281121079 1 l none /usr/local/libexec/git-core/git-ls-files=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-ls-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-ls-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mailinfo=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mailsplit=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-base=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-index=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-merge-octopus 0755 root root 2070 30350 1281121079 1 f none /usr/local/libexec/git-core/git-merge-one-file 0755 root root 3816 25261 1281121079 1 l none /usr/local/libexec/git-core/git-merge-ours=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-recursive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-merge-resolve 0755 root root 946 10064 1281121079 1 l none /usr/local/libexec/git-core/git-merge-subtree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-mergetool 0755 root root 6101 61044 1281121079 1 f none /usr/local/libexec/git-core/git-mergetool--lib 0644 root root 9008 1398 1281121079 1 l none /usr/local/libexec/git-core/git-mktag=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mktree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mv=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-name-rev=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-notes=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-redundant=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-refs=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-parse-remote 0644 root root 1943 12275 1281121079 1 l none /usr/local/libexec/git-core/git-patch-id=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-peek-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-prune=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-prune-packed=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-pull 0755 root root 8152 11241 1281121079 1 l none /usr/local/libexec/git-core/git-push=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-quiltimport 0755 root root 3350 62646 1281121079 1 l none /usr/local/libexec/git-core/git-read-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-rebase 0755 root root 14606 58281 1281121079 1 f none /usr/local/libexec/git-core/git-rebase--interactive 0755 root root 25011 21236 1281121079 1 l none /usr/local/libexec/git-core/git-receive-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-reflog=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-relink 0755 root root 4209 11152 1281121079 1 l none /usr/local/libexec/git-core/git-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-remote-ftp=/usr/local/libexec/git-core/git-remote-https 1 l none /usr/local/libexec/git-core/git-remote-ftps=/usr/local/libexec/git-core/git-remote-https 1 l none /usr/local/libexec/git-core/git-remote-http=/usr/local/libexec/git-core/git-remote-https 1 f none /usr/local/libexec/git-core/git-remote-https 0755 root root 4465920 25830 1281121079 1 f none /usr/local/libexec/git-core/git-remote-testgit 0755 root root 5250 24272 1281121079 1 f none /usr/local/libexec/git-core/git-repack 0755 root root 4494 24971 1281121079 1 l none /usr/local/libexec/git-core/git-replace=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-repo-config=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-request-pull 0755 root root 1633 280 1281121079 1 l none /usr/local/libexec/git-core/git-rerere=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-reset=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rev-list=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rev-parse=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-revert=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rm=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-send-email 0755 root root 38057 48970 1281121079 1 l none /usr/local/libexec/git-core/git-send-pack=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-sh-setup 0644 root root 4063 45494 1281121079 1 f none /usr/local/libexec/git-core/git-shell 0755 root root 4145088 14238 1281121078 1 l none /usr/local/libexec/git-core/git-shortlog=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-show=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-show-branch=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-show-index 0755 root root 4145360 28935 1281121078 1 l none /usr/local/libexec/git-core/git-show-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-stage=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-stash 0755 root root 8874 37954 1281121079 1 l none /usr/local/libexec/git-core/git-status=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-stripspace=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-submodule 0755 root root 18139 60578 1281121079 1 f none /usr/local/libexec/git-core/git-svn 0755 root root 176210 59646 1281121079 1 l none /usr/local/libexec/git-core/git-symbolic-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-tag=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-tar-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-unpack-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-unpack-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-server-info=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-upload-archive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-upload-pack 0755 root root 4258392 36127 1281121078 1 l none /usr/local/libexec/git-core/git-var=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-verify-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-verify-tag=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-web--browse 0755 root root 4204 63366 1281121079 1 l none /usr/local/libexec/git-core/git-whatchanged=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-write-tree=/usr/local/bin/git 1 d none /usr/local/share 0755 root root 1 d none /usr/local/share/git-core 0755 root root 1 d none /usr/local/share/git-core/templates 0755 root root 1 d none /usr/local/share/git-core/templates/branches 0755 root root 1 f none /usr/local/share/git-core/templates/description 0644 root root 73 6864 1281121056 1 d none /usr/local/share/git-core/templates/hooks 0755 root root 1 f none /usr/local/share/git-core/templates/hooks/applypatch-msg.sample 0755 root root 454 38933 1281121055 1 f none /usr/local/share/git-core/templates/hooks/commit-msg.sample 0755 root root 898 8540 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-commit.sample 0755 root root 162 13689 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-receive.sample 0755 root root 554 46803 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-update.sample 0755 root root 191 16733 1281121055 1 f none /usr/local/share/git-core/templates/hooks/pre-applypatch.sample 0755 root root 400 34234 1281121055 1 f none /usr/local/share/git-core/templates/hooks/pre-commit.sample 0755 root root 1580 4436 1281121056 1 f none /usr/local/share/git-core/templates/hooks/pre-rebase.sample 0755 root root 4953 2372 1281121056 1 f none /usr/local/share/git-core/templates/hooks/prepare-commit-msg.sample 0755 root root 1241 34727 1281121056 1 f none /usr/local/share/git-core/templates/hooks/update.sample 0755 root root 3613 30856 1281121056 1 d none /usr/local/share/git-core/templates/info 0755 root root 1 f none /usr/local/share/git-core/templates/info/exclude 0644 root root 240 20717 1281121056 1 d none /usr/local/share/gitweb 0755 root root 1 f none /usr/local/share/gitweb/gitweb.cgi 0755 root root 210794 31528 1281121082 1 d none /usr/local/share/gitweb/static 0755 root root 1 f none /usr/local/share/gitweb/static/git-favicon.png 0644 root root 115 7804 1281121082 1 f none /usr/local/share/gitweb/static/git-logo.png 0644 root root 207 20690 1281121082 1 f none /usr/local/share/gitweb/static/gitweb.css 0644 root root 8924 16100 1281121082 1 f none /usr/local/share/gitweb/static/gitweb.js 0644 root root 24142 21086 1281121082 1 d none /usr/local/share/man 0755 root root 1 d none /usr/local/share/man/man3 0755 root root 1 f none /usr/local/share/man/man3/Git.3 0444 root root 25499 6770 1281121055 1 f none /usr/local/share/man/man3/private-Error.3 0444 root root 14408 39919 1281121055 1 i checkinstall 785 2565 1281121408 1 i pkginfo 265 20444 1281121409 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!07070100055e7c000081a40000000000000000000000014c5c5c81000001090000010000010036ffffffffffffffff0000000800000000pkginfoPKG=git NAME=git 2.5.35 SPARC 64bit Solaris 10 VERSION=2.5.35 PSTAMP=6th August 2010 VENDOR=The Git Project EMAIL=http://git-scm.com/ DESC=Git distributed version control system ARCH=sparc CATEGORY=utility CLASSES=none BASEDIR=/ ISTATES=S s 1 2 3 RSTATES=S s 1 2 3 07070100055e7b000081a40000000000000000000000014c5c5c81000044620000010000010036ffffffffffffffff0000000700000000pkgmap: 1 172354 1 d none /usr ? ? ? 1 d none /usr/local ? ? ? 1 d none /usr/local/bin 0755 root root 1 f none /usr/local/bin/git 0755 root root 10626384 43010 1281121079 1 f none /usr/local/bin/git-cvsserver 0755 root root 120933 53695 1281121081 1 f none /usr/local/bin/git-receive-pack 0755 root root 10626384 43010 1281121080 1 f none /usr/local/bin/git-shell 0755 root root 4145088 14238 1281121081 1 f none /usr/local/bin/git-upload-archive 0755 root root 10626384 43010 1281121081 1 f none /usr/local/bin/git-upload-pack 0755 root root 4258392 36127 1281121080 1 d none /usr/local/lib 0755 root root 1 d none /usr/local/lib/python2.7 0755 root root 1 d none /usr/local/lib/python2.7/site-packages 0755 root root 1 d none /usr/local/lib/python2.7/site-packages/git_remote_helpers 0755 root root 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers-0.1.0-py2.7.egg-info 0644 root root 271 24136 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.py 0644 root root 512 46352 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.pyc 0644 root root 661 54946 1281121083 1 d none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git 0755 root root 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.py 0644 root root 0 0 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.pyc 0644 root root 160 9737 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.py 0644 root root 1629 63362 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyc 0644 root root 2306 14527 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.py 0644 root root 24708 34032 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyc 0644 root root 28807 42580 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.py 0644 root root 1076 16877 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyc 0644 root root 1732 39539 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.py 0644 root root 1899 10290 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyc 0644 root root 2728 18086 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.py 0644 root root 1915 12391 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyc 0644 root root 3072 38839 1281121083 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/util.py 0644 root root 6333 33126 1280336623 1 f none /usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyc 0644 root root 8007 31568 1281121083 1 d none /usr/local/lib/site_perl 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto 0755 root root 1 d none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git 0755 root root 1 f none /usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git/.packlist 0644 root root 140 12698 1281121081 1 f none /usr/local/lib/site_perl/Error.pm 0444 root root 19088 24604 1280336623 1 f none /usr/local/lib/site_perl/Git.pm 0444 root root 35938 36881 1280336623 1 d none /usr/local/lib/sun4-solaris-64int 0755 root root 1 f none /usr/local/lib/sun4-solaris-64int/perllocal.pod 0644 root root 208 15008 1281121082 1 d none /usr/local/libexec 0755 root root 1 d none /usr/local/libexec/git-core 0755 root root 1 l none /usr/local/libexec/git-core/git=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-add=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-add--interactive 0755 root root 35717 12137 1281121079 1 f none /usr/local/libexec/git-core/git-am 0755 root root 19578 34161 1281121079 1 l none /usr/local/libexec/git-core/git-annotate=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-apply=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-archimport 0755 root root 36955 38417 1281121079 1 l none /usr/local/libexec/git-core/git-archive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-bisect 0755 root root 10023 34081 1281121079 1 l none /usr/local/libexec/git-core/git-bisect--helper=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-blame=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-branch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-bundle=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cat-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-check-attr=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-check-ref-format=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-checkout=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-checkout-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cherry=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-cherry-pick=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-clean=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-clone=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-commit=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-commit-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-config=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-count-objects=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-cvsexportcommit 0755 root root 12731 39197 1281121079 1 f none /usr/local/libexec/git-core/git-cvsimport 0755 root root 29279 34142 1281121079 1 f none /usr/local/libexec/git-core/git-cvsserver 0755 root root 120933 53695 1281121079 1 f none /usr/local/libexec/git-core/git-daemon 0755 root root 4197624 8994 1281121079 1 l none /usr/local/libexec/git-core/git-describe=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-files=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-diff-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-difftool 0755 root root 2704 6909 1281121079 1 f none /usr/local/libexec/git-core/git-difftool--helper 0755 root root 1747 10379 1281121079 1 l none /usr/local/libexec/git-core/git-fast-export=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-fast-import 0755 root root 4364752 19278 1281121078 1 l none /usr/local/libexec/git-core/git-fetch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fetch-pack=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-filter-branch 0755 root root 12176 38679 1281121079 1 l none /usr/local/libexec/git-core/git-fmt-merge-msg=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-for-each-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-format-patch=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fsck=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-fsck-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-gc=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-get-tar-commit-id=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-grep=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-hash-object=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-help=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-http-backend 0755 root root 4207000 32211 1281121078 1 f none /usr/local/libexec/git-core/git-http-fetch 0755 root root 4428792 16999 1281121078 1 f none /usr/local/libexec/git-core/git-http-push 0755 root root 4458352 18655 1281121078 1 f none /usr/local/libexec/git-core/git-imap-send 0755 root root 4314896 14343 1281121078 1 l none /usr/local/libexec/git-core/git-index-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-init=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-init-db=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-instaweb 0755 root root 16945 50807 1281121079 1 l none /usr/local/libexec/git-core/git-log=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-lost-found 0755 root root 556 41720 1281121079 1 l none /usr/local/libexec/git-core/git-ls-files=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-ls-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-ls-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mailinfo=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mailsplit=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-base=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-index=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-merge-octopus 0755 root root 2070 30350 1281121079 1 f none /usr/local/libexec/git-core/git-merge-one-file 0755 root root 3816 25261 1281121079 1 l none /usr/local/libexec/git-core/git-merge-ours=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-recursive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-merge-resolve 0755 root root 946 10064 1281121079 1 l none /usr/local/libexec/git-core/git-merge-subtree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-merge-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-mergetool 0755 root root 6101 61044 1281121079 1 f none /usr/local/libexec/git-core/git-mergetool--lib 0644 root root 9008 1398 1281121079 1 l none /usr/local/libexec/git-core/git-mktag=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mktree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-mv=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-name-rev=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-notes=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-redundant=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-pack-refs=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-parse-remote 0644 root root 1943 12275 1281121079 1 l none /usr/local/libexec/git-core/git-patch-id=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-peek-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-prune=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-prune-packed=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-pull 0755 root root 8152 11241 1281121079 1 l none /usr/local/libexec/git-core/git-push=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-quiltimport 0755 root root 3350 62646 1281121079 1 l none /usr/local/libexec/git-core/git-read-tree=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-rebase 0755 root root 14606 58281 1281121079 1 f none /usr/local/libexec/git-core/git-rebase--interactive 0755 root root 25011 21236 1281121079 1 l none /usr/local/libexec/git-core/git-receive-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-reflog=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-relink 0755 root root 4209 11152 1281121079 1 l none /usr/local/libexec/git-core/git-remote=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-remote-ftp=/usr/local/libexec/git-core/git-remote-https 1 l none /usr/local/libexec/git-core/git-remote-ftps=/usr/local/libexec/git-core/git-remote-https 1 l none /usr/local/libexec/git-core/git-remote-http=/usr/local/libexec/git-core/git-remote-https 1 f none /usr/local/libexec/git-core/git-remote-https 0755 root root 4465920 25830 1281121079 1 f none /usr/local/libexec/git-core/git-remote-testgit 0755 root root 5250 24272 1281121079 1 f none /usr/local/libexec/git-core/git-repack 0755 root root 4494 24971 1281121079 1 l none /usr/local/libexec/git-core/git-replace=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-repo-config=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-request-pull 0755 root root 1633 280 1281121079 1 l none /usr/local/libexec/git-core/git-rerere=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-reset=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rev-list=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rev-parse=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-revert=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-rm=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-send-email 0755 root root 38057 48970 1281121079 1 l none /usr/local/libexec/git-core/git-send-pack=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-sh-setup 0644 root root 4063 45494 1281121079 1 f none /usr/local/libexec/git-core/git-shell 0755 root root 4145088 14238 1281121078 1 l none /usr/local/libexec/git-core/git-shortlog=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-show=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-show-branch=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-show-index 0755 root root 4145360 28935 1281121078 1 l none /usr/local/libexec/git-core/git-show-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-stage=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-stash 0755 root root 8874 37954 1281121079 1 l none /usr/local/libexec/git-core/git-status=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-stripspace=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-submodule 0755 root root 18139 60578 1281121079 1 f none /usr/local/libexec/git-core/git-svn 0755 root root 176210 59646 1281121079 1 l none /usr/local/libexec/git-core/git-symbolic-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-tag=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-tar-tree=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-unpack-file=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-unpack-objects=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-index=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-ref=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-update-server-info=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-upload-archive=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-upload-pack 0755 root root 4258392 36127 1281121078 1 l none /usr/local/libexec/git-core/git-var=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-verify-pack=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-verify-tag=/usr/local/bin/git 1 f none /usr/local/libexec/git-core/git-web--browse 0755 root root 4204 63366 1281121079 1 l none /usr/local/libexec/git-core/git-whatchanged=/usr/local/bin/git 1 l none /usr/local/libexec/git-core/git-write-tree=/usr/local/bin/git 1 d none /usr/local/share 0755 root root 1 d none /usr/local/share/git-core 0755 root root 1 d none /usr/local/share/git-core/templates 0755 root root 1 d none /usr/local/share/git-core/templates/branches 0755 root root 1 f none /usr/local/share/git-core/templates/description 0644 root root 73 6864 1281121056 1 d none /usr/local/share/git-core/templates/hooks 0755 root root 1 f none /usr/local/share/git-core/templates/hooks/applypatch-msg.sample 0755 root root 454 38933 1281121055 1 f none /usr/local/share/git-core/templates/hooks/commit-msg.sample 0755 root root 898 8540 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-commit.sample 0755 root root 162 13689 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-receive.sample 0755 root root 554 46803 1281121055 1 f none /usr/local/share/git-core/templates/hooks/post-update.sample 0755 root root 191 16733 1281121055 1 f none /usr/local/share/git-core/templates/hooks/pre-applypatch.sample 0755 root root 400 34234 1281121055 1 f none /usr/local/share/git-core/templates/hooks/pre-commit.sample 0755 root root 1580 4436 1281121056 1 f none /usr/local/share/git-core/templates/hooks/pre-rebase.sample 0755 root root 4953 2372 1281121056 1 f none /usr/local/share/git-core/templates/hooks/prepare-commit-msg.sample 0755 root root 1241 34727 1281121056 1 f none /usr/local/share/git-core/templates/hooks/update.sample 0755 root root 3613 30856 1281121056 1 d none /usr/local/share/git-core/templates/info 0755 root root 1 f none /usr/local/share/git-core/templates/info/exclude 0644 root root 240 20717 1281121056 1 d none /usr/local/share/gitweb 0755 root root 1 f none /usr/local/share/gitweb/gitweb.cgi 0755 root root 210794 31528 1281121082 1 d none /usr/local/share/gitweb/static 0755 root root 1 f none /usr/local/share/gitweb/static/git-favicon.png 0644 root root 115 7804 1281121082 1 f none /usr/local/share/gitweb/static/git-logo.png 0644 root root 207 20690 1281121082 1 f none /usr/local/share/gitweb/static/gitweb.css 0644 root root 8924 16100 1281121082 1 f none /usr/local/share/gitweb/static/gitweb.js 0644 root root 24142 21086 1281121082 1 d none /usr/local/share/man 0755 root root 1 d none /usr/local/share/man/man3 0755 root root 1 f none /usr/local/share/man/man3/Git.3 0444 root root 25499 6770 1281121055 1 f none /usr/local/share/man/man3/private-Error.3 0444 root root 14408 39919 1281121055 1 i checkinstall 785 2565 1281121408 1 i pkginfo 265 20444 1281121409 07070100055eef000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000000800000000install07070100055ef0000081ed0000000000000000000000014c5c5c80000003110000010000010036ffffffffffffffff0000001500000000install/checkinstall#!/bin/sh # expected_bits="64" expected_release="5.10" expected_platform="sparc" # release=`uname -r` platform=`uname -p` bits=`isainfo -b` # if [ ${platform} != ${expected_platform} ]; then echo "\n\n\n\tThis package must be installed on a ${expected_platform} architecture\n" echo "\tAborting installation.\n\n\n" exit 1 fi if [ ${release} != ${expected_release} ]; then echo "\n\n\n\tThis package must be installed on a ${expected_release} machine\n" echo "\tAborting installation.\n\n\n" exit 1 fi if [ ${bits} != ${expected_bits} ]; then echo "\n\n\n\tThis package must be installed on a ${expected_bits} bit machine\n" echo "\tYour machine is running a ${bits} bit O.S. currently\n" echo "\tAborting installation.\n\n\n" exit 1 fi exit 0 07070100055e7d000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000000500000000root07070100055e7e000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000000900000000root/usr07070100055e7f000041ed0000000000000000000000064c5c5c83000000000000010000010036ffffffffffffffff0000000f00000000root/usr/local07070100055ed3000041ed0000000000000000000000054c5c5c83000000000000010000010036ffffffffffffffff0000001500000000root/usr/local/share07070100055ee4000041ed0000000000000000000000034c5c5c83000000000000010000010036ffffffffffffffff0000001c00000000root/usr/local/share/gitweb07070100055ee5000081ed0000000000000000000000014c5c5b3a0003376a0000010000010036ffffffffffffffff0000002700000000root/usr/local/share/gitweb/gitweb.cgi#!/usr/bin/perl # gitweb - simple web interface to track changes in git repositories # # (C) 2005-2006, Kay Sievers # (C) 2005, Christian Gierke # # This program is licensed under the GPLv2 use strict; use warnings; use CGI qw(:standard :escapeHTML -nosticky); use CGI::Util qw(unescape); use CGI::Carp qw(fatalsToBrowser set_message); use Encode; use Fcntl ':mode'; use File::Find qw(); use File::Basename qw(basename); binmode STDOUT, ':utf8'; our $t0; if (eval { require Time::HiRes; 1; }) { $t0 = [Time::HiRes::gettimeofday()]; } our $number_of_git_cmds = 0; BEGIN { CGI->compile() if $ENV{'MOD_PERL'}; } our $version = "1.7.2.1"; our ($my_url, $my_uri, $base_url, $path_info, $home_link); sub evaluate_uri { our $cgi; our $my_url = $cgi->url(); our $my_uri = $cgi->url(-absolute => 1); # Base URL for relative URLs in gitweb ($logo, $favicon, ...), # needed and used only for URLs with nonempty PATH_INFO our $base_url = $my_url; # When the script is used as DirectoryIndex, the URL does not contain the name # of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we # have to do it ourselves. We make $path_info global because it's also used # later on. # # Another issue with the script being the DirectoryIndex is that the resulting # $my_url data is not the full script URL: this is good, because we want # generated links to keep implying the script name if it wasn't explicitly # indicated in the URL we're handling, but it means that $my_url cannot be used # as base URL. # Therefore, if we needed to strip PATH_INFO, then we know that we have # to build the base URL ourselves: our $path_info = $ENV{"PATH_INFO"}; if ($path_info) { if ($my_url =~ s,\Q$path_info\E$,, && $my_uri =~ s,\Q$path_info\E$,, && defined $ENV{'SCRIPT_NAME'}) { $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'}; } } # target of the home link on top of all pages our $home_link = $my_uri || "/"; } # core git executable to use # this can just be "git" if your webserver has a sensible PATH our $GIT = "/usr/local/bin/git"; # absolute fs-path which will be prepended to the project path #our $projectroot = "/pub/scm"; our $projectroot = "/pub/git"; # fs traversing limit for getting project list # the number is relative to the projectroot our $project_maxdepth = 2007; # string of the home link on top of all pages our $home_link_str = "projects"; # name of your site or organization to appear in page titles # replace this with something more descriptive for clearer bookmarks our $site_name = "" || ($ENV{'SERVER_NAME'} || "Untitled") . " Git"; # filename of html text to include at top of each page our $site_header = ""; # html text to include at home page our $home_text = "indextext.html"; # filename of html text to include at bottom of each page our $site_footer = ""; # URI of stylesheets our @stylesheets = ("static/gitweb.css"); # URI of a single stylesheet, which can be overridden in GITWEB_CONFIG. our $stylesheet = undef; # URI of GIT logo (72x27 size) our $logo = "static/git-logo.png"; # URI of GIT favicon, assumed to be image/png type our $favicon = "static/git-favicon.png"; # URI of gitweb.js (JavaScript code for gitweb) our $javascript = "static/gitweb.js"; # URI and label (title) of GIT logo link #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/"; #our $logo_label = "git documentation"; our $logo_url = "http://git-scm.com/"; our $logo_label = "git homepage"; # source of projects list our $projects_list = ""; # the width (in characters) of the projects list "Description" column our $projects_list_description_width = 25; # default order of projects list # valid values are none, project, descr, owner, and age our $default_projects_order = "project"; # show repository only if this file exists # (only effective if this variable evaluates to true) our $export_ok = ""; # show repository only if this subroutine returns true # when given the path to the project, for example: # sub { return -e "$_[0]/git-daemon-export-ok"; } our $export_auth_hook = undef; # only allow viewing of repositories also shown on the overview page our $strict_export = ""; # list of git base URLs used for URL to where fetch project from, # i.e. full URL is "$git_base_url/$project" our @git_base_url_list = grep { $_ ne '' } (""); # default blob_plain mimetype and default charset for text/plain blob our $default_blob_plain_mimetype = 'text/plain'; our $default_text_plain_charset = undef; # file to use for guessing MIME types before trying /etc/mime.types # (relative to the current git repository) our $mimetypes_file = undef; # assume this charset if line contains non-UTF-8 characters; # it should be valid encoding (see Encoding::Supported(3pm) for list), # for which encoding all byte sequences are valid, for example # 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it # could be even 'utf-8' for the old behavior) our $fallback_encoding = 'latin1'; # rename detection options for git-diff and git-diff-tree # - default is '-M', with the cost proportional to # (number of removed files) * (number of new files). # - more costly is '-C' (which implies '-M'), with the cost proportional to # (number of changed files + number of removed files) * (number of new files) # - even more costly is '-C', '--find-copies-harder' with cost # (number of files in the original tree) * (number of new files) # - one might want to include '-B' option, e.g. '-B', '-M' our @diff_opts = ('-M'); # taken from git_commit # Disables features that would allow repository owners to inject script into # the gitweb domain. our $prevent_xss = 0; # information about snapshot formats that gitweb is capable of serving our %known_snapshot_formats = ( # name => { # 'display' => display name, # 'type' => mime type, # 'suffix' => filename suffix, # 'format' => --format for git-archive, # 'compressor' => [compressor command and arguments] # (array reference, optional) # 'disabled' => boolean (optional)} # 'tgz' => { 'display' => 'tar.gz', 'type' => 'application/x-gzip', 'suffix' => '.tar.gz', 'format' => 'tar', 'compressor' => ['gzip']}, 'tbz2' => { 'display' => 'tar.bz2', 'type' => 'application/x-bzip2', 'suffix' => '.tar.bz2', 'format' => 'tar', 'compressor' => ['bzip2']}, 'txz' => { 'display' => 'tar.xz', 'type' => 'application/x-xz', 'suffix' => '.tar.xz', 'format' => 'tar', 'compressor' => ['xz'], 'disabled' => 1}, 'zip' => { 'display' => 'zip', 'type' => 'application/x-zip', 'suffix' => '.zip', 'format' => 'zip'}, ); # Aliases so we understand old gitweb.snapshot values in repository # configuration. our %known_snapshot_format_aliases = ( 'gzip' => 'tgz', 'bzip2' => 'tbz2', 'xz' => 'txz', # backward compatibility: legacy gitweb config support 'x-gzip' => undef, 'gz' => undef, 'x-bzip2' => undef, 'bz2' => undef, 'x-zip' => undef, '' => undef, ); # Pixel sizes for icons and avatars. If the default font sizes or lineheights # are changed, it may be appropriate to change these values too via # $GITWEB_CONFIG. our %avatar_size = ( 'default' => 16, 'double' => 32 ); # Used to set the maximum load that we will still respond to gitweb queries. # If server load exceed this value then return "503 server busy" error. # If gitweb cannot determined server load, it is taken to be 0. # Leave it undefined (or set to 'undef') to turn off load checking. our $maxload = 300; # You define site-wide feature defaults here; override them with # $GITWEB_CONFIG as necessary. our %feature = ( # feature => { # 'sub' => feature-sub (subroutine), # 'override' => allow-override (boolean), # 'default' => [ default options...] (array reference)} # # if feature is overridable (it means that allow-override has true value), # then feature-sub will be called with default options as parameters; # return value of feature-sub indicates if to enable specified feature # # if there is no 'sub' key (no feature-sub), then feature cannot be # overriden # # use gitweb_get_feature() to retrieve the value # (an array) or gitweb_check_feature() to check if # is enabled # Enable the 'blame' blob view, showing the last commit that modified # each line in the file. This can be very CPU-intensive. # To enable system wide have in $GITWEB_CONFIG # $feature{'blame'}{'default'} = [1]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'blame'}{'override'} = 1; # and in project config gitweb.blame = 0|1; 'blame' => { 'sub' => sub { feature_bool('blame', @_) }, 'override' => 0, 'default' => [0]}, # Enable the 'snapshot' link, providing a compressed archive of any # tree. This can potentially generate high traffic if you have large # project. # Value is a list of formats defined in %known_snapshot_formats that # you wish to offer. # To disable system wide have in $GITWEB_CONFIG # $feature{'snapshot'}{'default'} = []; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'snapshot'}{'override'} = 1; # and in project config, a comma-separated list of formats or "none" # to disable. Example: gitweb.snapshot = tbz2,zip; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, 'default' => ['tgz']}, # Enable text search, which will list the commits which match author, # committer or commit text to a given string. Enabled by default. # Project specific override is not supported. 'search' => { 'override' => 0, 'default' => [1]}, # Enable grep search, which will list the files in currently selected # tree containing the given string. Enabled by default. This can be # potentially CPU-intensive, of course. # To enable system wide have in $GITWEB_CONFIG # $feature{'grep'}{'default'} = [1]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'grep'}{'override'} = 1; # and in project config gitweb.grep = 0|1; 'grep' => { 'sub' => sub { feature_bool('grep', @_) }, 'override' => 0, 'default' => [1]}, # Enable the pickaxe search, which will list the commits that modified # a given string in a file. This can be practical and quite faster # alternative to 'blame', but still potentially CPU-intensive. # To enable system wide have in $GITWEB_CONFIG # $feature{'pickaxe'}{'default'} = [1]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'pickaxe'}{'override'} = 1; # and in project config gitweb.pickaxe = 0|1; 'pickaxe' => { 'sub' => sub { feature_bool('pickaxe', @_) }, 'override' => 0, 'default' => [1]}, # Enable showing size of blobs in a 'tree' view, in a separate # column, similar to what 'ls -l' does. This cost a bit of IO. # To disable system wide have in $GITWEB_CONFIG # $feature{'show-sizes'}{'default'} = [0]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'show-sizes'}{'override'} = 1; # and in project config gitweb.showsizes = 0|1; 'show-sizes' => { 'sub' => sub { feature_bool('showsizes', @_) }, 'override' => 0, 'default' => [1]}, # Make gitweb use an alternative format of the URLs which can be # more readable and natural-looking: project name is embedded # directly in the path and the query string contains other # auxiliary information. All gitweb installations recognize # URL in either format; this configures in which formats gitweb # generates links. # To enable system wide have in $GITWEB_CONFIG # $feature{'pathinfo'}{'default'} = [1]; # Project specific override is not supported. # Note that you will need to change the default location of CSS, # favicon, logo and possibly other files to an absolute URL. Also, # if gitweb.cgi serves as your indexfile, you will need to force # $my_uri to contain the script name in your $GITWEB_CONFIG. 'pathinfo' => { 'override' => 0, 'default' => [0]}, # Make gitweb consider projects in project root subdirectories # to be forks of existing projects. Given project $projname.git, # projects matching $projname/*.git will not be shown in the main # projects list, instead a '+' mark will be added to $projname # there and a 'forks' view will be enabled for the project, listing # all the forks. If project list is taken from a file, forks have # to be listed after the main project. # To enable system wide have in $GITWEB_CONFIG # $feature{'forks'}{'default'} = [1]; # Project specific override is not supported. 'forks' => { 'override' => 0, 'default' => [0]}, # Insert custom links to the action bar of all project pages. # This enables you mainly to link to third-party scripts integrating # into gitweb; e.g. git-browser for graphical history representation # or custom web-based repository administration interface. # The 'default' value consists of a list of triplets in the form # (label, link, position) where position is the label after which # to insert the link and link is a format string where %n expands # to the project name, %f to the project path within the filesystem, # %h to the current hash (h gitweb parameter) and %b to the current # hash base (hb gitweb parameter); %% expands to %. # To enable system wide have in $GITWEB_CONFIG e.g. # $feature{'actions'}{'default'} = [('graphiclog', # '/git-browser/by-commit.html?r=%n', 'summary')]; # Project specific override is not supported. 'actions' => { 'override' => 0, 'default' => []}, # Allow gitweb scan project content tags described in ctags/ # of project repository, and display the popular Web 2.0-ish # "tag cloud" near the project list. Note that this is something # COMPLETELY different from the normal Git tags. # gitweb by itself can show existing tags, but it does not handle # tagging itself; you need an external application for that. # For an example script, check Girocco's cgi/tagproj.cgi. # You may want to install the HTML::TagCloud Perl module to get # a pretty tag cloud instead of just a list of tags. # To enable system wide have in $GITWEB_CONFIG # $feature{'ctags'}{'default'} = ['path_to_tag_script']; # Project specific override is not supported. 'ctags' => { 'override' => 0, 'default' => [0]}, # The maximum number of patches in a patchset generated in patch # view. Set this to 0 or undef to disable patch view, or to a # negative number to remove any limit. # To disable system wide have in $GITWEB_CONFIG # $feature{'patches'}{'default'} = [0]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'patches'}{'override'} = 1; # and in project config gitweb.patches = 0|n; # where n is the maximum number of patches allowed in a patchset. 'patches' => { 'sub' => \&feature_patches, 'override' => 0, 'default' => [16]}, # Avatar support. When this feature is enabled, views such as # shortlog or commit will display an avatar associated with # the email of the committer(s) and/or author(s). # Currently available providers are gravatar and picon. # If an unknown provider is specified, the feature is disabled. # Gravatar depends on Digest::MD5. # Picon currently relies on the indiana.edu database. # To enable system wide have in $GITWEB_CONFIG # $feature{'avatar'}{'default'} = ['']; # where is either gravatar or picon. # To have project specific config enable override in $GITWEB_CONFIG # $feature{'avatar'}{'override'} = 1; # and in project config gitweb.avatar = ; 'avatar' => { 'sub' => \&feature_avatar, 'override' => 0, 'default' => ['']}, # Enable displaying how much time and how many git commands # it took to generate and display page. Disabled by default. # Project specific override is not supported. 'timed' => { 'override' => 0, 'default' => [0]}, # Enable turning some links into links to actions which require # JavaScript to run (like 'blame_incremental'). Not enabled by # default. Project specific override is currently not supported. 'javascript-actions' => { 'override' => 0, 'default' => [0]}, # Syntax highlighting support. This is based on Daniel Svensson's # and Sham Chukoury's work in gitweb-xmms2.git. # It requires the 'highlight' program present in $PATH, # and therefore is disabled by default. # To enable system wide have in $GITWEB_CONFIG # $feature{'highlight'}{'default'} = [1]; 'highlight' => { 'sub' => sub { feature_bool('highlight', @_) }, 'override' => 0, 'default' => [0]}, ); sub gitweb_get_feature { my ($name) = @_; return unless exists $feature{$name}; my ($sub, $override, @defaults) = ( $feature{$name}{'sub'}, $feature{$name}{'override'}, @{$feature{$name}{'default'}}); # project specific override is possible only if we have project our $git_dir; # global variable, declared later if (!$override || !defined $git_dir) { return @defaults; } if (!defined $sub) { warn "feature $name is not overridable"; return @defaults; } return $sub->(@defaults); } # A wrapper to check if a given feature is enabled. # With this, you can say # # my $bool_feat = gitweb_check_feature('bool_feat'); # gitweb_check_feature('bool_feat') or somecode; # # instead of # # my ($bool_feat) = gitweb_get_feature('bool_feat'); # (gitweb_get_feature('bool_feat'))[0] or somecode; # sub gitweb_check_feature { return (gitweb_get_feature(@_))[0]; } sub feature_bool { my $key = shift; my ($val) = git_get_project_config($key, '--bool'); if (!defined $val) { return ($_[0]); } elsif ($val eq 'true') { return (1); } elsif ($val eq 'false') { return (0); } } sub feature_snapshot { my (@fmts) = @_; my ($val) = git_get_project_config('snapshot'); if ($val) { @fmts = ($val eq 'none' ? () : split /\s*[,\s]\s*/, $val); } return @fmts; } sub feature_patches { my @val = (git_get_project_config('patches', '--int')); if (@val) { return @val; } return ($_[0]); } sub feature_avatar { my @val = (git_get_project_config('avatar')); return @val ? @val : @_; } # checking HEAD file with -e is fragile if the repository was # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed # and then pruned. sub check_head_link { my ($dir) = @_; my $headfile = "$dir/HEAD"; return ((-e $headfile) || (-l $headfile && readlink($headfile) =~ /^refs\/heads\//)); } sub check_export_ok { my ($dir) = @_; return (check_head_link($dir) && (!$export_ok || -e "$dir/$export_ok") && (!$export_auth_hook || $export_auth_hook->($dir))); } # process alternate names for backward compatibility # filter out unsupported (unknown) snapshot formats sub filter_snapshot_fmts { my @fmts = @_; @fmts = map { exists $known_snapshot_format_aliases{$_} ? $known_snapshot_format_aliases{$_} : $_} @fmts; @fmts = grep { exists $known_snapshot_formats{$_} && !$known_snapshot_formats{$_}{'disabled'}} @fmts; } our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM); sub evaluate_gitweb_config { our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "gitweb_config.perl"; our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "/etc/gitweb.conf"; # die if there are errors parsing config file if (-e $GITWEB_CONFIG) { do $GITWEB_CONFIG; die $@ if $@; } elsif (-e $GITWEB_CONFIG_SYSTEM) { do $GITWEB_CONFIG_SYSTEM; die $@ if $@; } } # Get loadavg of system, to compare against $maxload. # Currently it requires '/proc/loadavg' present to get loadavg; # if it is not present it returns 0, which means no load checking. sub get_loadavg { if( -e '/proc/loadavg' ){ open my $fd, '<', '/proc/loadavg' or return 0; my @load = split(/\s+/, scalar <$fd>); close $fd; # The first three columns measure CPU and IO utilization of the last one, # five, and 10 minute periods. The fourth column shows the number of # currently running processes and the total number of processes in the m/n # format. The last column displays the last process ID used. return $load[0] || 0; } # additional checks for load average should go here for things that don't export # /proc/loadavg return 0; } # version of the core git binary our $git_version; sub evaluate_git_version { our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown"; $number_of_git_cmds++; } sub check_loadavg { if (defined $maxload && get_loadavg() > $maxload) { die_error(503, "The load average on the server is too high"); } } # ====================================================================== # input validation and dispatch # input parameters can be collected from a variety of sources (presently, CGI # and PATH_INFO), so we define an %input_params hash that collects them all # together during validation: this allows subsequent uses (e.g. href()) to be # agnostic of the parameter origin our %input_params = (); # input parameters are stored with the long parameter name as key. This will # also be used in the href subroutine to convert parameters to their CGI # equivalent, and since the href() usage is the most frequent one, we store # the name -> CGI key mapping here, instead of the reverse. # # XXX: Warning: If you touch this, check the search form for updating, # too. our @cgi_param_mapping = ( project => "p", action => "a", file_name => "f", file_parent => "fp", hash => "h", hash_parent => "hp", hash_base => "hb", hash_parent_base => "hpb", page => "pg", order => "o", searchtext => "s", searchtype => "st", snapshot_format => "sf", extra_options => "opt", search_use_regexp => "sr", # this must be last entry (for manipulation from JavaScript) javascript => "js" ); our %cgi_param_mapping = @cgi_param_mapping; # we will also need to know the possible actions, for validation our %actions = ( "blame" => \&git_blame, "blame_incremental" => \&git_blame_incremental, "blame_data" => \&git_blame_data, "blobdiff" => \&git_blobdiff, "blobdiff_plain" => \&git_blobdiff_plain, "blob" => \&git_blob, "blob_plain" => \&git_blob_plain, "commitdiff" => \&git_commitdiff, "commitdiff_plain" => \&git_commitdiff_plain, "commit" => \&git_commit, "forks" => \&git_forks, "heads" => \&git_heads, "history" => \&git_history, "log" => \&git_log, "patch" => \&git_patch, "patches" => \&git_patches, "rss" => \&git_rss, "atom" => \&git_atom, "search" => \&git_search, "search_help" => \&git_search_help, "shortlog" => \&git_shortlog, "summary" => \&git_summary, "tag" => \&git_tag, "tags" => \&git_tags, "tree" => \&git_tree, "snapshot" => \&git_snapshot, "object" => \&git_object, # those below don't need $project "opml" => \&git_opml, "project_list" => \&git_project_list, "project_index" => \&git_project_index, ); # finally, we have the hash of allowed extra_options for the commands that # allow them our %allowed_options = ( "--no-merges" => [ qw(rss atom log shortlog history) ], ); # fill %input_params with the CGI parameters. All values except for 'opt' # should be single values, but opt can be an array. We should probably # build an array of parameters that can be multi-valued, but since for the time # being it's only this one, we just single it out sub evaluate_query_params { our $cgi; while (my ($name, $symbol) = each %cgi_param_mapping) { if ($symbol eq 'opt') { $input_params{$name} = [ $cgi->param($symbol) ]; } else { $input_params{$name} = $cgi->param($symbol); } } } # now read PATH_INFO and update the parameter list for missing parameters sub evaluate_path_info { return if defined $input_params{'project'}; return if !$path_info; $path_info =~ s,^/+,,; return if !$path_info; # find which part of PATH_INFO is project my $project = $path_info; $project =~ s,/+$,,; while ($project && !check_head_link("$projectroot/$project")) { $project =~ s,/*[^/]*$,,; } return unless $project; $input_params{'project'} = $project; # do not change any parameters if an action is given using the query string return if $input_params{'action'}; $path_info =~ s,^\Q$project\E/*,,; # next, check if we have an action my $action = $path_info; $action =~ s,/.*$,,; if (exists $actions{$action}) { $path_info =~ s,^$action/*,,; $input_params{'action'} = $action; } # list of actions that want hash_base instead of hash, but can have no # pathname (f) parameter my @wants_base = ( 'tree', 'history', ); # we want to catch # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name] my ($parentrefname, $parentpathname, $refname, $pathname) = ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/); # first, analyze the 'current' part if (defined $pathname) { # we got "branch:filename" or "branch:dir/" # we could use git_get_type(branch:pathname), but: # - it needs $git_dir # - it does a git() call # - the convention of terminating directories with a slash # makes it superfluous # - embedding the action in the PATH_INFO would make it even # more superfluous $pathname =~ s,^/+,,; if (!$pathname || substr($pathname, -1) eq "/") { $input_params{'action'} ||= "tree"; $pathname =~ s,/$,,; } else { # the default action depends on whether we had parent info # or not if ($parentrefname) { $input_params{'action'} ||= "blobdiff_plain"; } else { $input_params{'action'} ||= "blob_plain"; } } $input_params{'hash_base'} ||= $refname; $input_params{'file_name'} ||= $pathname; } elsif (defined $refname) { # we got "branch". In this case we have to choose if we have to # set hash or hash_base. # # Most of the actions without a pathname only want hash to be # set, except for the ones specified in @wants_base that want # hash_base instead. It should also be noted that hand-crafted # links having 'history' as an action and no pathname or hash # set will fail, but that happens regardless of PATH_INFO. $input_params{'action'} ||= "shortlog"; if (grep { $_ eq $input_params{'action'} } @wants_base) { $input_params{'hash_base'} ||= $refname; } else { $input_params{'hash'} ||= $refname; } } # next, handle the 'parent' part, if present if (defined $parentrefname) { # a missing pathspec defaults to the 'current' filename, allowing e.g. # someproject/blobdiff/oldrev..newrev:/filename if ($parentpathname) { $parentpathname =~ s,^/+,,; $parentpathname =~ s,/$,,; $input_params{'file_parent'} ||= $parentpathname; } else { $input_params{'file_parent'} ||= $input_params{'file_name'}; } # we assume that hash_parent_base is wanted if a path was specified, # or if the action wants hash_base instead of hash if (defined $input_params{'file_parent'} || grep { $_ eq $input_params{'action'} } @wants_base) { $input_params{'hash_parent_base'} ||= $parentrefname; } else { $input_params{'hash_parent'} ||= $parentrefname; } } # for the snapshot action, we allow URLs in the form # $project/snapshot/$hash.ext # where .ext determines the snapshot and gets removed from the # passed $refname to provide the $hash. # # To be able to tell that $refname includes the format extension, we # require the following two conditions to be satisfied: # - the hash input parameter MUST have been set from the $refname part # of the URL (i.e. they must be equal) # - the snapshot format MUST NOT have been defined already (e.g. from # CGI parameter sf) # It's also useless to try any matching unless $refname has a dot, # so we check for that too if (defined $input_params{'action'} && $input_params{'action'} eq 'snapshot' && defined $refname && index($refname, '.') != -1 && $refname eq $input_params{'hash'} && !defined $input_params{'snapshot_format'}) { # We loop over the known snapshot formats, checking for # extensions. Allowed extensions are both the defined suffix # (which includes the initial dot already) and the snapshot # format key itself, with a prepended dot while (my ($fmt, $opt) = each %known_snapshot_formats) { my $hash = $refname; unless ($hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//) { next; } my $sfx = $1; # a valid suffix was found, so set the snapshot format # and reset the hash parameter $input_params{'snapshot_format'} = $fmt; $input_params{'hash'} = $hash; # we also set the format suffix to the one requested # in the URL: this way a request for e.g. .tgz returns # a .tgz instead of a .tar.gz $known_snapshot_formats{$fmt}{'suffix'} = $sfx; last; } } } our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base, $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp, $searchtext, $search_regexp); sub evaluate_and_validate_params { our $action = $input_params{'action'}; if (defined $action) { if (!validate_action($action)) { die_error(400, "Invalid action parameter"); } } # parameters which are pathnames our $project = $input_params{'project'}; if (defined $project) { if (!validate_project($project)) { undef $project; die_error(404, "No such project"); } } our $file_name = $input_params{'file_name'}; if (defined $file_name) { if (!validate_pathname($file_name)) { die_error(400, "Invalid file parameter"); } } our $file_parent = $input_params{'file_parent'}; if (defined $file_parent) { if (!validate_pathname($file_parent)) { die_error(400, "Invalid file parent parameter"); } } # parameters which are refnames our $hash = $input_params{'hash'}; if (defined $hash) { if (!validate_refname($hash)) { die_error(400, "Invalid hash parameter"); } } our $hash_parent = $input_params{'hash_parent'}; if (defined $hash_parent) { if (!validate_refname($hash_parent)) { die_error(400, "Invalid hash parent parameter"); } } our $hash_base = $input_params{'hash_base'}; if (defined $hash_base) { if (!validate_refname($hash_base)) { die_error(400, "Invalid hash base parameter"); } } our @extra_options = @{$input_params{'extra_options'}}; # @extra_options is always defined, since it can only be (currently) set from # CGI, and $cgi->param() returns the empty array in array context if the param # is not set foreach my $opt (@extra_options) { if (not exists $allowed_options{$opt}) { die_error(400, "Invalid option parameter"); } if (not grep(/^$action$/, @{$allowed_options{$opt}})) { die_error(400, "Invalid option parameter for this action"); } } our $hash_parent_base = $input_params{'hash_parent_base'}; if (defined $hash_parent_base) { if (!validate_refname($hash_parent_base)) { die_error(400, "Invalid hash parent base parameter"); } } # other parameters our $page = $input_params{'page'}; if (defined $page) { if ($page =~ m/[^0-9]/) { die_error(400, "Invalid page parameter"); } } our $searchtype = $input_params{'searchtype'}; if (defined $searchtype) { if ($searchtype =~ m/[^a-z]/) { die_error(400, "Invalid searchtype parameter"); } } our $search_use_regexp = $input_params{'search_use_regexp'}; our $searchtext = $input_params{'searchtext'}; our $search_regexp; if (defined $searchtext) { if (length($searchtext) < 2) { die_error(403, "At least two characters are required for search parameter"); } $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext; } } # path to the current git repository our $git_dir; sub evaluate_git_dir { our $git_dir = "$projectroot/$project" if $project; } our (@snapshot_fmts, $git_avatar); sub configure_gitweb_features { # list of supported snapshot formats our @snapshot_fmts = gitweb_get_feature('snapshot'); @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts); # check that the avatar feature is set to a known provider name, # and for each provider check if the dependencies are satisfied. # if the provider name is invalid or the dependencies are not met, # reset $git_avatar to the empty string. our ($git_avatar) = gitweb_get_feature('avatar'); if ($git_avatar eq 'gravatar') { $git_avatar = '' unless (eval { require Digest::MD5; 1; }); } elsif ($git_avatar eq 'picon') { # no dependencies } else { $git_avatar = ''; } } # custom error handler: 'die ' is Internal Server Error sub handle_errors_html { my $msg = shift; # it is already HTML escaped # to avoid infinite loop where error occurs in die_error, # change handler to default handler, disabling handle_errors_html set_message("Error occured when inside die_error:\n$msg"); # you cannot jump out of die_error when called as error handler; # the subroutine set via CGI::Carp::set_message is called _after_ # HTTP headers are already written, so it cannot write them itself die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1); } set_message(\&handle_errors_html); # dispatch sub dispatch { if (!defined $action) { if (defined $hash) { $action = git_get_type($hash); } elsif (defined $hash_base && defined $file_name) { $action = git_get_type("$hash_base:$file_name"); } elsif (defined $project) { $action = 'summary'; } else { $action = 'project_list'; } } if (!defined($actions{$action})) { die_error(400, "Unknown action"); } if ($action !~ m/^(?:opml|project_list|project_index)$/ && !$project) { die_error(400, "Project needed"); } $actions{$action}->(); } sub reset_timer { our $t0 = [Time::HiRes::gettimeofday()] if defined $t0; our $number_of_git_cmds = 0; } sub run_request { reset_timer(); evaluate_uri(); check_loadavg(); evaluate_query_params(); evaluate_path_info(); evaluate_and_validate_params(); evaluate_git_dir(); configure_gitweb_features(); dispatch(); } our $is_last_request = sub { 1 }; our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook); our $CGI = 'CGI'; our $cgi; sub configure_as_fcgi { require CGI::Fast; our $CGI = 'CGI::Fast'; my $request_number = 0; # let each child service 100 requests our $is_last_request = sub { ++$request_number > 100 }; } sub evaluate_argv { my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__; configure_as_fcgi() if $script_name =~ /\.fcgi$/; return unless (@ARGV); require Getopt::Long; Getopt::Long::GetOptions( 'fastcgi|fcgi|f' => \&configure_as_fcgi, 'nproc|n=i' => sub { my ($arg, $val) = @_; return unless eval { require FCGI::ProcManager; 1; }; my $proc_manager = FCGI::ProcManager->new({ n_processes => $val, }); our $pre_listen_hook = sub { $proc_manager->pm_manage() }; our $pre_dispatch_hook = sub { $proc_manager->pm_pre_dispatch() }; our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() }; }, ); } sub run { evaluate_argv(); evaluate_gitweb_config(); evaluate_git_version(); # $projectroot and $projects_list might be set in gitweb config file $projects_list ||= $projectroot; $pre_listen_hook->() if $pre_listen_hook; REQUEST: while ($cgi = $CGI->new()) { $pre_dispatch_hook->() if $pre_dispatch_hook; run_request(); $pre_dispatch_hook->() if $post_dispatch_hook; last REQUEST if ($is_last_request->()); } DONE_GITWEB: 1; } run(); if (defined caller) { # wrapped in a subroutine processing requests, # e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI return; } else { # pure CGI script, serving single request exit; } ## ====================================================================== ## action links # possible values of extra options # -full => 0|1 - use absolute/full URL ($my_uri/$my_url as base) # -replay => 1 - start from a current view (replay with modifications) # -path_info => 0|1 - don't use/use path_info URL (if possible) sub href { my %params = @_; # default is to use -absolute url() i.e. $my_uri my $href = $params{-full} ? $my_url : $my_uri; $params{'project'} = $project unless exists $params{'project'}; if ($params{-replay}) { while (my ($name, $symbol) = each %cgi_param_mapping) { if (!exists $params{$name}) { $params{$name} = $input_params{$name}; } } } my $use_pathinfo = gitweb_check_feature('pathinfo'); if (defined $params{'project'} && (exists $params{-path_info} ? $params{-path_info} : $use_pathinfo)) { # try to put as many parameters as possible in PATH_INFO: # - project name # - action # - hash_parent or hash_parent_base:/file_parent # - hash or hash_base:/filename # - the snapshot_format as an appropriate suffix # When the script is the root DirectoryIndex for the domain, # $href here would be something like http://gitweb.example.com/ # Thus, we strip any trailing / from $href, to spare us double # slashes in the final URL $href =~ s,/$,,; # Then add the project name, if present $href .= "/".esc_url($params{'project'}); delete $params{'project'}; # since we destructively absorb parameters, we keep this # boolean that remembers if we're handling a snapshot my $is_snapshot = $params{'action'} eq 'snapshot'; # Summary just uses the project path URL, any other action is # added to the URL if (defined $params{'action'}) { $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary'; delete $params{'action'}; } # Next, we put hash_parent_base:/file_parent..hash_base:/file_name, # stripping nonexistent or useless pieces $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'} || $params{'hash_parent'} || $params{'hash'}); if (defined $params{'hash_base'}) { if (defined $params{'hash_parent_base'}) { $href .= esc_url($params{'hash_parent_base'}); # skip the file_parent if it's the same as the file_name if (defined $params{'file_parent'}) { if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) { delete $params{'file_parent'}; } elsif ($params{'file_parent'} !~ /\.\./) { $href .= ":/".esc_url($params{'file_parent'}); delete $params{'file_parent'}; } } $href .= ".."; delete $params{'hash_parent'}; delete $params{'hash_parent_base'}; } elsif (defined $params{'hash_parent'}) { $href .= esc_url($params{'hash_parent'}). ".."; delete $params{'hash_parent'}; } $href .= esc_url($params{'hash_base'}); if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) { $href .= ":/".esc_url($params{'file_name'}); delete $params{'file_name'}; } delete $params{'hash'}; delete $params{'hash_base'}; } elsif (defined $params{'hash'}) { $href .= esc_url($params{'hash'}); delete $params{'hash'}; } # If the action was a snapshot, we can absorb the # snapshot_format parameter too if ($is_snapshot) { my $fmt = $params{'snapshot_format'}; # snapshot_format should always be defined when href() # is called, but just in case some code forgets, we # fall back to the default $fmt ||= $snapshot_fmts[0]; $href .= $known_snapshot_formats{$fmt}{'suffix'}; delete $params{'snapshot_format'}; } } # now encode the parameters explicitly my @result = (); for (my $i = 0; $i < @cgi_param_mapping; $i += 2) { my ($name, $symbol) = ($cgi_param_mapping[$i], $cgi_param_mapping[$i+1]); if (defined $params{$name}) { if (ref($params{$name}) eq "ARRAY") { foreach my $par (@{$params{$name}}) { push @result, $symbol . "=" . esc_param($par); } } else { push @result, $symbol . "=" . esc_param($params{$name}); } } } $href .= "?" . join(';', @result) if scalar @result; return $href; } ## ====================================================================== ## validation, quoting/unquoting and escaping sub validate_action { my $input = shift || return undef; return undef unless exists $actions{$input}; return $input; } sub validate_project { my $input = shift || return undef; if (!validate_pathname($input) || !(-d "$projectroot/$input") || !check_export_ok("$projectroot/$input") || ($strict_export && !project_in_list($input))) { return undef; } else { return $input; } } sub validate_pathname { my $input = shift || return undef; # no '.' or '..' as elements of path, i.e. no '.' nor '..' # at the beginning, at the end, and between slashes. # also this catches doubled slashes if ($input =~ m!(^|/)(|\.|\.\.)(/|$)!) { return undef; } # no null characters if ($input =~ m!\0!) { return undef; } return $input; } sub validate_refname { my $input = shift || return undef; # textual hashes are O.K. if ($input =~ m/^[0-9a-fA-F]{40}$/) { return $input; } # it must be correct pathname $input = validate_pathname($input) or return undef; # restrictions on ref name according to git-check-ref-format if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) { return undef; } return $input; } # decode sequences of octets in utf8 into Perl's internal form, # which is utf-8 with utf8 flag set if needed. gitweb writes out # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning sub to_utf8 { my $str = shift; return undef unless defined $str; if (utf8::valid($str)) { utf8::decode($str); return $str; } else { return decode($fallback_encoding, $str, Encode::FB_DEFAULT); } } # quote unsafe chars, but keep the slash, even when it's not # correct, but quoted slashes look too horrible in bookmarks sub esc_param { my $str = shift; return undef unless defined $str; $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg; $str =~ s/ /\+/g; return $str; } # quote unsafe chars in whole URL, so some charactrs cannot be quoted sub esc_url { my $str = shift; return undef unless defined $str; $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg; $str =~ s/ /\+/g; return $str; } # replace invalid utf8 character with SUBSTITUTION sequence sub esc_html { my $str = shift; my %opts = @_; return undef unless defined $str; $str = to_utf8($str); $str = $cgi->escapeHTML($str); if ($opts{'-nbsp'}) { $str =~ s/ / /g; } $str =~ s|([[:cntrl:]])|(($1 ne "\t") ? quot_cec($1) : $1)|eg; return $str; } # quote control characters and escape filename to HTML sub esc_path { my $str = shift; my %opts = @_; return undef unless defined $str; $str = to_utf8($str); $str = $cgi->escapeHTML($str); if ($opts{'-nbsp'}) { $str =~ s/ / /g; } $str =~ s|([[:cntrl:]])|quot_cec($1)|eg; return $str; } # Make control characters "printable", using character escape codes (CEC) sub quot_cec { my $cntrl = shift; my %opts = @_; my %es = ( # character escape codes, aka escape sequences "\t" => '\t', # tab (HT) "\n" => '\n', # line feed (LF) "\r" => '\r', # carrige return (CR) "\f" => '\f', # form feed (FF) "\b" => '\b', # backspace (BS) "\a" => '\a', # alarm (bell) (BEL) "\e" => '\e', # escape (ESC) "\013" => '\v', # vertical tab (VT) "\000" => '\0', # nul character (NUL) ); my $chr = ( (exists $es{$cntrl}) ? $es{$cntrl} : sprintf('\%2x', ord($cntrl)) ); if ($opts{-nohtml}) { return $chr; } else { return "$chr"; } } # Alternatively use unicode control pictures codepoints, # Unicode "printable representation" (PR) sub quot_upr { my $cntrl = shift; my %opts = @_; my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl)); if ($opts{-nohtml}) { return $chr; } else { return "$chr"; } } # git may return quoted and escaped filenames sub unquote { my $str = shift; sub unq { my $seq = shift; my %es = ( # character escape codes, aka escape sequences 't' => "\t", # tab (HT, TAB) 'n' => "\n", # newline (NL) 'r' => "\r", # return (CR) 'f' => "\f", # form feed (FF) 'b' => "\b", # backspace (BS) 'a' => "\a", # alarm (bell) (BEL) 'e' => "\e", # escape (ESC) 'v' => "\013", # vertical tab (VT) ); if ($seq =~ m/^[0-7]{1,3}$/) { # octal char sequence return chr(oct($seq)); } elsif (exists $es{$seq}) { # C escape sequence, aka character escape code return $es{$seq}; } # quoted ordinary character return $seq; } if ($str =~ m/^"(.*)"$/) { # needs unquoting $str = $1; $str =~ s/\\([^0-7]|[0-7]{1,3})/unq($1)/eg; } return $str; } # escape tabs (convert tabs to spaces) sub untabify { my $line = shift; while ((my $pos = index($line, "\t")) != -1) { if (my $count = (8 - ($pos % 8))) { my $spaces = ' ' x $count; $line =~ s/\t/$spaces/; } } return $line; } sub project_in_list { my $project = shift; my @list = git_get_projects_list(); return @list && scalar(grep { $_->{'path'} eq $project } @list); } ## ---------------------------------------------------------------------- ## HTML aware string manipulation # Try to chop given string on a word boundary between position # $len and $len+$add_len. If there is no word boundary there, # chop at $len+$add_len. Do not chop if chopped part plus ellipsis # (marking chopped part) would be longer than given string. sub chop_str { my $str = shift; my $len = shift; my $add_len = shift || 10; my $where = shift || 'right'; # 'left' | 'center' | 'right' # Make sure perl knows it is utf8 encoded so we don't # cut in the middle of a utf8 multibyte char. $str = to_utf8($str); # allow only $len chars, but don't cut a word if it would fit in $add_len # if it doesn't fit, cut it if it's still longer than the dots we would add # remove chopped character entities entirely # when chopping in the middle, distribute $len into left and right part # return early if chopping wouldn't make string shorter if ($where eq 'center') { return $str if ($len + 5 >= length($str)); # filler is length 5 $len = int($len/2); } else { return $str if ($len + 4 >= length($str)); # filler is length 4 } # regexps: ending and beginning with word part up to $add_len my $endre = qr/.{$len}\w{0,$add_len}/; my $begre = qr/\w{0,$add_len}.{$len}/; if ($where eq 'left') { $str =~ m/^(.*?)($begre)$/; my ($lead, $body) = ($1, $2); if (length($lead) > 4) { $lead = " ..."; } return "$lead$body"; } elsif ($where eq 'center') { $str =~ m/^($endre)(.*)$/; my ($left, $str) = ($1, $2); $str =~ m/^(.*?)($begre)$/; my ($mid, $right) = ($1, $2); if (length($mid) > 5) { $mid = " ... "; } return "$left$mid$right"; } else { $str =~ m/^($endre)(.*)$/; my $body = $1; my $tail = $2; if (length($tail) > 4) { $tail = "... "; } return "$body$tail"; } } # takes the same arguments as chop_str, but also wraps a around the # result with a title attribute if it does get chopped. Additionally, the # string is HTML-escaped. sub chop_and_escape_str { my ($str) = @_; my $chopped = chop_str(@_); if ($chopped eq $str) { return esc_html($chopped); } else { $str =~ s/[[:cntrl:]]/?/g; return $cgi->span({-title=>$str}, esc_html($chopped)); } } ## ---------------------------------------------------------------------- ## functions returning short strings # CSS class for given age value (in seconds) sub age_class { my $age = shift; if (!defined $age) { return "noage"; } elsif ($age < 60*60*2) { return "age0"; } elsif ($age < 60*60*24*2) { return "age1"; } else { return "age2"; } } # convert age in seconds to "nn units ago" string sub age_string { my $age = shift; my $age_str; if ($age > 60*60*24*365*2) { $age_str = (int $age/60/60/24/365); $age_str .= " years ago"; } elsif ($age > 60*60*24*(365/12)*2) { $age_str = int $age/60/60/24/(365/12); $age_str .= " months ago"; } elsif ($age > 60*60*24*7*2) { $age_str = int $age/60/60/24/7; $age_str .= " weeks ago"; } elsif ($age > 60*60*24*2) { $age_str = int $age/60/60/24; $age_str .= " days ago"; } elsif ($age > 60*60*2) { $age_str = int $age/60/60; $age_str .= " hours ago"; } elsif ($age > 60*2) { $age_str = int $age/60; $age_str .= " min ago"; } elsif ($age > 2) { $age_str = int $age; $age_str .= " sec ago"; } else { $age_str .= " right now"; } return $age_str; } use constant { S_IFINVALID => 0030000, S_IFGITLINK => 0160000, }; # submodule/subproject, a commit object reference sub S_ISGITLINK { my $mode = shift; return (($mode & S_IFMT) == S_IFGITLINK) } # convert file mode in octal to symbolic file mode string sub mode_str { my $mode = oct shift; if (S_ISGITLINK($mode)) { return 'm---------'; } elsif (S_ISDIR($mode & S_IFMT)) { return 'drwxr-xr-x'; } elsif (S_ISLNK($mode)) { return 'lrwxrwxrwx'; } elsif (S_ISREG($mode)) { # git cares only about the executable bit if ($mode & S_IXUSR) { return '-rwxr-xr-x'; } else { return '-rw-r--r--'; }; } else { return '----------'; } } # convert file mode in octal to file type string sub file_type { my $mode = shift; if ($mode !~ m/^[0-7]+$/) { return $mode; } else { $mode = oct $mode; } if (S_ISGITLINK($mode)) { return "submodule"; } elsif (S_ISDIR($mode & S_IFMT)) { return "directory"; } elsif (S_ISLNK($mode)) { return "symlink"; } elsif (S_ISREG($mode)) { return "file"; } else { return "unknown"; } } # convert file mode in octal to file type description string sub file_type_long { my $mode = shift; if ($mode !~ m/^[0-7]+$/) { return $mode; } else { $mode = oct $mode; } if (S_ISGITLINK($mode)) { return "submodule"; } elsif (S_ISDIR($mode & S_IFMT)) { return "directory"; } elsif (S_ISLNK($mode)) { return "symlink"; } elsif (S_ISREG($mode)) { if ($mode & S_IXUSR) { return "executable"; } else { return "file"; }; } else { return "unknown"; } } ## ---------------------------------------------------------------------- ## functions returning short HTML fragments, or transforming HTML fragments ## which don't belong to other sections # format line of commit message. sub format_log_line_html { my $line = shift; $line = esc_html($line, -nbsp=>1); $line =~ s{\b([0-9a-fA-F]{8,40})\b}{ $cgi->a({-href => href(action=>"object", hash=>$1), -class => "text"}, $1); }eg; return $line; } # format marker of refs pointing to given object # the destination action is chosen based on object type and current context: # - for annotated tags, we choose the tag view unless it's the current view # already, in which case we go to shortlog view # - for other refs, we keep the current view if we're in history, shortlog or # log view, and select shortlog otherwise sub format_ref_marker { my ($refs, $id) = @_; my $markers = ''; if (defined $refs->{$id}) { foreach my $ref (@{$refs->{$id}}) { # this code exploits the fact that non-lightweight tags are the # only indirect objects, and that they are the only objects for which # we want to use tag instead of shortlog as action my ($type, $name) = qw(); my $indirect = ($ref =~ s/\^\{\}$//); # e.g. tags/v2.6.11 or heads/next if ($ref =~ m!^(.*?)s?/(.*)$!) { $type = $1; $name = $2; } else { $type = "ref"; $name = $ref; } my $class = $type; $class .= " indirect" if $indirect; my $dest_action = "shortlog"; if ($indirect) { $dest_action = "tag" unless $action eq "tag"; } elsif ($action =~ /^(history|(short)?log)$/) { $dest_action = $action; } my $dest = ""; $dest .= "refs/" unless $ref =~ m!^refs/!; $dest .= $ref; my $link = $cgi->a({ -href => href( action=>$dest_action, hash=>$dest )}, $name); $markers .= " " . $link . ""; } } if ($markers) { return ' '. $markers . ''; } else { return ""; } } # format, perhaps shortened and with markers, title line sub format_subject_html { my ($long, $short, $href, $extra) = @_; $extra = '' unless defined($extra); if (length($short) < length($long)) { $long =~ s/[[:cntrl:]]/?/g; return $cgi->a({-href => $href, -class => "list subject", -title => to_utf8($long)}, esc_html($short)) . $extra; } else { return $cgi->a({-href => $href, -class => "list subject"}, esc_html($long)) . $extra; } } # Rather than recomputing the url for an email multiple times, we cache it # after the first hit. This gives a visible benefit in views where the avatar # for the same email is used repeatedly (e.g. shortlog). # The cache is shared by all avatar engines (currently gravatar only), which # are free to use it as preferred. Since only one avatar engine is used for any # given page, there's no risk for cache conflicts. our %avatar_cache = (); # Compute the picon url for a given email, by using the picon search service over at # http://www.cs.indiana.edu/picons/search.html sub picon_url { my $email = lc shift; if (!$avatar_cache{$email}) { my ($user, $domain) = split('@', $email); $avatar_cache{$email} = "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" . "$domain/$user/" . "users+domains+unknown/up/single"; } return $avatar_cache{$email}; } # Compute the gravatar url for a given email, if it's not in the cache already. # Gravatar stores only the part of the URL before the size, since that's the # one computationally more expensive. This also allows reuse of the cache for # different sizes (for this particular engine). sub gravatar_url { my $email = lc shift; my $size = shift; $avatar_cache{$email} ||= "http://www.gravatar.com/avatar/" . Digest::MD5::md5_hex($email) . "?s="; return $avatar_cache{$email} . $size; } # Insert an avatar for the given $email at the given $size if the feature # is enabled. sub git_get_avatar { my ($email, %opts) = @_; my $pre_white = ($opts{-pad_before} ? " " : ""); my $post_white = ($opts{-pad_after} ? " " : ""); $opts{-size} ||= 'default'; my $size = $avatar_size{$opts{-size}} || $avatar_size{'default'}; my $url = ""; if ($git_avatar eq 'gravatar') { $url = gravatar_url($email, $size); } elsif ($git_avatar eq 'picon') { $url = picon_url($email); } # Other providers can be added by extending the if chain, defining $url # as needed. If no variant puts something in $url, we assume avatars # are completely disabled/unavailable. if ($url) { return $pre_white . "" . $post_white; } else { return ""; } } sub format_search_author { my ($author, $searchtype, $displaytext) = @_; my $have_search = gitweb_check_feature('search'); if ($have_search) { my $performed = ""; if ($searchtype eq 'author') { $performed = "authored"; } elsif ($searchtype eq 'committer') { $performed = "committed"; } return $cgi->a({-href => href(action=>"search", hash=>$hash, searchtext=>$author, searchtype=>$searchtype), class=>"list", title=>"Search for commits $performed by $author"}, $displaytext); } else { return $displaytext; } } # format the author name of the given commit with the given tag # the author name is chopped and escaped according to the other # optional parameters (see chop_str). sub format_author_html { my $tag = shift; my $co = shift; my $author = chop_and_escape_str($co->{'author_name'}, @_); return "<$tag class=\"author\">" . format_search_author($co->{'author_name'}, "author", git_get_avatar($co->{'author_email'}, -pad_after => 1) . $author) . ""; } # format git diff header line, i.e. "diff --(git|combined|cc) ..." sub format_git_diff_header_line { my $line = shift; my $diffinfo = shift; my ($from, $to) = @_; if ($diffinfo->{'nparents'}) { # combined diff $line =~ s!^(diff (.*?) )"?.*$!$1!; if ($to->{'href'}) { $line .= $cgi->a({-href => $to->{'href'}, -class => "path"}, esc_path($to->{'file'})); } else { # file was deleted (no href) $line .= esc_path($to->{'file'}); } } else { # "ordinary" diff $line =~ s!^(diff (.*?) )"?a/.*$!$1!; if ($from->{'href'}) { $line .= $cgi->a({-href => $from->{'href'}, -class => "path"}, 'a/' . esc_path($from->{'file'})); } else { # file was added (no href) $line .= 'a/' . esc_path($from->{'file'}); } $line .= ' '; if ($to->{'href'}) { $line .= $cgi->a({-href => $to->{'href'}, -class => "path"}, 'b/' . esc_path($to->{'file'})); } else { # file was deleted $line .= 'b/' . esc_path($to->{'file'}); } } return "
$line
\n"; } # format extended diff header line, before patch itself sub format_extended_diff_header_line { my $line = shift; my $diffinfo = shift; my ($from, $to) = @_; # match if ($line =~ s!^((copy|rename) from ).*$!$1! && $from->{'href'}) { $line .= $cgi->a({-href=>$from->{'href'}, -class=>"path"}, esc_path($from->{'file'})); } if ($line =~ s!^((copy|rename) to ).*$!$1! && $to->{'href'}) { $line .= $cgi->a({-href=>$to->{'href'}, -class=>"path"}, esc_path($to->{'file'})); } # match single if ($line =~ m/\s(\d{6})$/) { $line .= ' (' . file_type_long($1) . ')'; } # match if ($line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) { # can match only for combined diff $line = 'index '; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { if ($from->{'href'}[$i]) { $line .= $cgi->a({-href=>$from->{'href'}[$i], -class=>"hash"}, substr($diffinfo->{'from_id'}[$i],0,7)); } else { $line .= '0' x 7; } # separator $line .= ',' if ($i < $diffinfo->{'nparents'} - 1); } $line .= '..'; if ($to->{'href'}) { $line .= $cgi->a({-href=>$to->{'href'}, -class=>"hash"}, substr($diffinfo->{'to_id'},0,7)); } else { $line .= '0' x 7; } } elsif ($line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) { # can match only for ordinary diff my ($from_link, $to_link); if ($from->{'href'}) { $from_link = $cgi->a({-href=>$from->{'href'}, -class=>"hash"}, substr($diffinfo->{'from_id'},0,7)); } else { $from_link = '0' x 7; } if ($to->{'href'}) { $to_link = $cgi->a({-href=>$to->{'href'}, -class=>"hash"}, substr($diffinfo->{'to_id'},0,7)); } else { $to_link = '0' x 7; } my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'}); $line =~ s!$from_id\.\.$to_id!$from_link..$to_link!; } return $line . "
\n"; } # format from-file/to-file diff header sub format_diff_from_to_header { my ($from_line, $to_line, $diffinfo, $from, $to, @parents) = @_; my $line; my $result = ''; $line = $from_line; #assert($line =~ m/^---/) if DEBUG; # no extra formatting for "^--- /dev/null" if (! $diffinfo->{'nparents'}) { # ordinary (single parent) diff if ($line =~ m!^--- "?a/!) { if ($from->{'href'}) { $line = '--- a/' . $cgi->a({-href=>$from->{'href'}, -class=>"path"}, esc_path($from->{'file'})); } else { $line = '--- a/' . esc_path($from->{'file'}); } } $result .= qq!
$line
\n!; } else { # combined diff (merge commit) for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { if ($from->{'href'}[$i]) { $line = '--- ' . $cgi->a({-href=>href(action=>"blobdiff", hash_parent=>$diffinfo->{'from_id'}[$i], hash_parent_base=>$parents[$i], file_parent=>$from->{'file'}[$i], hash=>$diffinfo->{'to_id'}, hash_base=>$hash, file_name=>$to->{'file'}), -class=>"path", -title=>"diff" . ($i+1)}, $i+1) . '/' . $cgi->a({-href=>$from->{'href'}[$i], -class=>"path"}, esc_path($from->{'file'}[$i])); } else { $line = '--- /dev/null'; } $result .= qq!
$line
\n!; } } $line = $to_line; #assert($line =~ m/^\+\+\+/) if DEBUG; # no extra formatting for "^+++ /dev/null" if ($line =~ m!^\+\+\+ "?b/!) { if ($to->{'href'}) { $line = '+++ b/' . $cgi->a({-href=>$to->{'href'}, -class=>"path"}, esc_path($to->{'file'})); } else { $line = '+++ b/' . esc_path($to->{'file'}); } } $result .= qq!
$line
\n!; return $result; } # create note for patch simplified by combined diff sub format_diff_cc_simplified { my ($diffinfo, @parents) = @_; my $result = ''; $result .= "
" . "diff --cc "; if (!is_deleted($diffinfo)) { $result .= $cgi->a({-href => href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'}), -class => "path"}, esc_path($diffinfo->{'to_file'})); } else { $result .= esc_path($diffinfo->{'to_file'}); } $result .= "
\n" . # class="diff header" "
" . "Simple merge" . "
\n"; # class="diff nodifferences" return $result; } # format patch (diff) line (not to be used for diff headers) sub format_diff_line { my $line = shift; my ($from, $to) = @_; my $diff_class = ""; chomp $line; if ($from && $to && ref($from->{'href'}) eq "ARRAY") { # combined diff my $prefix = substr($line, 0, scalar @{$from->{'href'}}); if ($line =~ m/^\@{3}/) { $diff_class = " chunk_header"; } elsif ($line =~ m/^\\/) { $diff_class = " incomplete"; } elsif ($prefix =~ tr/+/+/) { $diff_class = " add"; } elsif ($prefix =~ tr/-/-/) { $diff_class = " rem"; } } else { # assume ordinary diff my $char = substr($line, 0, 1); if ($char eq '+') { $diff_class = " add"; } elsif ($char eq '-') { $diff_class = " rem"; } elsif ($char eq '@') { $diff_class = " chunk_header"; } elsif ($char eq "\\") { $diff_class = " incomplete"; } } $line = untabify($line); if ($from && $to && $line =~ m/^\@{2} /) { my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) = $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/; $from_lines = 0 unless defined $from_lines; $to_lines = 0 unless defined $to_lines; if ($from->{'href'}) { $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start", -class=>"list"}, $from_text); } if ($to->{'href'}) { $to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start", -class=>"list"}, $to_text); } $line = "@@ $from_text $to_text @@" . "" . esc_html($section, -nbsp=>1) . ""; return "
$line
\n"; } elsif ($from && $to && $line =~ m/^\@{3}/) { my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/; my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines); @from_text = split(' ', $ranges); for (my $i = 0; $i < @from_text; ++$i) { ($from_start[$i], $from_nlines[$i]) = (split(',', substr($from_text[$i], 1)), 0); } $to_text = pop @from_text; $to_start = pop @from_start; $to_nlines = pop @from_nlines; $line = "$prefix "; for (my $i = 0; $i < @from_text; ++$i) { if ($from->{'href'}[$i]) { $line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]", -class=>"list"}, $from_text[$i]); } else { $line .= $from_text[$i]; } $line .= " "; } if ($to->{'href'}) { $line .= $cgi->a({-href=>"$to->{'href'}#l$to_start", -class=>"list"}, $to_text); } else { $line .= $to_text; } $line .= " $prefix" . "" . esc_html($section, -nbsp=>1) . ""; return "
$line
\n"; } return "
" . esc_html($line, -nbsp=>1) . "
\n"; } # Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)", # linked. Pass the hash of the tree/commit to snapshot. sub format_snapshot_links { my ($hash) = @_; my $num_fmts = @snapshot_fmts; if ($num_fmts > 1) { # A parenthesized list of links bearing format names. # e.g. "snapshot (_tar.gz_ _zip_)" return "snapshot (" . join(' ', map $cgi->a({ -href => href( action=>"snapshot", hash=>$hash, snapshot_format=>$_ ) }, $known_snapshot_formats{$_}{'display'}) , @snapshot_fmts) . ")"; } elsif ($num_fmts == 1) { # A single "snapshot" link whose tooltip bears the format name. # i.e. "_snapshot_" my ($fmt) = @snapshot_fmts; return $cgi->a({ -href => href( action=>"snapshot", hash=>$hash, snapshot_format=>$fmt ), -title => "in format: $known_snapshot_formats{$fmt}{'display'}" }, "snapshot"); } else { # $num_fmts == 0 return undef; } } ## ...................................................................... ## functions returning values to be passed, perhaps after some ## transformation, to other functions; e.g. returning arguments to href() # returns hash to be passed to href to generate gitweb URL # in -title key it returns description of link sub get_feed_info { my $format = shift || 'Atom'; my %res = (action => lc($format)); # feed links are possible only for project views return unless (defined $project); # some views should link to OPML, or to generic project feed, # or don't have specific feed yet (so they should use generic) return if ($action =~ /^(?:tags|heads|forks|tag|search)$/x); my $branch; # branches refs uses 'refs/heads/' prefix (fullname) to differentiate # from tag links; this also makes possible to detect branch links if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) || (defined $hash && $hash =~ m!^refs/heads/(.*)$!)) { $branch = $1; } # find log type for feed description (title) my $type = 'log'; if (defined $file_name) { $type = "history of $file_name"; $type .= "/" if ($action eq 'tree'); $type .= " on '$branch'" if (defined $branch); } else { $type = "log of $branch" if (defined $branch); } $res{-title} = $type; $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef); $res{'file_name'} = $file_name; return %res; } ## ---------------------------------------------------------------------- ## git utility subroutines, invoking git commands # returns path to the core git executable and the --git-dir parameter as list sub git_cmd { $number_of_git_cmds++; return $GIT, '--git-dir='.$git_dir; } # quote the given arguments for passing them to the shell # quote_command("command", "arg 1", "arg with ' and ! characters") # => "'command' 'arg 1' 'arg with '\'' and '\!' characters'" # Try to avoid using this function wherever possible. sub quote_command { return join(' ', map { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ ); } # get HEAD ref of given project as hash sub git_get_head_hash { return git_get_full_hash(shift, 'HEAD'); } sub git_get_full_hash { return git_get_hash(@_); } sub git_get_short_hash { return git_get_hash(@_, '--short=7'); } sub git_get_hash { my ($project, $hash, @options) = @_; my $o_git_dir = $git_dir; my $retval = undef; $git_dir = "$projectroot/$project"; if (open my $fd, '-|', git_cmd(), 'rev-parse', '--verify', '-q', @options, $hash) { $retval = <$fd>; chomp $retval if defined $retval; close $fd; } if (defined $o_git_dir) { $git_dir = $o_git_dir; } return $retval; } # get type of given object sub git_get_type { my $hash = shift; open my $fd, "-|", git_cmd(), "cat-file", '-t', $hash or return; my $type = <$fd>; close $fd or return; chomp $type; return $type; } # repository configuration our $config_file = ''; our %config; # store multiple values for single key as anonymous array reference # single values stored directly in the hash, not as [ ] sub hash_set_multi { my ($hash, $key, $value) = @_; if (!exists $hash->{$key}) { $hash->{$key} = $value; } elsif (!ref $hash->{$key}) { $hash->{$key} = [ $hash->{$key}, $value ]; } else { push @{$hash->{$key}}, $value; } } # return hash of git project configuration # optionally limited to some section, e.g. 'gitweb' sub git_parse_project_config { my $section_regexp = shift; my %config; local $/ = "\0"; open my $fh, "-|", git_cmd(), "config", '-z', '-l', or return; while (my $keyval = <$fh>) { chomp $keyval; my ($key, $value) = split(/\n/, $keyval, 2); hash_set_multi(\%config, $key, $value) if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o); } close $fh; return %config; } # convert config value to boolean: 'true' or 'false' # no value, number > 0, 'true' and 'yes' values are true # rest of values are treated as false (never as error) sub config_to_bool { my $val = shift; return 1 if !defined $val; # section.key # strip leading and trailing whitespace $val =~ s/^\s+//; $val =~ s/\s+$//; return (($val =~ /^\d+$/ && $val) || # section.key = 1 ($val =~ /^(?:true|yes)$/i)); # section.key = true } # convert config value to simple decimal number # an optional value suffix of 'k', 'm', or 'g' will cause the value # to be multiplied by 1024, 1048576, or 1073741824 sub config_to_int { my $val = shift; # strip leading and trailing whitespace $val =~ s/^\s+//; $val =~ s/\s+$//; if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) { $unit = lc($unit); # unknown unit is treated as 1 return $num * ($unit eq 'g' ? 1073741824 : $unit eq 'm' ? 1048576 : $unit eq 'k' ? 1024 : 1); } return $val; } # convert config value to array reference, if needed sub config_to_multi { my $val = shift; return ref($val) ? $val : (defined($val) ? [ $val ] : []); } sub git_get_project_config { my ($key, $type) = @_; return unless defined $git_dir; # key sanity check return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); # type sanity check if (defined $type) { $type =~ s/^--//; $type = undef unless ($type eq 'bool' || $type eq 'int'); } # get config if (!defined $config_file || $config_file ne "$git_dir/config") { %config = git_parse_project_config('gitweb'); $config_file = "$git_dir/config"; } # check if config variable (key) exists return unless exists $config{"gitweb.$key"}; # ensure given type if (!defined $type) { return $config{"gitweb.$key"}; } elsif ($type eq 'bool') { # backward compatibility: 'git config --bool' returns true/false return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false'; } elsif ($type eq 'int') { return config_to_int($config{"gitweb.$key"}); } return $config{"gitweb.$key"}; } # get hash of given path at given ref sub git_get_hash_by_path { my $base = shift; my $path = shift || return undef; my $type = shift; $path =~ s,/+$,,; open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path or die_error(500, "Open git-ls-tree failed"); my $line = <$fd>; close $fd or return undef; if (!defined $line) { # there is no tree or hash given by $path at $base return undef; } #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/; if (defined $type && $type ne $2) { # type doesn't match return undef; } return $3; } # get path of entry with given hash at given tree-ish (ref) # used to get 'from' filename for combined diff (merge commit) for renames sub git_get_path_by_hash { my $base = shift || return; my $hash = shift || return; local $/ = "\0"; open my $fd, "-|", git_cmd(), "ls-tree", '-r', '-t', '-z', $base or return undef; while (my $line = <$fd>) { chomp $line; #'040000 tree 595596a6a9117ddba9fe379b6b012b558bac8423 gitweb' #'100644 blob e02e90f0429be0d2a69b76571101f20b8f75530f gitweb/README' if ($line =~ m/(?:[0-9]+) (?:.+) $hash\t(.+)$/) { close $fd; return $1; } } close $fd; return undef; } ## ...................................................................... ## git utility functions, directly accessing git repository sub git_get_project_description { my $path = shift; $git_dir = "$projectroot/$path"; open my $fd, '<', "$git_dir/description" or return git_get_project_config('description'); my $descr = <$fd>; close $fd; if (defined $descr) { chomp $descr; } return $descr; } sub git_get_project_ctags { my $path = shift; my $ctags = {}; $git_dir = "$projectroot/$path"; opendir my $dh, "$git_dir/ctags" or return $ctags; foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh)) { open my $ct, '<', $_ or next; my $val = <$ct>; chomp $val; close $ct; my $ctag = $_; $ctag =~ s#.*/##; $ctags->{$ctag} = $val; } closedir $dh; $ctags; } sub git_populate_project_tagcloud { my $ctags = shift; # First, merge different-cased tags; tags vote on casing my %ctags_lc; foreach (keys %$ctags) { $ctags_lc{lc $_}->{count} += $ctags->{$_}; if (not $ctags_lc{lc $_}->{topcount} or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) { $ctags_lc{lc $_}->{topcount} = $ctags->{$_}; $ctags_lc{lc $_}->{topname} = $_; } } my $cloud; if (eval { require HTML::TagCloud; 1; }) { $cloud = HTML::TagCloud->new; foreach (sort keys %ctags_lc) { # Pad the title with spaces so that the cloud looks # less crammed. my $title = $ctags_lc{$_}->{topname}; $title =~ s/ / /g; $title =~ s/^/ /g; $title =~ s/$/ /g; $cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count}); } } else { $cloud = \%ctags_lc; } $cloud; } sub git_show_project_tagcloud { my ($cloud, $count) = @_; print STDERR ref($cloud)."..\n"; if (ref $cloud eq 'HTML::TagCloud') { return $cloud->html_and_css($count); } else { my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud; return '

' . join (', ', map { "$cloud->{$_}->{topname}" } splice(@tags, 0, $count)) . '

'; } } sub git_get_project_url_list { my $path = shift; $git_dir = "$projectroot/$path"; open my $fd, '<', "$git_dir/cloneurl" or return wantarray ? @{ config_to_multi(git_get_project_config('url')) } : config_to_multi(git_get_project_config('url')); my @git_project_url_list = map { chomp; $_ } <$fd>; close $fd; return wantarray ? @git_project_url_list : \@git_project_url_list; } sub git_get_projects_list { my ($filter) = @_; my @list; $filter ||= ''; $filter =~ s/\.git$//; my $check_forks = gitweb_check_feature('forks'); if (-d $projects_list) { # search in directory my $dir = $projects_list . ($filter ? "/$filter" : ''); # remove the trailing "/" $dir =~ s!/+$!!; my $pfxlen = length("$dir"); my $pfxdepth = ($dir =~ tr!/!!); File::Find::find({ follow_fast => 1, # follow symbolic links follow_skip => 2, # ignore duplicates dangling_symlinks => 0, # ignore dangling symlinks, silently wanted => sub { # global variables our $project_maxdepth; our $projectroot; # skip project-list toplevel, if we get it. return if (m!^[/.]$!); # only directories can be git repositories return unless (-d $_); # don't traverse too deep (Find is super slow on os x) if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) { $File::Find::prune = 1; return; } my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot my $path = ($filter ? "$filter/" : '') . $subdir; if (check_export_ok("$projectroot/$path")) { push @list, { path => $path }; $File::Find::prune = 1; } }, }, "$dir"); } elsif (-f $projects_list) { # read from file(url-encoded): # 'git%2Fgit.git Linus+Torvalds' # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' my %paths; open my $fd, '<', $projects_list or return; PROJECT: while (my $line = <$fd>) { chomp $line; my ($path, $owner) = split ' ', $line; $path = unescape($path); $owner = unescape($owner); if (!defined $path) { next; } if ($filter ne '') { # looking for forks; my $pfx = substr($path, 0, length($filter)); if ($pfx ne $filter) { next PROJECT; } my $sfx = substr($path, length($filter)); if ($sfx !~ /^\/.*\.git$/) { next PROJECT; } } elsif ($check_forks) { PATH: foreach my $filter (keys %paths) { # looking for forks; my $pfx = substr($path, 0, length($filter)); if ($pfx ne $filter) { next PATH; } my $sfx = substr($path, length($filter)); if ($sfx !~ /^\/.*\.git$/) { next PATH; } # is a fork, don't include it in # the list next PROJECT; } } if (check_export_ok("$projectroot/$path")) { my $pr = { path => $path, owner => to_utf8($owner), }; push @list, $pr; (my $forks_path = $path) =~ s/\.git$//; $paths{$forks_path}++; } } close $fd; } return @list; } our $gitweb_project_owner = undef; sub git_get_project_list_from_file { return if (defined $gitweb_project_owner); $gitweb_project_owner = {}; # read from file (url-encoded): # 'git%2Fgit.git Linus+Torvalds' # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' if (-f $projects_list) { open(my $fd, '<', $projects_list); while (my $line = <$fd>) { chomp $line; my ($pr, $ow) = split ' ', $line; $pr = unescape($pr); $ow = unescape($ow); $gitweb_project_owner->{$pr} = to_utf8($ow); } close $fd; } } sub git_get_project_owner { my $project = shift; my $owner; return undef unless $project; $git_dir = "$projectroot/$project"; if (!defined $gitweb_project_owner) { git_get_project_list_from_file(); } if (exists $gitweb_project_owner->{$project}) { $owner = $gitweb_project_owner->{$project}; } if (!defined $owner){ $owner = git_get_project_config('owner'); } if (!defined $owner) { $owner = get_file_owner("$git_dir"); } return $owner; } sub git_get_last_activity { my ($path) = @_; my $fd; $git_dir = "$projectroot/$path"; open($fd, "-|", git_cmd(), 'for-each-ref', '--format=%(committer)', '--sort=-committerdate', '--count=1', 'refs/heads') or return; my $most_recent = <$fd>; close $fd or return; if (defined $most_recent && $most_recent =~ / (\d+) [-+][01]\d\d\d$/) { my $timestamp = $1; my $age = time - $timestamp; return ($age, age_string($age)); } return (undef, undef); } sub git_get_references { my $type = shift || ""; my %refs; # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} open my $fd, "-|", git_cmd(), "show-ref", "--dereference", ($type ? ("--", "refs/$type") : ()) # use -- if $type or return; while (my $line = <$fd>) { chomp $line; if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { if (defined $refs{$1}) { push @{$refs{$1}}, $2; } else { $refs{$1} = [ $2 ]; } } } close $fd or return; return \%refs; } sub git_get_rev_name_tags { my $hash = shift || return undef; open my $fd, "-|", git_cmd(), "name-rev", "--tags", $hash or return; my $name_rev = <$fd>; close $fd; if ($name_rev =~ m|^$hash tags/(.*)$|) { return $1; } else { # catches also '$hash undefined' output return undef; } } ## ---------------------------------------------------------------------- ## parse to hash functions sub parse_date { my $epoch = shift; my $tz = shift || "-0000"; my %date; my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch); $date{'hour'} = $hour; $date{'minute'} = $min; $date{'mday'} = $mday; $date{'day'} = $days[$wday]; $date{'month'} = $months[$mon]; $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", 1900+$year, 1+$mon, $mday, $hour ,$min, $sec; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); $date{'hour_local'} = $hour; $date{'minute_local'} = $min; $date{'tz_local'} = $tz; $date{'iso-tz'} = sprintf("%04d-%02d-%02d %02d:%02d:%02d %s", 1900+$year, $mon+1, $mday, $hour, $min, $sec, $tz); return %date; } sub parse_tag { my $tag_id = shift; my %tag; my @comment; open my $fd, "-|", git_cmd(), "cat-file", "tag", $tag_id or return; $tag{'id'} = $tag_id; while (my $line = <$fd>) { chomp $line; if ($line =~ m/^object ([0-9a-fA-F]{40})$/) { $tag{'object'} = $1; } elsif ($line =~ m/^type (.+)$/) { $tag{'type'} = $1; } elsif ($line =~ m/^tag (.+)$/) { $tag{'name'} = $1; } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) { $tag{'author'} = $1; $tag{'author_epoch'} = $2; $tag{'author_tz'} = $3; if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) { $tag{'author_name'} = $1; $tag{'author_email'} = $2; } else { $tag{'author_name'} = $tag{'author'}; } } elsif ($line =~ m/--BEGIN/) { push @comment, $line; last; } elsif ($line eq "") { last; } } push @comment, <$fd>; $tag{'comment'} = \@comment; close $fd or return; if (!defined $tag{'name'}) { return }; return %tag } sub parse_commit_text { my ($commit_text, $withparents) = @_; my @commit_lines = split '\n', $commit_text; my %co; pop @commit_lines; # Remove '\0' if (! @commit_lines) { return; } my $header = shift @commit_lines; if ($header !~ m/^[0-9a-fA-F]{40}/) { return; } ($co{'id'}, my @parents) = split ' ', $header; while (my $line = shift @commit_lines) { last if $line eq "\n"; if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) { $co{'tree'} = $1; } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) { push @parents, $1; } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) { $co{'author'} = to_utf8($1); $co{'author_epoch'} = $2; $co{'author_tz'} = $3; if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) { $co{'author_name'} = $1; $co{'author_email'} = $2; } else { $co{'author_name'} = $co{'author'}; } } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) { $co{'committer'} = to_utf8($1); $co{'committer_epoch'} = $2; $co{'committer_tz'} = $3; if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) { $co{'committer_name'} = $1; $co{'committer_email'} = $2; } else { $co{'committer_name'} = $co{'committer'}; } } } if (!defined $co{'tree'}) { return; }; $co{'parents'} = \@parents; $co{'parent'} = $parents[0]; foreach my $title (@commit_lines) { $title =~ s/^ //; if ($title ne "") { $co{'title'} = chop_str($title, 80, 5); # remove leading stuff of merges to make the interesting part visible if (length($title) > 50) { $title =~ s/^Automatic //; $title =~ s/^merge (of|with) /Merge ... /i; if (length($title) > 50) { $title =~ s/(http|rsync):\/\///; } if (length($title) > 50) { $title =~ s/(master|www|rsync)\.//; } if (length($title) > 50) { $title =~ s/kernel.org:?//; } if (length($title) > 50) { $title =~ s/\/pub\/scm//; } } $co{'title_short'} = chop_str($title, 50, 5); last; } } if (! defined $co{'title'} || $co{'title'} eq "") { $co{'title'} = $co{'title_short'} = '(no commit message)'; } # remove added spaces foreach my $line (@commit_lines) { $line =~ s/^ //; } $co{'comment'} = \@commit_lines; my $age = time - $co{'committer_epoch'}; $co{'age'} = $age; $co{'age_string'} = age_string($age); my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'}); if ($age > 60*60*24*7*2) { $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; $co{'age_string_age'} = $co{'age_string'}; } else { $co{'age_string_date'} = $co{'age_string'}; $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; } return %co; } sub parse_commit { my ($commit_id) = @_; my %co; local $/ = "\0"; open my $fd, "-|", git_cmd(), "rev-list", "--parents", "--header", "--max-count=1", $commit_id, "--", or die_error(500, "Open git-rev-list failed"); %co = parse_commit_text(<$fd>, 1); close $fd; return %co; } sub parse_commits { my ($commit_id, $maxcount, $skip, $filename, @args) = @_; my @cos; $maxcount ||= 1; $skip ||= 0; local $/ = "\0"; open my $fd, "-|", git_cmd(), "rev-list", "--header", @args, ("--max-count=" . $maxcount), ("--skip=" . $skip), @extra_options, $commit_id, "--", ($filename ? ($filename) : ()) or die_error(500, "Open git-rev-list failed"); while (my $line = <$fd>) { my %co = parse_commit_text($line); push @cos, \%co; } close $fd; return wantarray ? @cos : \@cos; } # parse line of git-diff-tree "raw" output sub parse_difftree_raw_line { my $line = shift; my %res; # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) { $res{'from_mode'} = $1; $res{'to_mode'} = $2; $res{'from_id'} = $3; $res{'to_id'} = $4; $res{'status'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7); } } # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' # combined diff (for merge commit) elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) { $res{'nparents'} = length($1); $res{'from_mode'} = [ split(' ', $2) ]; $res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'from_id'} = [ split(' ', $3) ]; $res{'to_id'} = pop @{$res{'from_id'}}; $res{'status'} = [ split('', $4) ]; $res{'to_file'} = unquote($5); } # 'c512b523472485aef4fff9e57b229d9d243c967f' elsif ($line =~ m/^([0-9a-fA-F]{40})$/) { $res{'commit'} = $1; } return wantarray ? %res : \%res; } # wrapper: return parsed line of git-diff-tree "raw" output # (the argument might be raw line, or parsed info) sub parsed_difftree_line { my $line_or_ref = shift; if (ref($line_or_ref) eq "HASH") { # pre-parsed (or generated by hand) return $line_or_ref; } else { return parse_difftree_raw_line($line_or_ref); } } # parse line of git-ls-tree output sub parse_ls_tree_line { my $line = shift; my %opts = @_; my %res; if ($opts{'-l'}) { #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa 16717 panic.c' $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s; $res{'mode'} = $1; $res{'type'} = $2; $res{'hash'} = $3; $res{'size'} = $4; if ($opts{'-z'}) { $res{'name'} = $5; } else { $res{'name'} = unquote($5); } } else { #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s; $res{'mode'} = $1; $res{'type'} = $2; $res{'hash'} = $3; if ($opts{'-z'}) { $res{'name'} = $4; } else { $res{'name'} = unquote($4); } } return wantarray ? %res : \%res; } # generates _two_ hashes, references to which are passed as 2 and 3 argument sub parse_from_to_diffinfo { my ($diffinfo, $from, $to, @parents) = @_; if ($diffinfo->{'nparents'}) { # combined diff $from->{'file'} = []; $from->{'href'} = []; fill_from_file_info($diffinfo, @parents) unless exists $diffinfo->{'from_file'}; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { $from->{'file'}[$i] = defined $diffinfo->{'from_file'}[$i] ? $diffinfo->{'from_file'}[$i] : $diffinfo->{'to_file'}; if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file $from->{'href'}[$i] = href(action=>"blob", hash_base=>$parents[$i], hash=>$diffinfo->{'from_id'}[$i], file_name=>$from->{'file'}[$i]); } else { $from->{'href'}[$i] = undef; } } } else { # ordinary (not combined) diff $from->{'file'} = $diffinfo->{'from_file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, file_name=>$from->{'file'}); } else { delete $from->{'href'}; } } $to->{'file'} = $diffinfo->{'to_file'}; if (!is_deleted($diffinfo)) { # file exists in result $to->{'href'} = href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, file_name=>$to->{'file'}); } else { delete $to->{'href'}; } } ## ...................................................................... ## parse to array of hashes functions sub git_get_heads_list { my $limit = shift; my @headslist; open my $fd, '-|', git_cmd(), 'for-each-ref', ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate', '--format=%(objectname) %(refname) %(subject)%00%(committer)', 'refs/heads' or return; while (my $line = <$fd>) { my %ref_item; chomp $line; my ($refinfo, $committerinfo) = split(/\0/, $line); my ($hash, $name, $title) = split(' ', $refinfo, 3); my ($committer, $epoch, $tz) = ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); $ref_item{'fullname'} = $name; $name =~ s!^refs/heads/!!; $ref_item{'name'} = $name; $ref_item{'id'} = $hash; $ref_item{'title'} = $title || '(no commit message)'; $ref_item{'epoch'} = $epoch; if ($epoch) { $ref_item{'age'} = age_string(time - $ref_item{'epoch'}); } else { $ref_item{'age'} = "unknown"; } push @headslist, \%ref_item; } close $fd; return wantarray ? @headslist : \@headslist; } sub git_get_tags_list { my $limit = shift; my @tagslist; open my $fd, '-|', git_cmd(), 'for-each-ref', ($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate', '--format=%(objectname) %(objecttype) %(refname) '. '%(*objectname) %(*objecttype) %(subject)%00%(creator)', 'refs/tags' or return; while (my $line = <$fd>) { my %ref_item; chomp $line; my ($refinfo, $creatorinfo) = split(/\0/, $line); my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6); my ($creator, $epoch, $tz) = ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/); $ref_item{'fullname'} = $name; $name =~ s!^refs/tags/!!; $ref_item{'type'} = $type; $ref_item{'id'} = $id; $ref_item{'name'} = $name; if ($type eq "tag") { $ref_item{'subject'} = $title; $ref_item{'reftype'} = $reftype; $ref_item{'refid'} = $refid; } else { $ref_item{'reftype'} = $type; $ref_item{'refid'} = $id; } if ($type eq "tag" || $type eq "commit") { $ref_item{'epoch'} = $epoch; if ($epoch) { $ref_item{'age'} = age_string(time - $ref_item{'epoch'}); } else { $ref_item{'age'} = "unknown"; } } push @tagslist, \%ref_item; } close $fd; return wantarray ? @tagslist : \@tagslist; } ## ---------------------------------------------------------------------- ## filesystem-related functions sub get_file_owner { my $path = shift; my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path); my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid); if (!defined $gcos) { return undef; } my $owner = $gcos; $owner =~ s/[,;].*$//; return to_utf8($owner); } # assume that file exists sub insert_file { my $filename = shift; open my $fd, '<', $filename; print map { to_utf8($_) } <$fd>; close $fd; } ## ...................................................................... ## mimetype related functions sub mimetype_guess_file { my $filename = shift; my $mimemap = shift; -r $mimemap or return undef; my %mimemap; open(my $mh, '<', $mimemap) or return undef; while (<$mh>) { next if m/^#/; # skip comments my ($mimetype, $exts) = split(/\t+/); if (defined $exts) { my @exts = split(/\s+/, $exts); foreach my $ext (@exts) { $mimemap{$ext} = $mimetype; } } } close($mh); $filename =~ /\.([^.]*)$/; return $mimemap{$1}; } sub mimetype_guess { my $filename = shift; my $mime; $filename =~ /\./ or return undef; if ($mimetypes_file) { my $file = $mimetypes_file; if ($file !~ m!^/!) { # if it is relative path # it is relative to project $file = "$projectroot/$project/$file"; } $mime = mimetype_guess_file($filename, $file); } $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); return $mime; } sub blob_mimetype { my $fd = shift; my $filename = shift; if ($filename) { my $mime = mimetype_guess($filename); $mime and return $mime; } # just in case return $default_blob_plain_mimetype unless $fd; if (-T $fd) { return 'text/plain'; } elsif (! $filename) { return 'application/octet-stream'; } elsif ($filename =~ m/\.png$/i) { return 'image/png'; } elsif ($filename =~ m/\.gif$/i) { return 'image/gif'; } elsif ($filename =~ m/\.jpe?g$/i) { return 'image/jpeg'; } else { return 'application/octet-stream'; } } sub blob_contenttype { my ($fd, $file_name, $type) = @_; $type ||= blob_mimetype($fd, $file_name); if ($type eq 'text/plain' && defined $default_text_plain_charset) { $type .= "; charset=$default_text_plain_charset"; } return $type; } # guess file syntax for syntax highlighting; return undef if no highlighting # the name of syntax can (in the future) depend on syntax highlighter used sub guess_file_syntax { my ($highlight, $mimetype, $file_name) = @_; return undef unless ($highlight && defined $file_name); # configuration for 'highlight' (http://www.andre-simon.de/) # match by basename my %highlight_basename = ( #'Program' => 'py', #'Library' => 'py', 'SConstruct' => 'py', # SCons equivalent of Makefile 'Makefile' => 'make', ); # match by extension my %highlight_ext = ( # main extensions, defining name of syntax; # see files in /usr/share/highlight/langDefs/ directory map { $_ => $_ } qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), # alternate extensions, see /etc/highlight/filetypes.conf 'h' => 'c', map { $_ => 'cpp' } qw(cxx c++ cc), map { $_ => 'php' } qw(php3 php4), map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' 'mak' => 'make', map { $_ => 'xml' } qw(xhtml html htm), ); my $basename = basename($file_name, '.in'); return $highlight_basename{$basename} if exists $highlight_basename{$basename}; $basename =~ /\.([^.]*)$/; my $ext = $1 or return undef; return $highlight_ext{$ext} if exists $highlight_ext{$ext}; return undef; } # run highlighter and return FD of its output, # or return original FD if no highlighting sub run_highlighter { my ($fd, $highlight, $syntax) = @_; return $fd unless ($highlight && defined $syntax); close $fd or die_error(404, "Reading blob failed"); open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". "highlight --xhtml --fragment --syntax $syntax |" or die_error(500, "Couldn't open file or run syntax highlighter"); return $fd; } ## ====================================================================== ## functions printing HTML: header, footer, error page sub get_page_title { my $title = to_utf8($site_name); return $title unless (defined $project); $title .= " - " . to_utf8($project); return $title unless (defined $action); $title .= "/$action"; # $action is US-ASCII (7bit ASCII) return $title unless (defined $file_name); $title .= " - " . esc_path($file_name); if ($action eq "tree" && $file_name !~ m|/$|) { $title .= "/"; } return $title; } sub git_header_html { my $status = shift || "200 OK"; my $expires = shift; my %opts = @_; my $title = get_page_title(); my $content_type; # require explicit support from the UA if we are to send the page as # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. # we have to do this because MSIE sometimes globs '*/*', pretending to # support xhtml+xml but choking when it gets what it asked for. if (defined $cgi->http('HTTP_ACCEPT') && $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { $content_type = 'application/xhtml+xml'; } else { $content_type = 'text/html'; } print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires) unless ($opts{'-no_http_header'}); my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : ''; print < $title EOF # the stylesheet, favicon etc urls won't work correctly with path_info # unless we set the appropriate base URL if ($ENV{'PATH_INFO'}) { print "\n"; } # print out each stylesheet that exist, providing backwards capability # for those people who defined $stylesheet in a config file if (defined $stylesheet) { print ''."\n"; } else { foreach my $stylesheet (@stylesheets) { next unless $stylesheet; print ''."\n"; } } if (defined $project) { my %href_params = get_feed_info(); if (!exists $href_params{'-title'}) { $href_params{'-title'} = 'log'; } foreach my $format qw(RSS Atom) { my $type = lc($format); my %link_attr = ( '-rel' => 'alternate', '-title' => "$project - $href_params{'-title'} - $format feed", '-type' => "application/$type+xml" ); $href_params{'action'} = $type; $link_attr{'-href'} = href(%href_params); print "\n"; $href_params{'extra_options'} = '--no-merges'; $link_attr{'-href'} = href(%href_params); $link_attr{'-title'} .= ' (no merges)'; print "\n"; } } else { printf(''."\n", $site_name, href(project=>undef, action=>"project_index")); printf(''."\n", $site_name, href(project=>undef, action=>"opml")); } if (defined $favicon) { print qq(\n); } print "\n" . "\n"; if (defined $site_header && -f $site_header) { insert_file($site_header); } print "
\n" . $cgi->a({-href => esc_url($logo_url), -title => $logo_label}, qq()); print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); if (defined $action) { print " / $action"; } print "\n"; } print "
\n"; my $have_search = gitweb_check_feature('search'); if (defined $project && $have_search) { if (!defined $searchtext) { $searchtext = ""; } my $search_hash; if (defined $hash_base) { $search_hash = $hash_base; } elsif (defined $hash) { $search_hash = $hash; } else { $search_hash = "HEAD"; } my $action = $my_uri; my $use_pathinfo = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { $action .= "/".esc_url($project); } print $cgi->startform(-method => "get", -action => $action) . "
\n" . (!$use_pathinfo && $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") . $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" . $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" . $cgi->popup_menu(-name => 'st', -default => 'commit', -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) . $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) . " search:\n", $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . "" . $cgi->checkbox(-name => 'sr', -value => 1, -label => 're', -checked => $search_use_regexp) . "" . "
" . $cgi->end_form() . "\n"; } } sub git_footer_html { my $feed_class = 'rss_logo'; print "
\n"; if (defined $project) { my $descr = git_get_project_description($project); if (defined $descr) { print "\n"; } my %href_params = get_feed_info(); if (!%href_params) { $feed_class .= ' generic'; } $href_params{'-title'} ||= 'log'; foreach my $format qw(RSS Atom) { $href_params{'action'} = lc($format); print $cgi->a({-href => href(%href_params), -title => "$href_params{'-title'} $format feed", -class => $feed_class}, $format)."\n"; } } else { print $cgi->a({-href => href(project=>undef, action=>"opml"), -class => $feed_class}, "OPML") . " "; print $cgi->a({-href => href(project=>undef, action=>"project_index"), -class => $feed_class}, "TXT") . "\n"; } print "
\n"; # class="page_footer" if (defined $t0 && gitweb_check_feature('timed')) { print "
\n"; print 'This page took '. ''. Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]). ' seconds '. ' and '. ''. $number_of_git_cmds. ' git commands '. " to generate.\n"; print "
\n"; # class="page_footer" } if (defined $site_footer && -f $site_footer) { insert_file($site_footer); } print qq!\n!; if (defined $action && $action eq 'blame_incremental') { print qq!\n!; } elsif (gitweb_check_feature('javascript-actions')) { print qq!\n!; } print "\n" . ""; } # die_error(, [, ]) # Example: die_error(404, 'Hash not found') # By convention, use the following status codes (as defined in RFC 2616): # 400: Invalid or missing CGI parameters, or # requested object exists but has wrong type. # 403: Requested feature (like "pickaxe" or "snapshot") not enabled on # this server or project. # 404: Requested object/revision/project doesn't exist. # 500: The server isn't configured properly, or # an internal error occurred (e.g. failed assertions caused by bugs), or # an unknown error occurred (e.g. the git binary died unexpectedly). # 503: The server is currently unavailable (because it is overloaded, # or down for maintenance). Generally, this is a temporary state. sub die_error { my $status = shift || 500; my $error = esc_html(shift) || "Internal Server Error"; my $extra = shift; my %opts = @_; my %http_responses = ( 400 => '400 Bad Request', 403 => '403 Forbidden', 404 => '404 Not Found', 500 => '500 Internal Server Error', 503 => '503 Service Unavailable', ); git_header_html($http_responses{$status}, undef, %opts); print <

$status - $error
EOF if (defined $extra) { print "
\n" . "$extra\n"; } print "\n"; git_footer_html(); goto DONE_GITWEB unless ($opts{'-error_handler'}); } ## ---------------------------------------------------------------------- ## functions printing or outputting HTML: navigation sub git_print_page_nav { my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; $extra = '' if !defined $extra; # pager or formats my @navs = qw(summary shortlog log commit commitdiff tree); if ($suppress) { @navs = grep { $_ ne $suppress } @navs; } my %arg = map { $_ => {action=>$_} } @navs; if (defined $head) { for (qw(commit commitdiff)) { $arg{$_}{'hash'} = $head; } if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { for (qw(shortlog log)) { $arg{$_}{'hash'} = $head; } } } $arg{'tree'}{'hash'} = $treehead if defined $treehead; $arg{'tree'}{'hash_base'} = $treebase if defined $treebase; my @actions = gitweb_get_feature('actions'); my %repl = ( '%' => '%', 'n' => $project, # project name 'f' => $git_dir, # project path within filesystem 'h' => $treehead || '', # current hash ('h' parameter) 'b' => $treebase || '', # hash base ('hb' parameter) ); while (@actions) { my ($label, $link, $pos) = splice(@actions,0,3); # insert @navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs; # munch munch $link =~ s/%([%nfhb])/$repl{$1}/g; $arg{$label}{'_href'} = $link; } print "
\n" . (join " | ", map { $_ eq $current ? $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_") } @navs); print "
\n$extra
\n" . "
\n"; } sub format_paging_nav { my ($action, $page, $has_next_link) = @_; my $paging_nav; if ($page > 0) { $paging_nav .= $cgi->a({-href => href(-replay=>1, page=>undef)}, "first") . " ⋅ " . $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first ⋅ prev"; } if ($has_next_link) { $paging_nav .= " ⋅ " . $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } else { $paging_nav .= " ⋅ next"; } return $paging_nav; } ## ...................................................................... ## functions printing or outputting HTML: div sub git_print_header_div { my ($action, $title, $hash, $hash_base) = @_; my %args = (); $args{'action'} = $action; $args{'hash'} = $hash if $hash; $args{'hash_base'} = $hash_base if $hash_base; print "
\n" . $cgi->a({-href => href(%args), -class => "title"}, $title ? $title : $action) . "\n
\n"; } sub print_local_time { print format_local_time(@_); } sub format_local_time { my $localtime = ''; my %date = @_; if ($date{'hour_local'} < 6) { $localtime .= sprintf(" (%02d:%02d %s)", $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'}); } else { $localtime .= sprintf(" (%02d:%02d %s)", $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'}); } return $localtime; } # Outputs the author name and date in long form sub git_print_authorship { my $co = shift; my %opts = @_; my $tag = $opts{-tag} || 'div'; my $author = $co->{'author_name'}; my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'}); print "<$tag class=\"author_date\">" . format_search_author($author, "author", esc_html($author)) . " [$ad{'rfc2822'}"; print_local_time(%ad) if ($opts{-localtime}); print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1) . "\n"; } # Outputs table rows containing the full author or committer information, # in the format expected for 'commit' view (& similia). # Parameters are a commit hash reference, followed by the list of people # to output information for. If the list is empty it defalts to both # author and committer. sub git_print_authorship_rows { my $co = shift; # too bad we can't use @people = @_ || ('author', 'committer') my @people = @_; @people = ('author', 'committer') unless @people; foreach my $who (@people) { my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"}); print "$who" . format_search_author($co->{"${who}_name"}, $who, esc_html($co->{"${who}_name"})) . " " . format_search_author($co->{"${who}_email"}, $who, esc_html("<" . $co->{"${who}_email"} . ">")) . "" . git_get_avatar($co->{"${who}_email"}, -size => 'double') . "\n" . "" . " $wd{'rfc2822'}"; print_local_time(%wd); print "" . "\n"; } } sub git_print_page_path { my $name = shift; my $type = shift; my $hb = shift; print "
"; print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), -title => 'tree root'}, to_utf8("[$project]")); print " / "; if (defined $name) { my @dirname = split '/', $name; my $basename = pop @dirname; my $fullname = ''; foreach my $dir (@dirname) { $fullname .= ($fullname ? '/' : '') . $dir; print $cgi->a({-href => href(action=>"tree", file_name=>$fullname, hash_base=>$hb), -title => $fullname}, esc_path($dir)); print " / "; } if (defined $type && $type eq 'blob') { print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name, hash_base=>$hb), -title => $name}, esc_path($basename)); } elsif (defined $type && $type eq 'tree') { print $cgi->a({-href => href(action=>"tree", file_name=>$file_name, hash_base=>$hb), -title => $name}, esc_path($basename)); print " / "; } else { print esc_path($basename); } } print "
\n"; } sub git_print_log { my $log = shift; my %opts = @_; if ($opts{'-remove_title'}) { # remove title, i.e. first line of log shift @$log; } # remove leading empty lines while (defined $log->[0] && $log->[0] eq "") { shift @$log; } # print log my $signoff = 0; my $empty = 0; foreach my $line (@$log) { if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { $signoff = 1; $empty = 0; if (! $opts{'-remove_signoff'}) { print "" . esc_html($line) . "
\n"; next; } else { # remove signoff lines next; } } else { $signoff = 0; } # print only one empty line # do not print empty line after signoff if ($line eq "") { next if ($empty || $signoff); $empty = 1; } else { $empty = 0; } print format_log_line_html($line) . "
\n"; } if ($opts{'-final_empty_line'}) { # end with single empty line print "
\n" unless $empty; } } # return link target (what link points to) sub git_get_link_target { my $hash = shift; my $link_target; # read link open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or return; { local $/ = undef; $link_target = <$fd>; } close $fd or return; return $link_target; } # given link target, and the directory (basedir) the link is in, # return target of link relative to top directory (top tree); # return undef if it is not possible (including absolute links). sub normalize_link_target { my ($link_target, $basedir) = @_; # absolute symlinks (beginning with '/') cannot be normalized return if (substr($link_target, 0, 1) eq '/'); # normalize link target to path from top (root) tree (dir) my $path; if ($basedir) { $path = $basedir . '/' . $link_target; } else { # we are in top (root) tree (dir) $path = $link_target; } # remove //, /./, and /../ my @path_parts; foreach my $part (split('/', $path)) { # discard '.' and '' next if (!$part || $part eq '.'); # handle '..' if ($part eq '..') { if (@path_parts) { pop @path_parts; } else { # link leads outside repository (outside top dir) return; } } else { push @path_parts, $part; } } $path = join('/', @path_parts); return $path; } # print tree entry (row of git_tree), but without encompassing element sub git_print_tree_entry { my ($t, $basedir, $hash_base, $have_blame) = @_; my %base_key = (); $base_key{'hash_base'} = $hash_base if defined $hash_base; # The format of a table row is: mode list link. Where mode is # the mode of the entry, list is the name of the entry, an href, # and link is the action links of the entry. print "" . mode_str($t->{'mode'}) . "\n"; if (exists $t->{'size'}) { print "$t->{'size'}\n"; } if ($t->{'type'} eq "blob") { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key), -class => "list"}, esc_path($t->{'name'})); if (S_ISLNK(oct $t->{'mode'})) { my $link_target = git_get_link_target($t->{'hash'}); if ($link_target) { my $norm_target = normalize_link_target($link_target, $basedir); if (defined $norm_target) { print " -> " . $cgi->a({-href => href(action=>"object", hash_base=>$hash_base, file_name=>$norm_target), -title => $norm_target}, esc_path($link_target)); } else { print " -> " . esc_path($link_target); } } } print "\n"; print ""; print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)}, "blob"); if ($have_blame) { print " | " . $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)}, "blame"); } if (defined $hash_base) { print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")}, "history"); } print " | " . $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base, file_name=>"$basedir$t->{'name'}")}, "raw"); print "\n"; } elsif ($t->{'type'} eq "tree") { print ""; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)}, esc_path($t->{'name'})); print "\n"; print ""; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)}, "tree"); if (defined $hash_base) { print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$basedir$t->{'name'}")}, "history"); } print "\n"; } else { # unknown object: we can only present history for it # (this includes 'commit' object, i.e. submodule support) print "" . esc_path($t->{'name'}) . "\n"; print ""; if (defined $hash_base) { print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$basedir$t->{'name'}")}, "history"); } print "\n"; } } ## ...................................................................... ## functions printing large fragments of HTML # get pre-image filenames for merge (combined) diff sub fill_from_file_info { my ($diff, @parents) = @_; $diff->{'from_file'} = [ ]; $diff->{'from_file'}[$diff->{'nparents'} - 1] = undef; for (my $i = 0; $i < $diff->{'nparents'}; $i++) { if ($diff->{'status'}[$i] eq 'R' || $diff->{'status'}[$i] eq 'C') { $diff->{'from_file'}[$i] = git_get_path_by_hash($parents[$i], $diff->{'from_id'}[$i]); } } return $diff; } # is current raw difftree line of file deletion sub is_deleted { my $diffinfo = shift; return $diffinfo->{'to_id'} eq ('0' x 40); } # does patch correspond to [previous] difftree raw line # $diffinfo - hashref of parsed raw diff format # $patchinfo - hashref of parsed patch diff format # (the same keys as in $diffinfo) sub is_patch_split { my ($diffinfo, $patchinfo) = @_; return defined $diffinfo && defined $patchinfo && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'}; } sub git_difftree_body { my ($difftree, $hash, @parents) = @_; my ($parent) = $parents[0]; my $have_blame = gitweb_check_feature('blame'); print "
\n"; if ($#{$difftree} > 10) { print(($#{$difftree} + 1) . " files changed:\n"); } print "
\n"; print " 1 ? "combined " : "") . "diff_tree\">\n"; # header only for combined diff in 'commitdiff' view my $has_header = @$difftree && @parents > 1 && $action eq 'commitdiff'; if ($has_header) { # table header print "\n" . "\n"; # filename, patchN link for (my $i = 0; $i < @parents; $i++) { my $par = $parents[$i]; print "\n"; } print "\n\n"; } my $alternate = 1; my $patchno = 0; foreach my $line (@{$difftree}) { my $diff = parsed_difftree_line($line); if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; if (exists $diff->{'nparents'}) { # combined diff fill_from_file_info($diff, @parents) unless exists $diff->{'from_file'}; if (!is_deleted($diff)) { # file exists in the result (child) commit print "\n"; } else { print "\n"; } if ($action eq 'commitdiff') { # link to patch $patchno++; print "\n"; } my $has_history = 0; my $not_deleted = 0; for (my $i = 0; $i < $diff->{'nparents'}; $i++) { my $hash_parent = $parents[$i]; my $from_hash = $diff->{'from_id'}[$i]; my $from_path = $diff->{'from_file'}[$i]; my $status = $diff->{'status'}[$i]; $has_history ||= ($status ne 'A'); $not_deleted ||= ($status ne 'D'); if ($status eq 'A') { print "\n"; } elsif ($status eq 'D') { print "\n"; } else { if ($diff->{'to_id'} eq $from_hash) { print "\n"; } } print "\n"; print "\n"; next; # instead of 'else' clause, to avoid extra indent } # else ordinary diff my ($to_mode_oct, $to_mode_str, $to_file_type); my ($from_mode_oct, $from_mode_str, $from_file_type); if ($diff->{'to_mode'} ne ('0' x 6)) { $to_mode_oct = oct $diff->{'to_mode'}; if (S_ISREG($to_mode_oct)) { # only for regular file $to_mode_str = sprintf("%04o", $to_mode_oct & 0777); # permission bits } $to_file_type = file_type($diff->{'to_mode'}); } if ($diff->{'from_mode'} ne ('0' x 6)) { $from_mode_oct = oct $diff->{'from_mode'}; if (S_ISREG($to_mode_oct)) { # only for regular file $from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits } $from_file_type = file_type($diff->{'from_mode'}); } if ($diff->{'status'} eq "A") { # created my $mode_chng = "[new $to_file_type"; $mode_chng .= " with mode: $to_mode_str" if $to_mode_str; $mode_chng .= "]"; print "\n"; print "\n"; print "\n"; } elsif ($diff->{'status'} eq "D") { # deleted my $mode_chng = "[deleted $from_file_type]"; print "\n"; print "\n"; print "\n"; } elsif ($diff->{'status'} eq "M" || $diff->{'status'} eq "T") { # modified, or type changed my $mode_chnge = ""; if ($diff->{'from_mode'} != $diff->{'to_mode'}) { $mode_chnge = "[changed"; if ($from_file_type ne $to_file_type) { $mode_chnge .= " from $from_file_type to $to_file_type"; } if (($from_mode_oct & 0777) != ($to_mode_oct & 0777)) { if ($from_mode_str && $to_mode_str) { $mode_chnge .= " mode: $from_mode_str->$to_mode_str"; } elsif ($to_mode_str) { $mode_chnge .= " mode: $to_mode_str"; } } $mode_chnge .= "]\n"; } print "\n"; print "\n"; print "\n"; } elsif ($diff->{'status'} eq "R" || $diff->{'status'} eq "C") { # renamed or copied my %status_name = ('R' => 'moved', 'C' => 'copied'); my $nstatus = $status_name{$diff->{'status'}}; my $mode_chng = ""; if ($diff->{'from_mode'} != $diff->{'to_mode'}) { # mode also for directories, so we cannot use $to_mode_str $mode_chng = sprintf(", mode: %04o", $to_mode_oct & 0777); } print "\n" . "\n" . "\n"; } # we should not encounter Unmerged (U) or Unknown (X) status print "\n"; } print "" if $has_header; print "
" . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par), -title => 'commitdiff to parent number ' . ($i+1) . ': ' . substr($par,0,7)}, $i+1) . " 
" . $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}, hash_base=>$hash), -class => "list"}, esc_path($diff->{'to_file'})) . "" . esc_path($diff->{'to_file'}) . "" . $cgi->a({-href => "#patch$patchno"}, "patch") . " | " . " | " . $cgi->a({-href => href(action=>"blob", hash_base=>$hash, hash=>$from_hash, file_name=>$from_path)}, "blob" . ($i+1)) . " | "; } print $cgi->a({-href => href(action=>"blobdiff", hash=>$diff->{'to_id'}, hash_parent=>$from_hash, hash_base=>$hash, hash_parent_base=>$hash_parent, file_name=>$diff->{'to_file'}, file_parent=>$from_path)}, "diff" . ($i+1)) . " | "; if ($not_deleted) { print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}, hash_base=>$hash)}, "blob"); print " | " if ($has_history); } if ($has_history) { print $cgi->a({-href => href(action=>"history", file_name=>$diff->{'to_file'}, hash_base=>$hash)}, "history"); } print "
"; print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$hash, file_name=>$diff->{'file'}), -class => "list"}, esc_path($diff->{'file'})); print "$mode_chng"; if ($action eq 'commitdiff') { # link to patch $patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch"); print " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$hash, file_name=>$diff->{'file'})}, "blob"); print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'}, hash_base=>$parent, file_name=>$diff->{'file'}), -class => "list"}, esc_path($diff->{'file'})); print "$mode_chng"; if ($action eq 'commitdiff') { # link to patch $patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch"); print " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'}, hash_base=>$parent, file_name=>$diff->{'file'})}, "blob") . " | "; if ($have_blame) { print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, file_name=>$diff->{'file'})}, "blame") . " | "; } print $cgi->a({-href => href(action=>"history", hash_base=>$parent, file_name=>$diff->{'file'})}, "history"); print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$hash, file_name=>$diff->{'file'}), -class => "list"}, esc_path($diff->{'file'})); print "$mode_chnge"; if ($action eq 'commitdiff') { # link to patch $patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch") . " | "; } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) { # "commit" view and modified file (not onlu mode changed) print $cgi->a({-href => href(action=>"blobdiff", hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'}, hash_base=>$hash, hash_parent_base=>$parent, file_name=>$diff->{'file'})}, "diff") . " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$hash, file_name=>$diff->{'file'})}, "blob") . " | "; if ($have_blame) { print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, file_name=>$diff->{'file'})}, "blame") . " | "; } print $cgi->a({-href => href(action=>"history", hash_base=>$hash, file_name=>$diff->{'file'})}, "history"); print "" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash, hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}), -class => "list"}, esc_path($diff->{'to_file'})) . "[$nstatus from " . $cgi->a({-href => href(action=>"blob", hash_base=>$parent, hash=>$diff->{'from_id'}, file_name=>$diff->{'from_file'}), -class => "list"}, esc_path($diff->{'from_file'})) . " with " . (int $diff->{'similarity'}) . "% similarity$mode_chng]"; if ($action eq 'commitdiff') { # link to patch $patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch") . " | "; } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) { # "commit" view and modified file (not only pure rename or copy) print $cgi->a({-href => href(action=>"blobdiff", hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'}, hash_base=>$hash, hash_parent_base=>$parent, file_name=>$diff->{'to_file'}, file_parent=>$diff->{'from_file'})}, "diff") . " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$parent, file_name=>$diff->{'to_file'})}, "blob") . " | "; if ($have_blame) { print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, file_name=>$diff->{'to_file'})}, "blame") . " | "; } print $cgi->a({-href => href(action=>"history", hash_base=>$hash, file_name=>$diff->{'to_file'})}, "history"); print "
\n"; } sub git_patchset_body { my ($fd, $difftree, $hash, @hash_parents) = @_; my ($hash_parent) = $hash_parents[0]; my $is_combined = (@hash_parents > 1); my $patch_idx = 0; my $patch_number = 0; my $patch_line; my $diffinfo; my $to_name; my (%from, %to); print "
\n"; # skip to first patch while ($patch_line = <$fd>) { chomp $patch_line; last if ($patch_line =~ m/^diff /); } PATCH: while ($patch_line) { # parse "git diff" header line if ($patch_line =~ m/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/) { # $1 is from_name, which we do not use $to_name = unquote($2); $to_name =~ s!^b/!!; } elsif ($patch_line =~ m/^diff --(cc|combined) ("?.*"?)$/) { # $1 is 'cc' or 'combined', which we do not use $to_name = unquote($2); } else { $to_name = undef; } # check if current patch belong to current raw line # and parse raw git-diff line if needed if (is_patch_split($diffinfo, { 'to_file' => $to_name })) { # this is continuation of a split patch print "
\n"; } else { # advance raw git-diff output if needed $patch_idx++ if defined $diffinfo; # read and prepare patch information $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); # compact combined diff output can have some patches skipped # find which patch (using pathname of result) we are at now; if ($is_combined) { while ($to_name ne $diffinfo->{'to_file'}) { print "
\n" . format_diff_cc_simplified($diffinfo, @hash_parents) . "
\n"; # class="patch" $patch_idx++; $patch_number++; last if $patch_idx > $#$difftree; $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); } } # modifies %from, %to hashes parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents); # this is first patch for raw difftree line with $patch_idx index # we index @$difftree array from 0, but number patches from 1 print "
\n"; } # git diff header #assert($patch_line =~ m/^diff /) if DEBUG; #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed $patch_number++; # print "git diff" header print format_git_diff_header_line($patch_line, $diffinfo, \%from, \%to); # print extended diff header print "
\n"; EXTENDED_HEADER: while ($patch_line = <$fd>) { chomp $patch_line; last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /); print format_extended_diff_header_line($patch_line, $diffinfo, \%from, \%to); } print "
\n"; # class="diff extended_header" # from-file/to-file diff header if (! $patch_line) { print "
\n"; # class="patch" last PATCH; } next PATCH if ($patch_line =~ m/^diff /); #assert($patch_line =~ m/^---/) if DEBUG; my $last_patch_line = $patch_line; $patch_line = <$fd>; chomp $patch_line; #assert($patch_line =~ m/^\+\+\+/) if DEBUG; print format_diff_from_to_header($last_patch_line, $patch_line, $diffinfo, \%from, \%to, @hash_parents); # the patch itself LINE: while ($patch_line = <$fd>) { chomp $patch_line; next PATCH if ($patch_line =~ m/^diff /); print format_diff_line($patch_line, \%from, \%to); } } continue { print "
\n"; # class="patch" } # for compact combined (--cc) format, with chunk and patch simpliciaction # patchset might be empty, but there might be unprocessed raw lines for (++$patch_idx if $patch_number > 0; $patch_idx < @$difftree; ++$patch_idx) { # read and prepare patch information $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); # generate anchor for "patch" links in difftree / whatchanged part print "
\n" . format_diff_cc_simplified($diffinfo, @hash_parents) . "
\n"; # class="patch" $patch_number++; } if ($patch_number == 0) { if (@hash_parents > 1) { print "
Trivial merge
\n"; } else { print "
No differences found
\n"; } } print "
\n"; # class="patchset" } # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # fills project list info (age, description, owner, forks) for each # project in the list, removing invalid projects from returned list # NOTE: modifies $projlist, but does not remove entries from it sub fill_project_list_info { my ($projlist, $check_forks) = @_; my @projects; my $show_ctags = gitweb_check_feature('ctags'); PROJECT: foreach my $pr (@$projlist) { my (@activity) = git_get_last_activity($pr->{'path'}); unless (@activity) { next PROJECT; } ($pr->{'age'}, $pr->{'age_string'}) = @activity; if (!defined $pr->{'descr'}) { my $descr = git_get_project_description($pr->{'path'}) || ""; $descr = to_utf8($descr); $pr->{'descr_long'} = $descr; $pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5); } if (!defined $pr->{'owner'}) { $pr->{'owner'} = git_get_project_owner("$pr->{'path'}") || ""; } if ($check_forks) { my $pname = $pr->{'path'}; if (($pname =~ s/\.git$//) && ($pname !~ /\/$/) && (-d "$projectroot/$pname")) { $pr->{'forks'} = "-d $projectroot/$pname"; } else { $pr->{'forks'} = 0; } } $show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'}); push @projects, $pr; } return @projects; } # print 'sort by' element, generating 'sort by $name' replay link # if that order is not selected sub print_sort_th { print format_sort_th(@_); } sub format_sort_th { my ($name, $order, $header) = @_; my $sort_th = ""; $header ||= ucfirst($name); if ($order eq $name) { $sort_th .= "$header\n"; } else { $sort_th .= "" . $cgi->a({-href => href(-replay=>1, order=>$name), -class => "header"}, $header) . "\n"; } return $sort_th; } sub git_project_list_body { # actually uses global variable $project my ($projlist, $order, $from, $to, $extra, $no_header) = @_; my $check_forks = gitweb_check_feature('forks'); my @projects = fill_project_list_info($projlist, $check_forks); $order ||= $default_projects_order; $from = 0 unless defined $from; $to = $#projects if (!defined $to || $#projects < $to); my %order_info = ( project => { key => 'path', type => 'str' }, descr => { key => 'descr_long', type => 'str' }, owner => { key => 'owner', type => 'str' }, age => { key => 'age', type => 'num' } ); my $oi = $order_info{$order}; if ($oi->{'type'} eq 'str') { @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects; } else { @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects; } my $show_ctags = gitweb_check_feature('ctags'); if ($show_ctags) { my %ctags; foreach my $p (@projects) { foreach my $ct (keys %{$p->{'ctags'}}) { $ctags{$ct} += $p->{'ctags'}->{$ct}; } } my $cloud = git_populate_project_tagcloud(\%ctags); print git_show_project_tagcloud($cloud, 64); } print "\n"; unless ($no_header) { print "\n"; if ($check_forks) { print "\n"; } print_sort_th('project', $order, 'Project'); print_sort_th('descr', $order, 'Description'); print_sort_th('owner', $order, 'Owner'); print_sort_th('age', $order, 'Last Change'); print "\n" . # for links "\n"; } my $alternate = 1; my $tagfilter = $cgi->param('by_tag'); for (my $i = $from; $i <= $to; $i++) { my $pr = $projects[$i]; next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}}; next if $searchtext and not $pr->{'path'} =~ /$searchtext/ and not $pr->{'descr_long'} =~ /$searchtext/; # Weed out forks or non-matching entries of search if ($check_forks) { my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#; $forkbase="^$forkbase" if $forkbase; next if not $searchtext and not $tagfilter and $show_ctags and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe } if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; if ($check_forks) { print "\n"; } print "\n" . "\n" . "\n"; print "\n" . "\n" . "\n"; } if (defined $extra) { print "\n"; if ($check_forks) { print "\n"; } print "\n" . "\n"; } print "
"; if ($pr->{'forks'}) { print "\n"; print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+"); } print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), -class => "list"}, esc_html($pr->{'path'})) . "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), -class => "list", -title => $pr->{'descr_long'}}, esc_html($pr->{'descr'})) . "" . chop_and_escape_str($pr->{'owner'}, 15) . "{'age'}) . "\">" . (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") . ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') . "
$extra
\n"; } sub git_log_body { # uses global variable $project my ($commitlist, $from, $to, $refs, $extra) = @_; $from = 0 unless defined $from; $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); for (my $i = 0; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; next if !%co; my $commit = $co{'id'}; my $ref = format_ref_marker($refs, $commit); my %ad = parse_date($co{'author_epoch'}); git_print_header_div('commit', "$co{'age_string'}" . esc_html($co{'title'}) . $ref, $commit); print "
\n" . "
\n" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . "
\n" . "
\n"; git_print_authorship(\%co, -tag => 'span'); print "
\n
\n"; print "
\n"; git_print_log($co{'comment'}, -final_empty_line=> 1); print "
\n"; } if ($extra) { print "
\n"; print "$extra\n"; print "
\n"; } } sub git_shortlog_body { # uses global variable $project my ($commitlist, $from, $to, $refs, $extra) = @_; $from = 0 unless defined $from; $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); print "\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; my $commit = $co{'id'}; my $ref = format_ref_marker($refs, $commit); if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; # git_summary() used print "\n" . print "\n" . format_author_html('td', \%co, 10) . "\n" . "\n" . "\n"; } if (defined $extra) { print "\n" . "\n" . "\n"; } print "
$co{'age_string'}$co{'age_string_date'}"; print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref); print "" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree"); my $snapshot_links = format_snapshot_links($commit); if (defined $snapshot_links) { print " | " . $snapshot_links; } print "
$extra
\n"; } sub git_history_body { # Warning: assumes constant type (blob or tree) during history my ($commitlist, $from, $to, $refs, $extra, $file_name, $file_hash, $ftype) = @_; $from = 0 unless defined $from; $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist}); print "\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; if (!%co) { next; } my $commit = $co{'id'}; my $ref = format_ref_marker($refs, $commit); if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; print "\n" . # shortlog: format_author_html('td', \%co, 10) format_author_html('td', \%co, 15, 3) . "\n" . "\n" . "\n"; } if (defined $extra) { print "\n" . "\n" . "\n"; } print "
$co{'age_string_date'}"; # originally git_history used chop_str($co{'title'}, 50) print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref); print "" . $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff"); if ($ftype eq 'blob') { my $blob_current = $file_hash; my $blob_parent = git_get_hash_by_path($commit, $file_name); if (defined $blob_current && defined $blob_parent && $blob_current ne $blob_parent) { print " | " . $cgi->a({-href => href(action=>"blobdiff", hash=>$blob_current, hash_parent=>$blob_parent, hash_base=>$hash_base, hash_parent_base=>$commit, file_name=>$file_name)}, "diff to current"); } } print "
$extra
\n"; } sub git_tags_body { # uses global variable $project my ($taglist, $from, $to, $extra) = @_; $from = 0 unless defined $from; $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); print "\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my $entry = $taglist->[$i]; my %tag = %$entry; my $comment = $tag{'subject'}; my $comment_short; if (defined $comment) { $comment_short = chop_str($comment, 30, 5); } if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; if (defined $tag{'age'}) { print "\n"; } else { print "\n"; } print "\n" . "\n" . "\n" . "\n" . ""; } if (defined $extra) { print "\n" . "\n" . "\n"; } print "
$tag{'age'}" . $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}), -class => "list name"}, esc_html($tag{'name'})) . ""; if (defined $comment) { print format_subject_html($comment, $comment_short, href(action=>"tag", hash=>$tag{'id'})); } print ""; if ($tag{'type'} eq "tag") { print $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag"); } else { print " "; } print "" . " | " . $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") . " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log"); } elsif ($tag{'reftype'} eq "blob") { print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw"); } print "
$extra
\n"; } sub git_heads_body { # uses global variable $project my ($headlist, $head, $from, $to, $extra) = @_; $from = 0 unless defined $from; $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to); print "\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my $entry = $headlist->[$i]; my %ref = %$entry; my $curr = $ref{'id'} eq $head; if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; print "\n" . ($curr ? "\n" . "\n" . ""; } if (defined $extra) { print "\n" . "\n" . "\n"; } print "
$ref{'age'}" : "") . $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}), -class => "list name"},esc_html($ref{'name'})) . "" . $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " . $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") . "
$extra
\n"; } sub git_search_grep_body { my ($commitlist, $from, $to, $extra) = @_; $from = 0 unless defined $from; $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); print "\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; if (!%co) { next; } my $commit = $co{'id'}; if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; print "\n" . format_author_html('td', \%co, 15, 5) . "\n" . "\n" . "\n"; } if (defined $extra) { print "\n" . "\n" . "\n"; } print "
$co{'age_string_date'}" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, chop_and_escape_str($co{'title'}, 50) . "
"); my $comment = $co{'comment'}; foreach my $line (@$comment) { if ($line =~ m/^(.*?)($search_regexp)(.*)$/i) { my ($lead, $match, $trail) = ($1, $2, $3); $match = chop_str($match, 70, 5, 'center'); my $contextlen = int((80 - length($match))/2); $contextlen = 30 if ($contextlen > 30); $lead = chop_str($lead, $contextlen, 10, 'left'); $trail = chop_str($trail, $contextlen, 10, 'right'); $lead = esc_html($lead); $match = esc_html($match); $trail = esc_html($trail); print "$lead$match$trail
"; } } print "
" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "
$extra
\n"; } ## ====================================================================== ## ====================================================================== ## actions sub git_project_list { my $order = $input_params{'order'}; if (defined $order && $order !~ m/none|project|descr|owner|age/) { die_error(400, "Unknown order parameter"); } my @list = git_get_projects_list(); if (!@list) { die_error(404, "No projects found"); } git_header_html(); if (defined $home_text && -f $home_text) { print "
\n"; insert_file($home_text); print "
\n"; } print $cgi->startform(-method => "get") . "

Search:\n" . $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . "

" . $cgi->end_form() . "\n"; git_project_list_body(\@list, $order); git_footer_html(); } sub git_forks { my $order = $input_params{'order'}; if (defined $order && $order !~ m/none|project|descr|owner|age/) { die_error(400, "Unknown order parameter"); } my @list = git_get_projects_list($project); if (!@list) { die_error(404, "No forks found"); } git_header_html(); git_print_page_nav('',''); git_print_header_div('summary', "$project forks"); git_project_list_body(\@list, $order); git_footer_html(); } sub git_project_index { my @projects = git_get_projects_list($project); print $cgi->header( -type => 'text/plain', -charset => 'utf-8', -content_disposition => 'inline; filename="index.aux"'); foreach my $pr (@projects) { if (!exists $pr->{'owner'}) { $pr->{'owner'} = git_get_project_owner("$pr->{'path'}"); } my ($path, $owner) = ($pr->{'path'}, $pr->{'owner'}); # quote as in CGI::Util::encode, but keep the slash, and use '+' for ' ' $path =~ s/([^a-zA-Z0-9_.\-\/ ])/sprintf("%%%02X", ord($1))/eg; $owner =~ s/([^a-zA-Z0-9_.\-\/ ])/sprintf("%%%02X", ord($1))/eg; $path =~ s/ /\+/g; $owner =~ s/ /\+/g; print "$path $owner\n"; } } sub git_summary { my $descr = git_get_project_description($project) || "none"; my %co = parse_commit("HEAD"); my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : (); my $head = $co{'id'}; my $owner = git_get_project_owner($project); my $refs = git_get_references(); # These get_*_list functions return one more to allow us to see if # there are more ... my @taglist = git_get_tags_list(16); my @headlist = git_get_heads_list(16); my @forklist; my $check_forks = gitweb_check_feature('forks'); if ($check_forks) { @forklist = git_get_projects_list($project); } git_header_html(); git_print_page_nav('summary','', $head); print "
 
\n"; print "\n" . "\n" . "\n"; if (defined $cd{'rfc2822'}) { print "\n"; } # use per project git URL list in $projectroot/$project/cloneurl # or make project git URL from git base URL and project name my $url_tag = "URL"; my @url_list = git_get_project_url_list($project); @url_list = map { "$_/$project" } @git_base_url_list unless @url_list; foreach my $git_url (@url_list) { next unless $git_url; print "\n"; $url_tag = ""; } # Tag cloud my $show_ctags = gitweb_check_feature('ctags'); if ($show_ctags) { my $ctags = git_get_project_ctags($project); my $cloud = git_populate_project_tagcloud($ctags); print "\n\n"; } print "
description" . esc_html($descr) . "
owner" . esc_html($owner) . "
last change$cd{'rfc2822'}
Content tags:
"; print "
" unless %$ctags; print "
Add:
"; print "
" if %$ctags; print git_show_project_tagcloud($cloud, 48); print "
\n"; # If XSS prevention is on, we don't include README.html. # TODO: Allow a readme in some safe format. if (!$prevent_xss && -s "$projectroot/$project/README.html") { print "
readme
\n" . "
\n"; insert_file("$projectroot/$project/README.html"); print "\n
\n"; # class="readme" } # we need to request one more than 16 (0..15) to check if # those 16 are all my @commitlist = $head ? parse_commits($head, 17) : (); if (@commitlist) { git_print_header_div('shortlog'); git_shortlog_body(\@commitlist, 0, 15, $refs, $#commitlist <= 15 ? undef : $cgi->a({-href => href(action=>"shortlog")}, "...")); } if (@taglist) { git_print_header_div('tags'); git_tags_body(\@taglist, 0, 15, $#taglist <= 15 ? undef : $cgi->a({-href => href(action=>"tags")}, "...")); } if (@headlist) { git_print_header_div('heads'); git_heads_body(\@headlist, $head, 0, 15, $#headlist <= 15 ? undef : $cgi->a({-href => href(action=>"heads")}, "...")); } if (@forklist) { git_print_header_div('forks'); git_project_list_body(\@forklist, 'age', 0, 15, $#forklist <= 15 ? undef : $cgi->a({-href => href(action=>"forks")}, "..."), 'no_header'); } git_footer_html(); } sub git_tag { my $head = git_get_head_hash($project); git_header_html(); git_print_page_nav('','', $head,undef,$head); my %tag = parse_tag($hash); if (! %tag) { die_error(404, "Unknown tag object"); } git_print_header_div('commit', esc_html($tag{'name'}), $hash); print "
\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n"; if (defined($tag{'author'})) { git_print_authorship_rows(\%tag, 'author'); } print "
object" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'object'}) . "" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'type'}) . "
\n\n" . "
\n"; print "
"; my $comment = $tag{'comment'}; foreach my $line (@$comment) { chomp $line; print esc_html($line, -nbsp=>1) . "
\n"; } print "
\n"; git_footer_html(); } sub git_blame_common { my $format = shift || 'porcelain'; if ($format eq 'porcelain' && $cgi->param('js')) { $format = 'incremental'; $action = 'blame_incremental'; # for page title etc } # permissions gitweb_check_feature('blame') or die_error(403, "Blame view not allowed"); # error checking die_error(400, "No file name given") unless $file_name; $hash_base ||= git_get_head_hash($project); die_error(404, "Couldn't find base commit") unless $hash_base; my %co = parse_commit($hash_base) or die_error(404, "Commit not found"); my $ftype = "blob"; if (!defined $hash) { $hash = git_get_hash_by_path($hash_base, $file_name, "blob") or die_error(404, "Error looking up file"); } else { $ftype = git_get_type($hash); if ($ftype !~ "blob") { die_error(400, "Object is not a blob"); } } my $fd; if ($format eq 'incremental') { # get file contents (as base) open $fd, "-|", git_cmd(), 'cat-file', 'blob', $hash or die_error(500, "Open git-cat-file failed"); } elsif ($format eq 'data') { # run git-blame --incremental open $fd, "-|", git_cmd(), "blame", "--incremental", $hash_base, "--", $file_name or die_error(500, "Open git-blame --incremental failed"); } else { # run git-blame --porcelain open $fd, "-|", git_cmd(), "blame", '-p', $hash_base, '--', $file_name or die_error(500, "Open git-blame --porcelain failed"); } # incremental blame data returns early if ($format eq 'data') { print $cgi->header( -type=>"text/plain", -charset => "utf-8", -status=> "200 OK"); local $| = 1; # output autoflush print while <$fd>; close $fd or print "ERROR $!\n"; print 'END'; if (defined $t0 && gitweb_check_feature('timed')) { print ' '. Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]). ' '.$number_of_git_cmds; } print "\n"; return; } # page header git_header_html(); my $formats_nav = $cgi->a({-href => href(action=>"blob", -replay=>1)}, "blob") . " | "; if ($format eq 'incremental') { $formats_nav .= $cgi->a({-href => href(action=>"blame", javascript=>0, -replay=>1)}, "blame") . " (non-incremental)"; } else { $formats_nav .= $cgi->a({-href => href(action=>"blame_incremental", -replay=>1)}, "blame") . " (incremental)"; } $formats_nav .= " | " . $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . $cgi->a({-href => href(action=>$action, file_name=>$file_name)}, "HEAD"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, $ftype, $hash_base); # page body if ($format eq 'incremental') { print "\n"; print qq!
\n!; } print qq!
\n!; print qq!
... / ...
\n! if ($format eq 'incremental'); print qq!\n!. #qq!\n!. qq!\n!. qq!\n!. qq!\n!. qq!\n!; my @rev_color = qw(light dark); my $num_colors = scalar(@rev_color); my $current_color = 0; if ($format eq 'incremental') { my $color_class = $rev_color[$current_color]; #contents of a file my $linenr = 0; LINE: while (my $line = <$fd>) { chomp $line; $linenr++; print qq!!. qq!!. qq!!; print qq!\n"; print qq!\n!; } } else { # porcelain, i.e. ordinary blame my %metainfo = (); # saves information about commits # blame data LINE: while (my $line = <$fd>) { chomp $line; # the header: [] # no for subsequent lines in group of lines my ($full_rev, $orig_lineno, $lineno, $group_size) = ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/); if (!exists $metainfo{$full_rev}) { $metainfo{$full_rev} = { 'nprevious' => 0 }; } my $meta = $metainfo{$full_rev}; my $data; while ($data = <$fd>) { chomp $data; last if ($data =~ s/^\t//); # contents of line if ($data =~ /^(\S+)(?: (.*))?$/) { $meta->{$1} = $2 unless exists $meta->{$1}; } if ($data =~ /^previous /) { $meta->{'nprevious'}++; } } my $short_rev = substr($full_rev, 0, 8); my $author = $meta->{'author'}; my %date = parse_date($meta->{'author-time'}, $meta->{'author-tz'}); my $date = $date{'iso-tz'}; if ($group_size) { $current_color = ($current_color + 1) % $num_colors; } my $tr_class = $rev_color[$current_color]; $tr_class .= ' boundary' if (exists $meta->{'boundary'}); $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0); $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1); print "\n"; if ($group_size) { print "\n"; } # 'previous' if (exists $meta->{'previous'} && $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) { $meta->{'parent'} = $1; $meta->{'file_parent'} = unquote($2); } my $linenr_commit = exists($meta->{'parent'}) ? $meta->{'parent'} : $full_rev; my $linenr_filename = exists($meta->{'file_parent'}) ? $meta->{'file_parent'} : unquote($meta->{'filename'}); my $blamed = href(action => 'blame', file_name => $linenr_filename, hash_base => $linenr_commit); print ""; print "\n"; print "\n"; } # end while } # footer print "\n". "
CommitLineData
!. qq!$linenr! . esc_html($line) . "
1); print ">"; print $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, esc_html($short_rev)); if ($group_size >= 2) { my @author_initials = ($author =~ /\b([[:upper:]])\B/g); if (@author_initials) { print "
" . esc_html(join('', @author_initials)); # or join('.', ...) } } print "
"; print $cgi->a({ -href => "$blamed#l$orig_lineno", -class => "linenr" }, esc_html($lineno)); print "" . esc_html($data) . "
\n"; # class="blame" print "
\n"; # class="blame_body" close $fd or print "Reading blob failed\n"; git_footer_html(); } sub git_blame { git_blame_common(); } sub git_blame_incremental { git_blame_common('incremental'); } sub git_blame_data { git_blame_common('data'); } sub git_tags { my $head = git_get_head_hash($project); git_header_html(); git_print_page_nav('','', $head,undef,$head); git_print_header_div('summary', $project); my @tagslist = git_get_tags_list(); if (@tagslist) { git_tags_body(\@tagslist); } git_footer_html(); } sub git_heads { my $head = git_get_head_hash($project); git_header_html(); git_print_page_nav('','', $head,undef,$head); git_print_header_div('summary', $project); my @headslist = git_get_heads_list(); if (@headslist) { git_heads_body(\@headslist, $head); } git_footer_html(); } sub git_blob_plain { my $type = shift; my $expires; if (!defined $hash) { if (defined $file_name) { my $base = $hash_base || git_get_head_hash($project); $hash = git_get_hash_by_path($base, $file_name, "blob") or die_error(404, "Cannot find file"); } else { die_error(400, "No file name defined"); } } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) { # blobs defined by non-textual hash id's can be cached $expires = "+1d"; } open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(500, "Open git-cat-file blob '$hash' failed"); # content-type (can include charset) $type = blob_contenttype($fd, $file_name, $type); # "save as" filename, even when no $file_name is given my $save_as = "$hash"; if (defined $file_name) { $save_as = $file_name; } elsif ($type =~ m/^text\//) { $save_as .= '.txt'; } # With XSS prevention on, blobs of all types except a few known safe # ones are served with "Content-Disposition: attachment" to make sure # they don't run in our security domain. For certain image types, # blob view writes an tag referring to blob_plain view, and we # want to be sure not to break that by serving the image as an # attachment (though Firefox 3 doesn't seem to care). my $sandbox = $prevent_xss && $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!; print $cgi->header( -type => $type, -expires => $expires, -content_disposition => ($sandbox ? 'attachment' : 'inline') . '; filename="' . $save_as . '"'); local $/ = undef; binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi close $fd; } sub git_blob { my $expires; if (!defined $hash) { if (defined $file_name) { my $base = $hash_base || git_get_head_hash($project); $hash = git_get_hash_by_path($base, $file_name, "blob") or die_error(404, "Cannot find file"); } else { die_error(400, "No file name defined"); } } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) { # blobs defined by non-textual hash id's can be cached $expires = "+1d"; } my $have_blame = gitweb_check_feature('blame'); open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(500, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); # use 'blob_plain' (aka 'raw') view for files that cannot be displayed if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) { close $fd; return git_blob_plain($mimetype); } # we can have blame only for text/* mimetype $have_blame &&= ($mimetype =~ m!^text/!); my $highlight = gitweb_check_feature('highlight'); my $syntax = guess_file_syntax($highlight, $mimetype, $file_name); $fd = run_highlighter($fd, $highlight, $syntax) if $syntax; git_header_html(undef, $expires); my $formats_nav = ''; if (defined $hash_base && (my %co = parse_commit($hash_base))) { if (defined $file_name) { if ($have_blame) { $formats_nav .= $cgi->a({-href => href(action=>"blame", -replay=>1)}, "blame") . " | "; } $formats_nav .= $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw") . " | " . $cgi->a({-href => href(action=>"blob", hash_base=>"HEAD", file_name=>$file_name)}, "HEAD"); } else { $formats_nav .= $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); } else { print "
\n" . "

\n" . "
$hash
\n"; } git_print_page_path($file_name, "blob", $hash_base); print "
\n"; if ($mimetype =~ m!^image/!) { print qq!$file_name$hash, hash_base=>$hash_base, file_name=>$file_name) . qq!" />\n!; } else { my $nr; while (my $line = <$fd>) { chomp $line; $nr++; $line = untabify($line); printf qq!
%4i %s
\n!, $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1); } } close $fd or print "Reading blob failed.\n"; print "
"; git_footer_html(); } sub git_tree { if (!defined $hash_base) { $hash_base = "HEAD"; } if (!defined $hash) { if (defined $file_name) { $hash = git_get_hash_by_path($hash_base, $file_name, "tree"); } else { $hash = $hash_base; } } die_error(404, "No such tree") unless defined($hash); my $show_sizes = gitweb_check_feature('show-sizes'); my $have_blame = gitweb_check_feature('blame'); my @entries = (); { local $/ = "\0"; open my $fd, "-|", git_cmd(), "ls-tree", '-z', ($show_sizes ? '-l' : ()), @extra_options, $hash or die_error(500, "Open git-ls-tree failed"); @entries = map { chomp; $_ } <$fd>; close $fd or die_error(404, "Reading tree failed"); } my $refs = git_get_references(); my $ref = format_ref_marker($refs, $hash_base); git_header_html(); my $basedir = ''; if (defined $hash_base && (my %co = parse_commit($hash_base))) { my @views_nav = (); if (defined $file_name) { push @views_nav, $cgi->a({-href => href(action=>"history", -replay=>1)}, "history"), $cgi->a({-href => href(action=>"tree", hash_base=>"HEAD", file_name=>$file_name)}, "HEAD"), } my $snapshot_links = format_snapshot_links($hash); if (defined $snapshot_links) { # FIXME: Should be available when we have no hash base as well. push @views_nav, $snapshot_links; } git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav)); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); } else { undef $hash_base; print "
\n"; print "

\n"; print "
$hash
\n"; } if (defined $file_name) { $basedir = $file_name; if ($basedir ne '' && substr($basedir, -1) ne '/') { $basedir .= '/'; } git_print_page_path($file_name, 'tree', $hash_base); } print "
\n"; print "\n"; my $alternate = 1; # '..' (top directory) link if possible if (defined $hash_base && defined $file_name && $file_name =~ m![^/]+$!) { if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; my $up = $file_name; $up =~ s!/?[^/]+$!!; undef $up unless $up; # based on git_print_tree_entry print '\n"; print ''."\n" if $show_sizes; print '\n"; print "\n"; print "\n"; } foreach my $line (@entries) { my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes); if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame); print "\n"; } print "
' . mode_str('040000') . " '; print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, file_name=>$up)}, ".."); print "
\n" . "
"; git_footer_html(); } sub snapshot_name { my ($project, $hash) = @_; # path/to/project.git -> project # path/to/project/.git -> project my $name = to_utf8($project); $name =~ s,([^/])/*\.git$,$1,; $name = basename($name); # sanitize name $name =~ s/[[:cntrl:]]/?/g; my $ver = $hash; if ($hash =~ /^[0-9a-fA-F]+$/) { # shorten SHA-1 hash my $full_hash = git_get_full_hash($project, $hash); if ($full_hash =~ /^$hash/ && length($hash) > 7) { $ver = git_get_short_hash($project, $hash); } } elsif ($hash =~ m!^refs/tags/(.*)$!) { # tags don't need shortened SHA-1 hash $ver = $1; } else { # branches and other need shortened SHA-1 hash if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) { $ver = $1; } $ver .= '-' . git_get_short_hash($project, $hash); } # in case of hierarchical branch names $ver =~ s!/!.!g; # name = project-version_string $name = "$name-$ver"; return wantarray ? ($name, $name) : $name; } sub git_snapshot { my $format = $input_params{'snapshot_format'}; if (!@snapshot_fmts) { die_error(403, "Snapshots not allowed"); } # default to first supported snapshot format $format ||= $snapshot_fmts[0]; if ($format !~ m/^[a-z0-9]+$/) { die_error(400, "Invalid snapshot format parameter"); } elsif (!exists($known_snapshot_formats{$format})) { die_error(400, "Unknown snapshot format"); } elsif ($known_snapshot_formats{$format}{'disabled'}) { die_error(403, "Snapshot format not allowed"); } elsif (!grep($_ eq $format, @snapshot_fmts)) { die_error(403, "Unsupported snapshot format"); } my $type = git_get_type("$hash^{}"); if (!$type) { die_error(404, 'Object does not exist'); } elsif ($type eq 'blob') { die_error(400, 'Object is not a tree-ish'); } my ($name, $prefix) = snapshot_name($project, $hash); my $filename = "$name$known_snapshot_formats{$format}{'suffix'}"; my $cmd = quote_command( git_cmd(), 'archive', "--format=$known_snapshot_formats{$format}{'format'}", "--prefix=$prefix/", $hash); if (exists $known_snapshot_formats{$format}{'compressor'}) { $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}}); } $filename =~ s/(["\\])/\\$1/g; print $cgi->header( -type => $known_snapshot_formats{$format}{'type'}, -content_disposition => 'inline; filename="' . $filename . '"', -status => '200 OK'); open my $fd, "-|", $cmd or die_error(500, "Execute git-archive failed"); binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi close $fd; } sub git_log_generic { my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_; my $head = git_get_head_hash($project); if (!defined $base) { $base = $head; } if (!defined $page) { $page = 0; } my $refs = git_get_references(); my $commit_hash = $base; if (defined $parent) { $commit_hash = "$parent..$base"; } my @commitlist = parse_commits($commit_hash, 101, (100 * $page), defined $file_name ? ($file_name, "--full-history") : ()); my $ftype; if (!defined $file_hash && defined $file_name) { # some commits could have deleted file in question, # and not have it in tree, but one of them has to have it for (my $i = 0; $i < @commitlist; $i++) { $file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name); last if defined $file_hash; } } if (defined $file_hash) { $ftype = git_get_type($file_hash); } if (defined $file_name && !defined $ftype) { die_error(500, "Unknown type of object"); } my %co; if (defined $file_name) { %co = parse_commit($base) or die_error(404, "Unknown commit object"); } my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100); my $next_link = ''; if ($#commitlist >= 100) { $next_link = $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } my $patch_max = gitweb_get_feature('patches'); if ($patch_max && !defined $file_name) { if ($patch_max < 0 || @commitlist <= $patch_max) { $paging_nav .= " ⋅ " . $cgi->a({-href => href(action=>"patches", -replay=>1)}, "patches"); } } git_header_html(); git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav); if (defined $file_name) { git_print_header_div('commit', esc_html($co{'title'}), $base); } else { git_print_header_div('summary', $project) } git_print_page_path($file_name, $ftype, $hash_base) if (defined $file_name); $body_subr->(\@commitlist, 0, 99, $refs, $next_link, $file_name, $file_hash, $ftype); git_footer_html(); } sub git_log { git_log_generic('log', \&git_log_body, $hash, $hash_parent); } sub git_commit { $hash ||= $hash_base || "HEAD"; my %co = parse_commit($hash) or die_error(404, "Unknown commit object"); my $parent = $co{'parent'}; my $parents = $co{'parents'}; # listref # we need to prepare $formats_nav before any parameter munging my $formats_nav; if (!defined $parent) { # --root commitdiff $formats_nav .= '(initial)'; } elsif (@$parents == 1) { # single parent commit $formats_nav .= '(parent: ' . $cgi->a({-href => href(action=>"commit", hash=>$parent)}, esc_html(substr($parent, 0, 7))) . ')'; } else { # merge commit $formats_nav .= '(merge: ' . join(' ', map { $cgi->a({-href => href(action=>"commit", hash=>$_)}, esc_html(substr($_, 0, 7))); } @$parents ) . ')'; } if (gitweb_check_feature('patches') && @$parents <= 1) { $formats_nav .= " | " . $cgi->a({-href => href(action=>"patch", -replay=>1)}, "patch"); } if (!defined $parent) { $parent = "--root"; } my @difftree; open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id", @diff_opts, (@$parents <= 1 ? $parent : '-c'), $hash, "--" or die_error(500, "Open git-diff-tree failed"); @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(404, "Reading git-diff-tree failed"); # non-textual hash id's can be cached my $expires; if ($hash =~ m/^[0-9a-fA-F]{40}$/) { $expires = "+1d"; } my $refs = git_get_references(); my $ref = format_ref_marker($refs, $co{'id'}); git_header_html(undef, $expires); git_print_page_nav('commit', '', $hash, $co{'tree'}, $hash, $formats_nav); if (defined $co{'parent'}) { git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); } else { git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash); } print "
\n" . "\n"; git_print_authorship_rows(\%co); print "\n"; print "" . "" . "" . "" . "\n"; foreach my $par (@$parents) { print "" . "" . "" . "" . "\n"; } print "
commit$co{'id'}
tree" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), class => "list"}, $co{'tree'}) . "" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, "tree"); my $snapshot_links = format_snapshot_links($hash); if (defined $snapshot_links) { print " | " . $snapshot_links; } print "
parent" . $cgi->a({-href => href(action=>"commit", hash=>$par), class => "list"}, $par) . "" . $cgi->a({-href => href(action=>"commit", hash=>$par)}, "commit") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "diff") . "
". "
\n"; print "
\n"; git_print_log($co{'comment'}); print "
\n"; git_difftree_body(\@difftree, $hash, @$parents); git_footer_html(); } sub git_object { # object is defined by: # - hash or hash_base alone # - hash_base and file_name my $type; # - hash or hash_base alone if ($hash || ($hash_base && !defined $file_name)) { my $object_id = $hash || $hash_base; open my $fd, "-|", quote_command( git_cmd(), 'cat-file', '-t', $object_id) . ' 2> /dev/null' or die_error(404, "Object does not exist"); $type = <$fd>; chomp $type; close $fd or die_error(404, "Object does not exist"); # - hash_base and file_name } elsif ($hash_base && defined $file_name) { $file_name =~ s,/+$,,; system(git_cmd(), "cat-file", '-e', $hash_base) == 0 or die_error(404, "Base object does not exist"); # here errors should not hapen open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name or die_error(500, "Open git-ls-tree failed"); my $line = <$fd>; close $fd; #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) { die_error(404, "File or directory for given base does not exist"); } $type = $2; $hash = $3; } else { die_error(400, "Not enough information to find object"); } print $cgi->redirect(-uri => href(action=>$type, -full=>1, hash=>$hash, hash_base=>$hash_base, file_name=>$file_name), -status => '302 Found'); } sub git_blobdiff { my $format = shift || 'html'; my $fd; my @difftree; my %diffinfo; my $expires; # preparing $fd and %diffinfo for git_patchset_body # new style URI if (defined $hash_base && defined $hash_parent_base) { if (defined $file_name) { # read raw output open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base, "--", (defined $file_parent ? $file_parent : ()), $file_name or die_error(500, "Open git-diff-tree failed"); @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(404, "Reading git-diff-tree failed"); @difftree or die_error(404, "Blob diff not found"); } elsif (defined $hash && $hash =~ /[0-9a-fA-F]{40}/) { # try to find filename from $hash # read filtered raw output open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base, "--" or die_error(500, "Open git-diff-tree failed"); @difftree = # ':100644 100644 03b21826... 3b93d5e7... M ls-files.c' # $hash == to_id grep { /^:[0-7]{6} [0-7]{6} [0-9a-fA-F]{40} $hash/ } map { chomp; $_ } <$fd>; close $fd or die_error(404, "Reading git-diff-tree failed"); @difftree or die_error(404, "Blob diff not found"); } else { die_error(400, "Missing one of the blob diff parameters"); } if (@difftree > 1) { die_error(400, "Ambiguous blob diff specification"); } %diffinfo = parse_difftree_raw_line($difftree[0]); $file_parent ||= $diffinfo{'from_file'} || $file_name; $file_name ||= $diffinfo{'to_file'}; $hash_parent ||= $diffinfo{'from_id'}; $hash ||= $diffinfo{'to_id'}; # non-textual hash id's can be cached if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ && $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) { $expires = '+1d'; } # open patch output open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, '-p', ($format eq 'html' ? "--full-index" : ()), $hash_parent_base, $hash_base, "--", (defined $file_parent ? $file_parent : ()), $file_name or die_error(500, "Open git-diff-tree failed"); } # old/legacy style URI -- not generated anymore since 1.4.3. if (!%diffinfo) { die_error('404 Not Found', "Missing one of the blob diff parameters") } # header if ($format eq 'html') { my $formats_nav = $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)}, "raw"); git_header_html(undef, $expires); if (defined $hash_base && (my %co = parse_commit($hash_base))) { git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); } else { print "

$formats_nav
\n"; print "
$hash vs $hash_parent
\n"; } if (defined $file_name) { git_print_page_path($file_name, "blob", $hash_base); } else { print "
\n"; } } elsif ($format eq 'plain') { print $cgi->header( -type => 'text/plain', -charset => 'utf-8', -expires => $expires, -content_disposition => 'inline; filename="' . "$file_name" . '.patch"'); print "X-Git-Url: " . $cgi->self_url() . "\n\n"; } else { die_error(400, "Unknown blobdiff format"); } # patch if ($format eq 'html') { print "
\n"; git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base); close $fd; print "
\n"; # class="page_body" git_footer_html(); } else { while (my $line = <$fd>) { $line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg; $line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg; print $line; last if $line =~ m!^\+\+\+!; } local $/ = undef; print <$fd>; close $fd; } } sub git_blobdiff_plain { git_blobdiff('plain'); } sub git_commitdiff { my %params = @_; my $format = $params{-format} || 'html'; my ($patch_max) = gitweb_get_feature('patches'); if ($format eq 'patch') { die_error(403, "Patch view not allowed") unless $patch_max; } $hash ||= $hash_base || "HEAD"; my %co = parse_commit($hash) or die_error(404, "Unknown commit object"); # choose format for commitdiff for merge if (! defined $hash_parent && @{$co{'parents'}} > 1) { $hash_parent = '--cc'; } # we need to prepare $formats_nav before almost any parameter munging my $formats_nav; if ($format eq 'html') { $formats_nav = $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); if ($patch_max && @{$co{'parents'}} <= 1) { $formats_nav .= " | " . $cgi->a({-href => href(action=>"patch", -replay=>1)}, "patch"); } if (defined $hash_parent && $hash_parent ne '-c' && $hash_parent ne '--cc') { # commitdiff with two commits given my $hash_parent_short = $hash_parent; if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) { $hash_parent_short = substr($hash_parent, 0, 7); } $formats_nav .= ' (from'; for (my $i = 0; $i < @{$co{'parents'}}; $i++) { if ($co{'parents'}[$i] eq $hash_parent) { $formats_nav .= ' parent ' . ($i+1); last; } } $formats_nav .= ': ' . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash_parent)}, esc_html($hash_parent_short)) . ')'; } elsif (!$co{'parent'}) { # --root commitdiff $formats_nav .= ' (initial)'; } elsif (scalar @{$co{'parents'}} == 1) { # single parent commit $formats_nav .= ' (parent: ' . $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'parent'})}, esc_html(substr($co{'parent'}, 0, 7))) . ')'; } else { # merge commit if ($hash_parent eq '--cc') { $formats_nav .= ' | ' . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>'-c')}, 'combined'); } else { # $hash_parent eq '-c' $formats_nav .= ' | ' . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>'--cc')}, 'compact'); } $formats_nav .= ' (merge: ' . join(' ', map { $cgi->a({-href => href(action=>"commitdiff", hash=>$_)}, esc_html(substr($_, 0, 7))); } @{$co{'parents'}} ) . ')'; } } my $hash_parent_param = $hash_parent; if (!defined $hash_parent_param) { # --cc for multiple parents, --root for parentless $hash_parent_param = @{$co{'parents'}} > 1 ? '--cc' : $co{'parent'} || '--root'; } # read commitdiff my $fd; my @difftree; if ($format eq 'html') { open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, "--no-commit-id", "--patch-with-raw", "--full-index", $hash_parent_param, $hash, "--" or die_error(500, "Open git-diff-tree failed"); while (my $line = <$fd>) { chomp $line; # empty line ends raw part of diff-tree output last unless $line; push @difftree, scalar parse_difftree_raw_line($line); } } elsif ($format eq 'plain') { open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, '-p', $hash_parent_param, $hash, "--" or die_error(500, "Open git-diff-tree failed"); } elsif ($format eq 'patch') { # For commit ranges, we limit the output to the number of # patches specified in the 'patches' feature. # For single commits, we limit the output to a single patch, # diverging from the git-format-patch default. my @commit_spec = (); if ($hash_parent) { if ($patch_max > 0) { push @commit_spec, "-$patch_max"; } push @commit_spec, '-n', "$hash_parent..$hash"; } else { if ($params{-single}) { push @commit_spec, '-1'; } else { if ($patch_max > 0) { push @commit_spec, "-$patch_max"; } push @commit_spec, "-n"; } push @commit_spec, '--root', $hash; } open $fd, "-|", git_cmd(), "format-patch", @diff_opts, '--encoding=utf8', '--stdout', @commit_spec or die_error(500, "Open git-format-patch failed"); } else { die_error(400, "Unknown commitdiff format"); } # non-textual hash id's can be cached my $expires; if ($hash =~ m/^[0-9a-fA-F]{40}$/) { $expires = "+1d"; } # write commit message if ($format eq 'html') { my $refs = git_get_references(); my $ref = format_ref_marker($refs, $co{'id'}); git_header_html(undef, $expires); git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); print "
\n" . "\n"; git_print_authorship_rows(\%co); print "
". "
\n"; print "
\n"; if (@{$co{'comment'}} > 1) { print "
\n"; git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1); print "
\n"; # class="log" } } elsif ($format eq 'plain') { my $refs = git_get_references("tags"); my $tagname = git_get_rev_name_tags($hash); my $filename = basename($project) . "-$hash.patch"; print $cgi->header( -type => 'text/plain', -charset => 'utf-8', -expires => $expires, -content_disposition => 'inline; filename="' . "$filename" . '"'); my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); print "From: " . to_utf8($co{'author'}) . "\n"; print "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n"; print "Subject: " . to_utf8($co{'title'}) . "\n"; print "X-Git-Tag: $tagname\n" if $tagname; print "X-Git-Url: " . $cgi->self_url() . "\n\n"; foreach my $line (@{$co{'comment'}}) { print to_utf8($line) . "\n"; } print "---\n\n"; } elsif ($format eq 'patch') { my $filename = basename($project) . "-$hash.patch"; print $cgi->header( -type => 'text/plain', -charset => 'utf-8', -expires => $expires, -content_disposition => 'inline; filename="' . "$filename" . '"'); } # write patch if ($format eq 'html') { my $use_parents = !defined $hash_parent || $hash_parent eq '-c' || $hash_parent eq '--cc'; git_difftree_body(\@difftree, $hash, $use_parents ? @{$co{'parents'}} : $hash_parent); print "
\n"; git_patchset_body($fd, \@difftree, $hash, $use_parents ? @{$co{'parents'}} : $hash_parent); close $fd; print "
\n"; # class="page_body" git_footer_html(); } elsif ($format eq 'plain') { local $/ = undef; print <$fd>; close $fd or print "Reading git-diff-tree failed\n"; } elsif ($format eq 'patch') { local $/ = undef; print <$fd>; close $fd or print "Reading git-format-patch failed\n"; } } sub git_commitdiff_plain { git_commitdiff(-format => 'plain'); } # format-patch-style patches sub git_patch { git_commitdiff(-format => 'patch', -single => 1); } sub git_patches { git_commitdiff(-format => 'patch'); } sub git_history { git_log_generic('history', \&git_history_body, $hash_base, $hash_parent_base, $file_name, $hash); } sub git_search { gitweb_check_feature('search') or die_error(403, "Search is disabled"); if (!defined $searchtext) { die_error(400, "Text field is empty"); } if (!defined $hash) { $hash = git_get_head_hash($project); } my %co = parse_commit($hash); if (!%co) { die_error(404, "Unknown commit object"); } if (!defined $page) { $page = 0; } $searchtype ||= 'commit'; if ($searchtype eq 'pickaxe') { # pickaxe may take all resources of your box and run for several minutes # with every query - so decide by yourself how public you make this feature gitweb_check_feature('pickaxe') or die_error(403, "Pickaxe is disabled"); } if ($searchtype eq 'grep') { gitweb_check_feature('grep') or die_error(403, "Grep is disabled"); } git_header_html(); if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') { my $greptype; if ($searchtype eq 'commit') { $greptype = "--grep="; } elsif ($searchtype eq 'author') { $greptype = "--author="; } elsif ($searchtype eq 'committer') { $greptype = "--committer="; } $greptype .= $searchtext; my @commitlist = parse_commits($hash, 101, (100 * $page), undef, $greptype, '--regexp-ignore-case', $search_use_regexp ? '--extended-regexp' : '--fixed-strings'); my $paging_nav = ''; if ($page > 0) { $paging_nav .= $cgi->a({-href => href(action=>"search", hash=>$hash, searchtext=>$searchtext, searchtype=>$searchtype)}, "first"); $paging_nav .= " ⋅ " . $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } my $next_link = ''; if ($#commitlist >= 100) { $next_link = $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); $paging_nav .= " ⋅ $next_link"; } else { $paging_nav .= " ⋅ next"; } if ($#commitlist >= 100) { } git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash); git_search_grep_body(\@commitlist, 0, 99, $next_link); } if ($searchtype eq 'pickaxe') { git_print_page_nav('','', $hash,$co{'tree'},$hash); git_print_header_div('commit', esc_html($co{'title'}), $hash); print "\n"; my $alternate = 1; local $/ = "\n"; open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts, '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext", ($search_use_regexp ? '--pickaxe-regex' : ()); undef %co; my @files; while (my $line = <$fd>) { chomp $line; next unless $line; my %set = parse_difftree_raw_line($line); if (defined $set{'commit'}) { # finish previous commit if (%co) { print "\n" . "\n" . "\n"; } if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; %co = parse_commit($set{'commit'}); my $author = chop_and_escape_str($co{'author_name'}, 15, 5); print "\n" . "\n" . "\n" . "\n" . "\n"; } print "
" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "
$co{'age_string_date'}$author" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, chop_and_escape_str($co{'title'}, 50) . "
"); } elsif (defined $set{'to_id'}) { next if ($set{'to_id'} =~ m/^0{40}$/); print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'}, hash=>$set{'to_id'}, file_name=>$set{'to_file'}), -class => "list"}, "" . esc_path($set{'file'}) . "") . "
\n"; } } close $fd; # finish last commit (warning: repetition!) if (%co) { print "
" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "
\n"; } if ($searchtype eq 'grep') { git_print_page_nav('','', $hash,$co{'tree'},$hash); git_print_header_div('commit', esc_html($co{'title'}), $hash); print "\n"; my $alternate = 1; my $matches = 0; local $/ = "\n"; open my $fd, "-|", git_cmd(), 'grep', '-n', $search_use_regexp ? ('-E', '-i') : '-F', $searchtext, $co{'tree'}; my $lastfile = ''; while (my $line = <$fd>) { chomp $line; my ($file, $lno, $ltext, $binary); last if ($matches++ > 1000); if ($line =~ /^Binary file (.+) matches$/) { $file = $1; $binary = 1; } else { (undef, $file, $lno, $ltext) = split(/:/, $line, 4); } if ($file ne $lastfile) { $lastfile and print "\n"; if ($alternate++) { print "\n"; } else { print "\n"; } print "\n"; if ($matches > 1000) { print "
Too many matches, listing trimmed
\n"; } } else { print "
No matches found
\n"; } close $fd; print "
". $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'}, file_name=>"$file"), -class => "list"}, esc_path($file)); print "\n"; $lastfile = $file; } if ($binary) { print "
Binary file
\n"; } else { $ltext = untabify($ltext); if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) { $ltext = esc_html($1, -nbsp=>1); $ltext .= ''; $ltext .= esc_html($2, -nbsp=>1); $ltext .= ''; $ltext .= esc_html($3, -nbsp=>1); } else { $ltext = esc_html($ltext, -nbsp=>1); } print "
" . $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'}, file_name=>"$file").'#l'.$lno, -class => "linenr"}, sprintf('%4i', $lno)) . ' ' . $ltext . "
\n"; } } if ($lastfile) { print "
\n"; } git_footer_html(); } sub git_search_help { git_header_html(); git_print_page_nav('','', $hash,$hash,$hash); print <Pattern is by default a normal string that is matched precisely (but without regard to case, except in the case of pickaxe). However, when you check the re checkbox, the pattern entered is recognized as the POSIX extended regular expression (also case insensitive).

commit
The commit messages and authorship information will be scanned for the given pattern.
EOT my $have_grep = gitweb_check_feature('grep'); if ($have_grep) { print <grep
All files in the currently selected tree (HEAD unless you are explicitly browsing a different one) are searched for the given pattern. On large trees, this search can take a while and put some strain on the server, so please use it with some consideration. Note that due to git-grep peculiarity, currently if regexp mode is turned off, the matches are case-sensitive.
EOT } print <author
Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.
committer
Name and e-mail of the committer and date of commit will be scanned for the given pattern.
EOT my $have_pickaxe = gitweb_check_feature('pickaxe'); if ($have_pickaxe) { print <pickaxe
All commits that caused the string to appear or disappear from any file (changes that added, removed or "modified" the string) will be listed. This search can take a while and takes a lot of strain on the server, so please use it wisely. Note that since you may be interested even in changes just changing the case as well, this search is case sensitive.
EOT } print "
\n"; git_footer_html(); } sub git_shortlog { git_log_generic('shortlog', \&git_shortlog_body, $hash, $hash_parent); } ## ...................................................................... ## feeds (RSS, Atom; OPML) sub git_feed { my $format = shift || 'atom'; my $have_blame = gitweb_check_feature('blame'); # Atom: http://www.atomenabled.org/developers/syndication/ # RSS: http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ if ($format ne 'rss' && $format ne 'atom') { die_error(400, "Unknown web feed format"); } # log/feed of current (HEAD) branch, log of given branch, history of file/directory my $head = $hash || 'HEAD'; my @commitlist = parse_commits($head, 150, 0, $file_name); my %latest_commit; my %latest_date; my $content_type = "application/$format+xml"; if (defined $cgi->http('HTTP_ACCEPT') && $cgi->Accept('text/xml') > $cgi->Accept($content_type)) { # browser (feed reader) prefers text/xml $content_type = 'text/xml'; } if (defined($commitlist[0])) { %latest_commit = %{$commitlist[0]}; my $latest_epoch = $latest_commit{'committer_epoch'}; %latest_date = parse_date($latest_epoch); my $if_modified = $cgi->http('IF_MODIFIED_SINCE'); if (defined $if_modified) { my $since; if (eval { require HTTP::Date; 1; }) { $since = HTTP::Date::str2time($if_modified); } elsif (eval { require Time::ParseDate; 1; }) { $since = Time::ParseDate::parsedate($if_modified, GMT => 1); } if (defined $since && $latest_epoch <= $since) { print $cgi->header( -type => $content_type, -charset => 'utf-8', -last_modified => $latest_date{'rfc2822'}, -status => '304 Not Modified'); return; } } print $cgi->header( -type => $content_type, -charset => 'utf-8', -last_modified => $latest_date{'rfc2822'}); } else { print $cgi->header( -type => $content_type, -charset => 'utf-8'); } # Optimization: skip generating the body if client asks only # for Last-Modified date. return if ($cgi->request_method() eq 'HEAD'); # header variables my $title = "$site_name - $project/$action"; my $feed_type = 'log'; if (defined $hash) { $title .= " - '$hash'"; $feed_type = 'branch log'; if (defined $file_name) { $title .= " :: $file_name"; $feed_type = 'history'; } } elsif (defined $file_name) { $title .= " - $file_name"; $feed_type = 'history'; } $title .= " $feed_type"; my $descr = git_get_project_description($project); if (defined $descr) { $descr = esc_html($descr); } else { $descr = "$project " . ($format eq 'rss' ? 'RSS' : 'Atom') . " feed"; } my $owner = git_get_project_owner($project); $owner = esc_html($owner); #header my $alt_url; if (defined $file_name) { $alt_url = href(-full=>1, action=>"history", hash=>$hash, file_name=>$file_name); } elsif (defined $hash) { $alt_url = href(-full=>1, action=>"log", hash=>$hash); } else { $alt_url = href(-full=>1, action=>"summary"); } print qq!\n!; if ($format eq 'rss') { print < XML print "$title\n" . "$alt_url\n" . "$descr\n" . "en\n" . # project owner is responsible for 'editorial' content "$owner\n"; if (defined $logo || defined $favicon) { # prefer the logo to the favicon, since RSS # doesn't allow both my $img = esc_url($logo || $favicon); print "\n" . "$img\n" . "$title\n" . "$alt_url\n" . "\n"; } if (%latest_date) { print "$latest_date{'rfc2822'}\n"; print "$latest_date{'rfc2822'}\n"; } print "gitweb v.$version/$git_version\n"; } elsif ($format eq 'atom') { print < XML print "$title\n" . "$descr\n" . '' . "\n" . '' . "\n" . "" . href(-full=>1) . "\n" . # use project owner for feed author "$owner\n"; if (defined $favicon) { print "" . esc_url($favicon) . "\n"; } if (defined $logo_url) { # not twice as wide as tall: 72 x 27 pixels print "" . esc_url($logo) . "\n"; } if (! %latest_date) { # dummy date to keep the feed valid until commits trickle in: print "1970-01-01T00:00:00Z\n"; } else { print "$latest_date{'iso-8601'}\n"; } print "gitweb\n"; } # contents for (my $i = 0; $i <= $#commitlist; $i++) { my %co = %{$commitlist[$i]}; my $commit = $co{'id'}; # we read 150, we always show 30 and the ones more recent than 48 hours if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) { last; } my %cd = parse_date($co{'author_epoch'}); # get list of changed files open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $co{'parent'} || "--root", $co{'id'}, "--", (defined $file_name ? $file_name : ()) or next; my @difftree = map { chomp; $_ } <$fd>; close $fd or next; # print element (entry, item) my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit); if ($format eq 'rss') { print "\n" . "" . esc_html($co{'title'}) . "\n" . "" . esc_html($co{'author'}) . "\n" . "$cd{'rfc2822'}\n" . "$co_url\n" . "$co_url\n" . "" . esc_html($co{'title'}) . "\n" . "" . "\n" . "" . esc_html($co{'title'}) . "\n" . "$cd{'iso-8601'}\n" . "\n" . " " . esc_html($co{'author_name'}) . "\n"; if ($co{'author_email'}) { print " " . esc_html($co{'author_email'}) . "\n"; } print "\n" . # use committer for contributor "\n" . " " . esc_html($co{'committer_name'}) . "\n"; if ($co{'committer_email'}) { print " " . esc_html($co{'committer_email'}) . "\n"; } print "\n" . "$cd{'iso-8601'}\n" . "\n" . "$co_url\n" . "\n" . "
\n"; } my $comment = $co{'comment'}; print "
\n";
		foreach my $line (@$comment) {
			$line = esc_html($line);
			print "$line\n";
		}
		print "
    \n"; foreach my $difftree_line (@difftree) { my %difftree = parse_difftree_raw_line($difftree_line); next if !$difftree{'from_id'}; my $file = $difftree{'file'} || $difftree{'to_file'}; print "
  • " . "[" . $cgi->a({-href => href(-full=>1, action=>"blobdiff", hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'}, hash_base=>$co{'id'}, hash_parent_base=>$co{'parent'}, file_name=>$file, file_parent=>$difftree{'from_file'}), -title => "diff"}, 'D'); if ($have_blame) { print $cgi->a({-href => href(-full=>1, action=>"blame", file_name=>$file, hash_base=>$commit), -title => "blame"}, 'B'); } # if this is not a feed of a file history if (!defined $file_name || $file_name ne $file) { print $cgi->a({-href => href(-full=>1, action=>"history", file_name=>$file, hash=>$commit), -title => "history"}, 'H'); } $file = esc_path($file); print "] ". "$file
  • \n"; } if ($format eq 'rss') { print "
]]>\n" . "\n" . "\n"; } elsif ($format eq 'atom') { print "\n
\n" . "
\n" . "\n"; } } # end of feed if ($format eq 'rss') { print "
\n\n"; } elsif ($format eq 'atom') { print "\n"; } } sub git_rss { git_feed('rss'); } sub git_atom { git_feed('atom'); } sub git_opml { my @list = git_get_projects_list(); print $cgi->header( -type => 'text/xml', -charset => 'utf-8', -content_disposition => 'inline; filename="opml.xml"'); print < $site_name OPML Export XML foreach my $pr (@list) { my %proj = %$pr; my $head = git_get_head_hash($proj{'path'}); if (!defined $head) { next; } $git_dir = "$projectroot/$proj{'path'}"; my %co = parse_commit($head); if (!%co) { next; } my $path = esc_html(chop_str($proj{'path'}, 25, 5)); my $rss = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1); my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1); print "\n"; } print < XML } 07070100055ee6000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000002300000000root/usr/local/share/gitweb/static07070100055ee9000081a40000000000000000000000014c5c5b3a000022dc0000010000010036ffffffffffffffff0000002e00000000root/usr/local/share/gitweb/static/gitweb.cssbody { font-family: sans-serif; font-size: small; border: solid #d9d8d1; border-width: 1px; margin: 10px; background-color: #ffffff; color: #000000; } a { color: #0000cc; } a:hover, a:visited, a:active { color: #880000; } span.cntrl { border: dashed #aaaaaa; border-width: 1px; padding: 0px 2px 0px 2px; margin: 0px 2px 0px 2px; } img.logo { float: right; border-width: 0px; } img.avatar { vertical-align: middle; } a.list img.avatar { border-style: none; } div.page_header { height: 25px; padding: 8px; font-size: 150%; font-weight: bold; background-color: #d9d8d1; } div.page_header a:visited, a.header { color: #0000cc; } div.page_header a:hover { color: #880000; } div.page_nav { padding: 8px; } div.page_nav a:visited { color: #0000cc; } div.page_path { padding: 8px; font-weight: bold; border: solid #d9d8d1; border-width: 0px 0px 1px; } div.page_footer { height: 17px; padding: 4px 8px; background-color: #d9d8d1; } div.page_footer_text { float: left; color: #555555; font-style: italic; } div#generating_info { margin: 4px; font-size: smaller; text-align: center; color: #505050; } div.page_body { padding: 8px; font-family: monospace; } div.title, a.title { display: block; padding: 6px 8px; font-weight: bold; background-color: #edece6; text-decoration: none; color: #000000; } div.readme { padding: 8px; } a.title:hover { background-color: #d9d8d1; } div.title_text { padding: 6px 0px; border: solid #d9d8d1; border-width: 0px 0px 1px; font-family: monospace; } div.log_body { padding: 8px 8px 8px 150px; } span.age { position: relative; float: left; width: 142px; font-style: italic; } span.signoff { color: #888888; } div.log_link { padding: 0px 8px; font-size: 70%; font-family: sans-serif; font-style: normal; position: relative; float: left; width: 136px; } div.list_head { padding: 6px 8px 4px; border: solid #d9d8d1; border-width: 1px 0px 0px; font-style: italic; } .author_date, .author { font-style: italic; } div.author_date { padding: 8px; border: solid #d9d8d1; border-width: 0px 0px 1px 0px; } a.list { text-decoration: none; color: #000000; } a.subject, a.name { font-weight: bold; } table.tags a.subject { font-weight: normal; } a.list:hover { text-decoration: underline; color: #880000; } a.text { text-decoration: none; color: #0000cc; } a.text:visited { text-decoration: none; color: #880000; } a.text:hover { text-decoration: underline; color: #880000; } table { padding: 8px 4px; border-spacing: 0; } table.diff_tree { font-family: monospace; } table.combined.diff_tree th { text-align: center; } table.combined.diff_tree td { padding-right: 24px; } table.combined.diff_tree th.link, table.combined.diff_tree td.link { padding: 0px 2px; } table.combined.diff_tree td.nochange a { color: #6666ff; } table.combined.diff_tree td.nochange a:hover, table.combined.diff_tree td.nochange a:visited { color: #d06666; } table.blame { border-collapse: collapse; } table.blame td { padding: 0px 5px; font-size: 100%; vertical-align: top; } th { padding: 2px 5px; font-size: 100%; text-align: left; } /* do not change row style on hover for 'blame' view */ tr.light, table.blame .light:hover { background-color: #ffffff; } tr.dark, table.blame .dark:hover { background-color: #f6f6f0; } /* currently both use the same, but it can change */ tr.light:hover, tr.dark:hover { background-color: #edece6; } /* boundary commits in 'blame' view */ /* and commits without "previous" */ tr.boundary td.sha1, tr.no-previous td.linenr { font-weight: bold; } /* for 'blame_incremental', during processing */ tr.color1 { background-color: #f6fff6; } tr.color2 { background-color: #f6f6ff; } tr.color3 { background-color: #fff6f6; } td { padding: 2px 5px; font-size: 100%; vertical-align: top; } td.link, td.selflink { padding: 2px 5px; font-family: sans-serif; font-size: 70%; } td.selflink { padding-right: 0px; } td.sha1 { font-family: monospace; } .error { color: red; background-color: yellow; } td.current_head { text-decoration: underline; } table.diff_tree span.file_status.new { color: #008000; } table.diff_tree span.file_status.deleted { color: #c00000; } table.diff_tree span.file_status.moved, table.diff_tree span.file_status.mode_chnge { color: #777777; } table.diff_tree span.file_status.copied { color: #70a070; } /* noage: "No commits" */ table.project_list td.noage { color: #808080; font-style: italic; } /* age2: 60*60*24*2 <= age */ table.project_list td.age2, table.blame td.age2 { font-style: italic; } /* age1: 60*60*2 <= age < 60*60*24*2 */ table.project_list td.age1 { color: #009900; font-style: italic; } table.blame td.age1 { color: #009900; background: transparent; } /* age0: age < 60*60*2 */ table.project_list td.age0 { color: #009900; font-style: italic; font-weight: bold; } table.blame td.age0 { color: #009900; background: transparent; font-weight: bold; } td.pre, div.pre, div.diff { font-family: monospace; font-size: 12px; white-space: pre; } td.mode { font-family: monospace; } /* progress of blame_interactive */ div#progress_bar { height: 2px; margin-bottom: -2px; background-color: #d8d9d0; } div#progress_info { float: right; text-align: right; } /* format of (optional) objects size in 'tree' view */ td.size { font-family: monospace; text-align: right; } /* styling of diffs (patchsets): commitdiff and blobdiff views */ div.diff.header, div.diff.extended_header { white-space: normal; } div.diff.header { font-weight: bold; background-color: #edece6; margin-top: 4px; padding: 4px 0px 2px 0px; border: solid #d9d8d1; border-width: 1px 0px 1px 0px; } div.diff.header a.path { text-decoration: underline; } div.diff.extended_header, div.diff.extended_header a.path, div.diff.extended_header a.hash { color: #777777; } div.diff.extended_header .info { color: #b0b0b0; } div.diff.extended_header { background-color: #f6f5ee; padding: 2px 0px 2px 0px; } div.diff a.list, div.diff a.path, div.diff a.hash { text-decoration: none; } div.diff a.list:hover, div.diff a.path:hover, div.diff a.hash:hover { text-decoration: underline; } div.diff.to_file a.path, div.diff.to_file { color: #007000; } div.diff.add { color: #008800; } div.diff.from_file a.path, div.diff.from_file { color: #aa0000; } div.diff.rem { color: #cc0000; } div.diff.chunk_header a, div.diff.chunk_header { color: #990099; } div.diff.chunk_header { border: dotted #ffe0ff; border-width: 1px 0px 0px 0px; margin-top: 2px; } div.diff.chunk_header span.chunk_info { background-color: #ffeeff; } div.diff.chunk_header span.section { color: #aa22aa; } div.diff.incomplete { color: #cccccc; } div.diff.nodifferences { font-weight: bold; color: #600000; } div.index_include { border: solid #d9d8d1; border-width: 0px 0px 1px; padding: 12px 8px; } div.search { font-size: 100%; font-weight: normal; margin: 4px 8px; float: right; top: 56px; right: 12px } p.projsearch { text-align: center; } td.linenr { text-align: right; } a.linenr { color: #999999; text-decoration: none } a.rss_logo { float: right; padding: 3px 0px; width: 35px; line-height: 10px; border: 1px solid; border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; color: #ffffff; background-color: #ff6600; font-weight: bold; font-family: sans-serif; font-size: 70%; text-align: center; text-decoration: none; } a.rss_logo:hover { background-color: #ee5500; } a.rss_logo.generic { background-color: #ff8800; } a.rss_logo.generic:hover { background-color: #ee7700; } span.refs span { padding: 0px 4px; font-size: 70%; font-weight: normal; border: 1px solid; background-color: #ffaaff; border-color: #ffccff #ff00ee #ff00ee #ffccff; } span.refs span a { text-decoration: none; color: inherit; } span.refs span a:hover { text-decoration: underline; } span.refs span.indirect { font-style: italic; } span.refs span.ref { background-color: #aaaaff; border-color: #ccccff #0033cc #0033cc #ccccff; } span.refs span.tag { background-color: #ffffaa; border-color: #ffffcc #ffee00 #ffee00 #ffffcc; } span.refs span.head { background-color: #aaffaa; border-color: #ccffcc #00cc33 #00cc33 #ccffcc; } span.atnight { color: #cc0000; } span.match { color: #e00000; } div.binary { font-style: italic; } /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */ /* Highlighting theme definition: */ .num { color:#2928ff; } .esc { color:#ff00ff; } .str { color:#ff0000; } .dstr { color:#818100; } .slc { color:#838183; font-style:italic; } .com { color:#838183; font-style:italic; } .dir { color:#008200; } .sym { color:#000000; } .line { color:#555555; } .kwa { color:#000000; font-weight:bold; } .kwb { color:#830000; } .kwc { color:#000000; font-weight:bold; } .kwd { color:#010181; } 07070100055eea000081a40000000000000000000000014c5c5b3a00005e4e0000010000010036ffffffffffffffff0000002d00000000root/usr/local/share/gitweb/static/gitweb.js// Copyright (C) 2007, Fredrik Kuivinen // 2007, Petr Baudis // 2008-2009, Jakub Narebski /** * @fileOverview JavaScript code for gitweb (git web interface). * @license GPLv2 or later */ /* ============================================================ */ /* functions for generic gitweb actions and views */ /** * used to check if link has 'js' query parameter already (at end), * and other reasons to not add 'js=1' param at the end of link * @constant */ var jsExceptionsRe = /[;?]js=[01]$/; /** * Add '?js=1' or ';js=1' to the end of every link in the document * that doesn't have 'js' query parameter set already. * * Links with 'js=1' lead to JavaScript version of given action, if it * exists (currently there is only 'blame_incremental' for 'blame') * * @globals jsExceptionsRe */ function fixLinks() { var allLinks = document.getElementsByTagName("a") || document.links; for (var i = 0, len = allLinks.length; i < len; i++) { var link = allLinks[i]; if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/; link.href += (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1'; } } } /* ============================================================ */ /* * This code uses DOM methods instead of (nonstandard) innerHTML * to modify page. * * innerHTML is non-standard IE extension, though supported by most * browsers; however Firefox up to version 1.5 didn't implement it in * a strict mode (application/xml+xhtml mimetype). * * Also my simple benchmarks show that using elem.firstChild.data = * 'content' is slightly faster than elem.innerHTML = 'content'. It * is however more fragile (text element fragment must exists), and * less feature-rich (we cannot add HTML). * * Note that DOM 2 HTML is preferred over generic DOM 2 Core; the * equivalent using DOM 2 Core is usually shown in comments. */ /* ============================================================ */ /* generic utility functions */ /** * pad number N with nonbreakable spaces on the left, to WIDTH characters * example: padLeftStr(12, 3, '\u00A0') == '\u00A012' * ('\u00A0' is nonbreakable space) * * @param {Number|String} input: number to pad * @param {Number} width: visible width of output * @param {String} str: string to prefix to string, e.g. '\u00A0' * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR */ function padLeftStr(input, width, str) { var prefix = ''; width -= input.toString().length; while (width > 0) { prefix += str; width--; } return prefix + input; } /** * Pad INPUT on the left to SIZE width, using given padding character CH, * for example padLeft('a', 3, '_') is '__a'. * * @param {String} input: input value converted to string. * @param {Number} width: desired length of output. * @param {String} ch: single character to prefix to string. * * @returns {String} Modified string, at least SIZE length. */ function padLeft(input, width, ch) { var s = input + ""; while (s.length < width) { s = ch + s; } return s; } /** * Create XMLHttpRequest object in cross-browser way * @returns XMLHttpRequest object, or null */ function createRequestObject() { try { return new XMLHttpRequest(); } catch (e) {} try { return window.createRequest(); } catch (e) {} try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {} try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} return null; } /* ============================================================ */ /* utility/helper functions (and variables) */ var xhr; // XMLHttpRequest object var projectUrl; // partial query + separator ('?' or ';') // 'commits' is an associative map. It maps SHA1s to Commit objects. var commits = {}; /** * constructor for Commit objects, used in 'blame' * @class Represents a blamed commit * @param {String} sha1: SHA-1 identifier of a commit */ function Commit(sha1) { if (this instanceof Commit) { this.sha1 = sha1; this.nprevious = 0; /* number of 'previous', effective parents */ } else { return new Commit(sha1); } } /* ............................................................ */ /* progress info, timing, error reporting */ var blamedLines = 0; var totalLines = '???'; var div_progress_bar; var div_progress_info; /** * Detects how many lines does a blamed file have, * This information is used in progress info * * @returns {Number|String} Number of lines in file, or string '...' */ function countLines() { var table = document.getElementById('blame_table') || document.getElementsByTagName('table')[0]; if (table) { return table.getElementsByTagName('tr').length - 1; // for header } else { return '...'; } } /** * update progress info and length (width) of progress bar * * @globals div_progress_info, div_progress_bar, blamedLines, totalLines */ function updateProgressInfo() { if (!div_progress_info) { div_progress_info = document.getElementById('progress_info'); } if (!div_progress_bar) { div_progress_bar = document.getElementById('progress_bar'); } if (!div_progress_info && !div_progress_bar) { return; } var percentage = Math.floor(100.0*blamedLines/totalLines); if (div_progress_info) { div_progress_info.firstChild.data = blamedLines + ' / ' + totalLines + ' (' + padLeftStr(percentage, 3, '\u00A0') + '%)'; } if (div_progress_bar) { //div_progress_bar.setAttribute('style', 'width: '+percentage+'%;'); div_progress_bar.style.width = percentage + '%'; } } var t_interval_server = ''; var cmds_server = ''; var t0 = new Date(); /** * write how much it took to generate data, and to run script * * @globals t0, t_interval_server, cmds_server */ function writeTimeInterval() { var info_time = document.getElementById('generating_time'); if (!info_time || !t_interval_server) { return; } var t1 = new Date(); info_time.firstChild.data += ' + (' + t_interval_server + ' sec server blame_data / ' + (t1.getTime() - t0.getTime())/1000 + ' sec client JavaScript)'; var info_cmds = document.getElementById('generating_cmd'); if (!info_time || !cmds_server) { return; } info_cmds.firstChild.data += ' + ' + cmds_server; } /** * show an error message alert to user within page (in prohress info area) * @param {String} str: plain text error message (no HTML) * * @globals div_progress_info */ function errorInfo(str) { if (!div_progress_info) { div_progress_info = document.getElementById('progress_info'); } if (div_progress_info) { div_progress_info.className = 'error'; div_progress_info.firstChild.data = str; } } /* ............................................................ */ /* coloring rows during blame_data (git blame --incremental) run */ /** * used to extract N from 'colorN', where N is a number, * @constant */ var colorRe = /\bcolor([0-9]*)\b/; /** * return N if , otherwise return null * (some browsers require CSS class names to begin with letter) * * @param {HTMLElement} tr: table row element to check * @param {String} tr.className: 'class' attribute of tr element * @returns {Number|null} N if tr.className == 'colorN', otherwise null * * @globals colorRe */ function getColorNo(tr) { if (!tr) { return null; } var className = tr.className; if (className) { var match = colorRe.exec(className); if (match) { return parseInt(match[1], 10); } } return null; } var colorsFreq = [0, 0, 0]; /** * return one of given possible colors (curently least used one) * example: chooseColorNoFrom(2, 3) returns 2 or 3 * * @param {Number[]} arguments: one or more numbers * assumes that 1 <= arguments[i] <= colorsFreq.length * @returns {Number} Least used color number from arguments * @globals colorsFreq */ function chooseColorNoFrom() { // choose the color which is least used var colorNo = arguments[0]; for (var i = 1; i < arguments.length; i++) { if (colorsFreq[arguments[i]-1] < colorsFreq[colorNo-1]) { colorNo = arguments[i]; } } colorsFreq[colorNo-1]++; return colorNo; } /** * given two neigbour elements, find color which would be different * from color of both of neighbours; used to 3-color blame table * * @param {HTMLElement} tr_prev * @param {HTMLElement} tr_next * @returns {Number} color number N such that * colorN != tr_prev.className && colorN != tr_next.className */ function findColorNo(tr_prev, tr_next) { var color_prev = getColorNo(tr_prev); var color_next = getColorNo(tr_next); // neither of neighbours has color set // THEN we can use any of 3 possible colors if (!color_prev && !color_next) { return chooseColorNoFrom(1,2,3); } // either both neighbours have the same color, // or only one of neighbours have color set // THEN we can use any color except given var color; if (color_prev === color_next) { color = color_prev; // = color_next; } else if (!color_prev) { color = color_next; } else if (!color_next) { color = color_prev; } if (color) { return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1); } // neighbours have different colors // THEN there is only one color left return (3 - ((color_prev + color_next) % 3)); } /* ............................................................ */ /* coloring rows like 'blame' after 'blame_data' finishes */ /** * returns true if given row element (tr) is first in commit group * to be used only after 'blame_data' finishes (after processing) * * @param {HTMLElement} tr: table row * @returns {Boolean} true if TR is first in commit group */ function isStartOfGroup(tr) { return tr.firstChild.className === 'sha1'; } /** * change colors to use zebra coloring (2 colors) instead of 3 colors * concatenate neighbour commit groups belonging to the same commit * * @globals colorRe */ function fixColorsAndGroups() { var colorClasses = ['light', 'dark']; var linenum = 1; var tr, prev_group; var colorClass = 0; var table = document.getElementById('blame_table') || document.getElementsByTagName('table')[0]; while ((tr = document.getElementById('l'+linenum))) { // index origin is 0, which is table header; start from 1 //while ((tr = table.rows[linenum])) { // <- it is slower if (isStartOfGroup(tr, linenum, document)) { if (prev_group && prev_group.firstChild.firstChild.href === tr.firstChild.firstChild.href) { // we have to concatenate groups var prev_rows = prev_group.firstChild.rowSpan || 1; var curr_rows = tr.firstChild.rowSpan || 1; prev_group.firstChild.rowSpan = prev_rows + curr_rows; //tr.removeChild(tr.firstChild); tr.deleteCell(0); // DOM2 HTML way } else { colorClass = (colorClass + 1) % 2; prev_group = tr; } } var tr_class = tr.className; tr.className = tr_class.replace(colorRe, colorClasses[colorClass]); linenum++; } } /* ............................................................ */ /* time and data */ /** * used to extract hours and minutes from timezone info, e.g '-0900' * @constant */ var tzRe = /^([+-][0-9][0-9])([0-9][0-9])$/; /** * return date in local time formatted in iso-8601 like format * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200' * * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC' * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM' * @returns {String} date in local time in iso-8601 like format * * @globals tzRe */ function formatDateISOLocal(epoch, timezoneInfo) { var match = tzRe.exec(timezoneInfo); // date corrected by timezone var localDate = new Date(1000 * (epoch + (parseInt(match[1],10)*3600 + parseInt(match[2],10)*60))); var localDateStr = // e.g. '2005-08-07' localDate.getUTCFullYear() + '-' + padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' + padLeft(localDate.getUTCDate(), 2, '0'); var localTimeStr = // e.g. '21:49:46' padLeft(localDate.getUTCHours(), 2, '0') + ':' + padLeft(localDate.getUTCMinutes(), 2, '0') + ':' + padLeft(localDate.getUTCSeconds(), 2, '0'); return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo; } /* ............................................................ */ /* unquoting/unescaping filenames */ /**#@+ * @constant */ var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g; var octEscRe = /^[0-7]{1,3}$/; var maybeQuotedRe = /^\"(.*)\"$/; /**#@-*/ /** * unquote maybe git-quoted filename * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a a' * * @param {String} str: git-quoted string * @returns {String} Unquoted and unescaped string * * @globals escCodeRe, octEscRe, maybeQuotedRe */ function unquote(str) { function unq(seq) { var es = { // character escape codes, aka escape sequences (from C) // replacements are to some extent JavaScript specific t: "\t", // tab (HT, TAB) n: "\n", // newline (NL) r: "\r", // return (CR) f: "\f", // form feed (FF) b: "\b", // backspace (BS) a: "\x07", // alarm (bell) (BEL) e: "\x1B", // escape (ESC) v: "\v" // vertical tab (VT) }; if (seq.search(octEscRe) !== -1) { // octal char sequence return String.fromCharCode(parseInt(seq, 8)); } else if (seq in es) { // C escape sequence, aka character escape code return es[seq]; } // quoted ordinary character return seq; } var match = str.match(maybeQuotedRe); if (match) { str = match[1]; // perhaps str = eval('"'+str+'"'); would be enough? str = str.replace(escCodeRe, function (substr, p1, offset, s) { return unq(p1); }); } return str; } /* ============================================================ */ /* main part: parsing response */ /** * Function called for each blame entry, as soon as it finishes. * It updates page via DOM manipulation, adding sha1 info, etc. * * @param {Commit} commit: blamed commit * @param {Object} group: object representing group of lines, * which blame the same commit (blame entry) * * @globals blamedLines */ function handleLine(commit, group) { /* This is the structure of the HTML fragment we are working with: 123 # times (my ext3 doesn't). */ var resline = group.resline; // format date and time string only once per commit if (!commit.info) { /* e.g. 'Kay Sievers, 2005-08-07 21:49:46 +0200' */ commit.info = commit.author + ', ' + formatDateISOLocal(commit.authorTime, commit.authorTimezone); } // color depends on group of lines, not only on blamed commit var colorNo = findColorNo( document.getElementById('l'+(resline-1)), document.getElementById('l'+(resline+group.numlines)) ); // loop over lines in commit group for (var i = 0; i < group.numlines; i++, resline++) { var tr = document.getElementById('l'+resline); if (!tr) { break; } /* 123 # times (my ext3 doesn't). */ var td_sha1 = tr.firstChild; var a_sha1 = td_sha1.firstChild; var a_linenr = td_sha1.nextSibling.firstChild; /* */ var tr_class = ''; if (colorNo !== null) { tr_class = 'color'+colorNo; } if (commit.boundary) { tr_class += ' boundary'; } if (commit.nprevious === 0) { tr_class += ' no-previous'; } else if (commit.nprevious > 1) { tr_class += ' multiple-previous'; } tr.className = tr_class; /* ? */ if (i === 0) { td_sha1.title = commit.info; td_sha1.rowSpan = group.numlines; a_sha1.href = projectUrl + 'a=commit;h=' + commit.sha1; if (a_sha1.firstChild) { a_sha1.firstChild.data = commit.sha1.substr(0, 8); } else { a_sha1.appendChild( document.createTextNode(commit.sha1.substr(0, 8))); } if (group.numlines >= 2) { var fragment = document.createDocumentFragment(); var br = document.createElement("br"); var match = commit.author.match(/\b([A-Z])\B/g); if (match) { var text = document.createTextNode( match.join('')); } if (br && text) { var elem = fragment || td_sha1; elem.appendChild(br); elem.appendChild(text); if (fragment) { td_sha1.appendChild(fragment); } } } } else { //tr.removeChild(td_sha1); // DOM2 Core way tr.deleteCell(0); // DOM2 HTML way } /* 123 */ var linenr_commit = ('previous' in commit ? commit.previous : commit.sha1); var linenr_filename = ('file_parent' in commit ? commit.file_parent : commit.filename); a_linenr.href = projectUrl + 'a=blame_incremental' + ';hb=' + linenr_commit + ';f=' + encodeURIComponent(linenr_filename) + '#l' + (group.srcline + i); blamedLines++; //updateProgressInfo(); } } // ---------------------------------------------------------------------- var inProgress = false; // are we processing response /**#@+ * @constant */ var sha1Re = /^([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)/; var infoRe = /^([a-z-]+) ?(.*)/; var endRe = /^END ?([^ ]*) ?(.*)/; /**@-*/ var curCommit = new Commit(); var curGroup = {}; var pollTimer = null; /** * Parse output from 'git blame --incremental [...]', received via * XMLHttpRequest from server (blamedataUrl), and call handleLine * (which updates page) as soon as blame entry is completed. * * @param {String[]} lines: new complete lines from blamedata server * * @globals commits, curCommit, curGroup, t_interval_server, cmds_server * @globals sha1Re, infoRe, endRe */ function processBlameLines(lines) { var match; for (var i = 0, len = lines.length; i < len; i++) { if ((match = sha1Re.exec(lines[i]))) { var sha1 = match[1]; var srcline = parseInt(match[2], 10); var resline = parseInt(match[3], 10); var numlines = parseInt(match[4], 10); var c = commits[sha1]; if (!c) { c = new Commit(sha1); commits[sha1] = c; } curCommit = c; curGroup.srcline = srcline; curGroup.resline = resline; curGroup.numlines = numlines; } else if ((match = infoRe.exec(lines[i]))) { var info = match[1]; var data = match[2]; switch (info) { case 'filename': curCommit.filename = unquote(data); // 'filename' information terminates the entry handleLine(curCommit, curGroup); updateProgressInfo(); break; case 'author': curCommit.author = data; break; case 'author-time': curCommit.authorTime = parseInt(data, 10); break; case 'author-tz': curCommit.authorTimezone = data; break; case 'previous': curCommit.nprevious++; // store only first 'previous' header if (!'previous' in curCommit) { var parts = data.split(' ', 2); curCommit.previous = parts[0]; curCommit.file_parent = unquote(parts[1]); } break; case 'boundary': curCommit.boundary = true; break; } // end switch } else if ((match = endRe.exec(lines[i]))) { t_interval_server = match[1]; cmds_server = match[2]; } else if (lines[i] !== '') { // malformed line } // end if (match) } // end for (lines) } /** * Process new data and return pointer to end of processed part * * @param {String} unprocessed: new data (from nextReadPos) * @param {Number} nextReadPos: end of last processed data * @return {Number} end of processed data (new value for nextReadPos) */ function processData(unprocessed, nextReadPos) { var lastLineEnd = unprocessed.lastIndexOf('\n'); if (lastLineEnd !== -1) { var lines = unprocessed.substring(0, lastLineEnd).split('\n'); nextReadPos += lastLineEnd + 1 /* 1 == '\n'.length */; processBlameLines(lines); } // end if return nextReadPos; } /** * Handle XMLHttpRequest errors * * @param {XMLHttpRequest} xhr: XMLHttpRequest object * * @globals pollTimer, commits, inProgress */ function handleError(xhr) { errorInfo('Server error: ' + xhr.status + ' - ' + (xhr.statusText || 'Error contacting server')); clearInterval(pollTimer); commits = {}; // free memory inProgress = false; } /** * Called after XMLHttpRequest finishes (loads) * * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused) * * @globals pollTimer, commits, inProgress */ function responseLoaded(xhr) { clearInterval(pollTimer); fixColorsAndGroups(); writeTimeInterval(); commits = {}; // free memory inProgress = false; } /** * handler for XMLHttpRequest onreadystatechange event * @see startBlame * * @globals xhr, inProgress */ function handleResponse() { /* * xhr.readyState * * Value Constant (W3C) Description * ------------------------------------------------------------------- * 0 UNSENT open() has not been called yet. * 1 OPENED send() has not been called yet. * 2 HEADERS_RECEIVED send() has been called, and headers * and status are available. * 3 LOADING Downloading; responseText holds partial data. * 4 DONE The operation is complete. */ if (xhr.readyState !== 4 && xhr.readyState !== 3) { return; } // the server returned error // try ... catch block is to work around bug in IE8 try { if (xhr.readyState === 3 && xhr.status !== 200) { return; } } catch (e) { return; } if (xhr.readyState === 4 && xhr.status !== 200) { handleError(xhr); return; } // In konqueror xhr.responseText is sometimes null here... if (xhr.responseText === null) { return; } // in case we were called before finished processing if (inProgress) { return; } else { inProgress = true; } // extract new whole (complete) lines, and process them while (xhr.prevDataLength !== xhr.responseText.length) { if (xhr.readyState === 4 && xhr.prevDataLength === xhr.responseText.length) { break; } xhr.prevDataLength = xhr.responseText.length; var unprocessed = xhr.responseText.substring(xhr.nextReadPos); xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos); } // end while // did we finish work? if (xhr.readyState === 4 && xhr.prevDataLength === xhr.responseText.length) { responseLoaded(xhr); } inProgress = false; } // ============================================================ // ------------------------------------------------------------ /** * Incrementally update line data in blame_incremental view in gitweb. * * @param {String} blamedataUrl: URL to server script generating blame data. * @param {String} bUrl: partial URL to project, used to generate links. * * Called from 'blame_incremental' view after loading table with * file contents, a base for blame view. * * @globals xhr, t0, projectUrl, div_progress_bar, totalLines, pollTimer */ function startBlame(blamedataUrl, bUrl) { xhr = createRequestObject(); if (!xhr) { errorInfo('ERROR: XMLHttpRequest not supported'); return; } t0 = new Date(); projectUrl = bUrl + (bUrl.indexOf('?') === -1 ? '?' : ';'); if ((div_progress_bar = document.getElementById('progress_bar'))) { //div_progress_bar.setAttribute('style', 'width: 100%;'); div_progress_bar.style.cssText = 'width: 100%;'; } totalLines = countLines(); updateProgressInfo(); /* add extra properties to xhr object to help processing response */ xhr.prevDataLength = -1; // used to detect if we have new data xhr.nextReadPos = 0; // where unread part of response starts xhr.onreadystatechange = handleResponse; //xhr.onreadystatechange = function () { handleResponse(xhr); }; xhr.open('GET', blamedataUrl); xhr.setRequestHeader('Accept', 'text/plain'); xhr.send(null); // not all browsers call onreadystatechange event on each server flush // poll response using timer every second to handle this issue pollTimer = setInterval(xhr.onreadystatechange, 1000); } // end of gitweb.js 07070100055ee8000081a40000000000000000000000014c5c5b3a000000cf0000010000010036ffffffffffffffff0000003000000000root/usr/local/share/gitweb/static/git-logo.pngPNG  IHDRH)9,PLTE``] GrIDATx CwKK4tC!EɦI$-4 VNTC0mFB)yV6׊PhS'jjiWqD_H"F'<pG SIENDB`07070100055ee7000081a40000000000000000000000014c5c5b3a000000730000010000010036ffffffffffffffff0000003300000000root/usr/local/share/gitweb/static/git-favicon.pngPNG  IHDRb PLTE|%IDATcX ؈&[2+ajjdsQ#[ovIENDB`07070100055ed4000041ed0000000000000000000000034c5c5c83000000000000010000010036ffffffffffffffff0000001e00000000root/usr/local/share/git-core07070100055ed5000041ed0000000000000000000000044c5c5c83000000000000010000010036ffffffffffffffff0000002800000000root/usr/local/share/git-core/templates07070100055ed6000081a40000000000000000000000014c5c5b20000000490000010000010036ffffffffffffffff0000003400000000root/usr/local/share/git-core/templates/descriptionUnnamed repository; edit this file 'description' to name the repository. 07070100055ed7000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000002e00000000root/usr/local/share/git-core/templates/hooks07070100055edf000081ed0000000000000000000000014c5c5b20000013590000010000010036ffffffffffffffff0000004000000000root/usr/local/share/git-core/templates/hooks/pre-rebase.sample#!/bin/bash # # Copyright (c) 2006, 2008 Junio C Hamano # # The "pre-rebase" hook is run just before "git rebase" starts doing # its job, and can prevent the command from running by exiting with # non-zero status. # # The hook is called with the following parameters: # # $1 -- the upstream the series was forked from. # $2 -- the branch being rebased (or empty when rebasing the current branch). # # This sample shows how to prevent topic branches that are already # merged to 'next' branch from getting rebased, because allowing it # would result in rebasing already published history. publish=next basebranch="$1" if test "$#" = 2 then topic="refs/heads/$2" else topic=`git symbolic-ref HEAD` || exit 0 ;# we do not interrupt rebasing detached HEAD fi case "$topic" in refs/heads/??/*) ;; *) exit 0 ;# we do not interrupt others. ;; esac # Now we are dealing with a topic branch being rebased # on top of master. Is it OK to rebase it? # Does the topic really exist? git show-ref -q "$topic" || { echo >&2 "No such branch $topic" exit 1 } # Is topic fully merged to master? not_in_master=`git rev-list --pretty=oneline ^master "$topic"` if test -z "$not_in_master" then echo >&2 "$topic is fully merged to master; better remove it." exit 1 ;# we could allow it, but there is no point. fi # Is topic ever merged to next? If so you should not be rebasing it. only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` only_next_2=`git rev-list ^master ${publish} | sort` if test "$only_next_1" = "$only_next_2" then not_in_topic=`git rev-list "^$topic" master` if test -z "$not_in_topic" then echo >&2 "$topic is already up-to-date with master" exit 1 ;# we could allow it, but there is no point. else exit 0 fi else not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` /usr/bin/perl -e ' my $topic = $ARGV[0]; my $msg = "* $topic has commits already merged to public branch:\n"; my (%not_in_next) = map { /^([0-9a-f]+) /; ($1 => 1); } split(/\n/, $ARGV[1]); for my $elem (map { /^([0-9a-f]+) (.*)$/; [$1 => $2]; } split(/\n/, $ARGV[2])) { if (!exists $not_in_next{$elem->[0]}) { if ($msg) { print STDERR $msg; undef $msg; } print STDERR " $elem->[1]\n"; } } ' "$topic" "$not_in_next" "$not_in_master" exit 1 fi exit 0 ################################################################ This sample hook safeguards topic branches that have been published from being rewound. The workflow assumed here is: * Once a topic branch forks from "master", "master" is never merged into it again (either directly or indirectly). * Once a topic branch is fully cooked and merged into "master", it is deleted. If you need to build on top of it to correct earlier mistakes, a new topic branch is created by forking at the tip of the "master". This is not strictly necessary, but it makes it easier to keep your history simple. * Whenever you need to test or publish your changes to topic branches, merge them into "next" branch. The script, being an example, hardcodes the publish branch name to be "next", but it is trivial to make it configurable via $GIT_DIR/config mechanism. With this workflow, you would want to know: (1) ... if a topic branch has ever been merged to "next". Young topic branches can have stupid mistakes you would rather clean up before publishing, and things that have not been merged into other branches can be easily rebased without affecting other people. But once it is published, you would not want to rewind it. (2) ... if a topic branch has been fully merged to "master". Then you can delete it. More importantly, you should not build on top of it -- other people may already want to change things related to the topic as patches against your "master", so if you need further changes, it is better to fork the topic (perhaps with the same name) afresh from the tip of "master". Let's look at this example: o---o---o---o---o---o---o---o---o---o "next" / / / / / a---a---b A / / / / / / / / c---c---c---c B / / / / \ / / / / b---b C \ / / / / / \ / ---o---o---o---o---o---o---o---o---o---o---o "master" A, B and C are topic branches. * A has one fix since it was merged up to "next". * B has finished. It has been fully merged up to "master" and "next", and is ready to be deleted. * C has not merged to "next" at all. We would want to allow C to be rebased, refuse A, and encourage B to be deleted. To compute (1): git rev-list ^master ^topic next git rev-list ^master next if these match, topic has not merged in next at all. To compute (2): git rev-list master..topic if this is empty, it is fully merged to "master". 07070100055ed9000081ed0000000000000000000000014c5c5b1f000003820000010000010036ffffffffffffffff0000004000000000root/usr/local/share/git-core/templates/hooks/commit-msg.sample#!/bin/bash # # An example hook script to check the commit log message. # Called by "git commit" with one argument, the name of the file # that has the commit message. The hook should exit with non-zero # status after issuing an appropriate message if it wants to stop the # commit. The hook is allowed to edit the commit message file. # # To enable this hook, rename this file to "commit-msg". # Uncomment the below to add a Signed-off-by line to the message. # Doing this in a hook is a bad idea in general, but the prepare-commit-msg # hook is more suited to it. # # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" # This example catches duplicate Signed-off-by lines. test "" = "$(grep '^Signed-off-by: ' "$1" | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { echo >&2 Duplicate Signed-off-by lines. exit 1 } 07070100055ee1000081ed0000000000000000000000014c5c5b2000000e1d0000010000010036ffffffffffffffff0000003c00000000root/usr/local/share/git-core/templates/hooks/update.sample#!/bin/bash # # An example hook script to blocks unannotated tags from entering. # Called by "git receive-pack" with arguments: refname sha1-old sha1-new # # To enable this hook, rename this file to "update". # # Config # ------ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. # hooks.allowdeletetag # This boolean sets whether deleting tags will be allowed in the # repository. By default they won't be. # hooks.allowmodifytag # This boolean sets whether a tag may be modified after creation. By default # it won't be. # hooks.allowdeletebranch # This boolean sets whether deleting branches will be allowed in the # repository. By default they won't be. # hooks.denycreatebranch # This boolean sets whether remotely creating branches will be denied # in the repository. By default this is allowed. # # --- Command line refname="$1" oldrev="$2" newrev="$3" # --- Safety check if [ -z "$GIT_DIR" ]; then echo "Don't run this script from the command line." >&2 echo " (if you want, you could supply GIT_DIR then run" >&2 echo " $0 )" >&2 exit 1 fi if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then echo "Usage: $0 " >&2 exit 1 fi # --- Config allowunannotated=$(git config --bool hooks.allowunannotated) allowdeletebranch=$(git config --bool hooks.allowdeletebranch) denycreatebranch=$(git config --bool hooks.denycreatebranch) allowdeletetag=$(git config --bool hooks.allowdeletetag) allowmodifytag=$(git config --bool hooks.allowmodifytag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") case "$projectdesc" in "Unnamed repository"* | "") echo "*** Project description file hasn't been set" >&2 exit 1 ;; esac # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. zero="0000000000000000000000000000000000000000" if [ "$newrev" = "$zero" ]; then newrev_type=delete else newrev_type=$(git cat-file -t $newrev) fi case "$refname","$newrev_type" in refs/tags/*,commit) # un-annotated tag short_refname=${refname##refs/tags/} if [ "$allowunannotated" != "true" ]; then echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 exit 1 fi ;; refs/tags/*,delete) # delete tag if [ "$allowdeletetag" != "true" ]; then echo "*** Deleting a tag is not allowed in this repository" >&2 exit 1 fi ;; refs/tags/*,tag) # annotated tag if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 then echo "*** Tag '$refname' already exists." >&2 echo "*** Modifying a tag is not allowed in this repository." >&2 exit 1 fi ;; refs/heads/*,commit) # branch if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then echo "*** Creating a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/heads/*,delete) # delete branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/remotes/*,commit) # tracking branch ;; refs/remotes/*,delete) # delete tracking branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a tracking branch is not allowed in this repository" >&2 exit 1 fi ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 exit 1 ;; esac # --- Finished exit 0 07070100055edb000081ed0000000000000000000000014c5c5b1f0000022a0000010000010036ffffffffffffffff0000004200000000root/usr/local/share/git-core/templates/hooks/post-receive.sample#!/bin/bash # # An example hook script for the "post-receive" event. # # The "post-receive" script is run after receive-pack has accepted a pack # and the repository has been updated. It is passed arguments in through # stdin in the form # # For example: # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master # # see contrib/hooks/ for a sample, or uncomment the next line and # rename the file to "post-receive". #. /usr/share/doc/git-core/contrib/hooks/post-receive-email 07070100055edc000081ed0000000000000000000000014c5c5b1f000000bf0000010000010036ffffffffffffffff0000004100000000root/usr/local/share/git-core/templates/hooks/post-update.sample#!/bin/bash # # An example hook script to prepare a packed repository for use over # dumb transports. # # To enable this hook, rename this file to "post-update". exec git update-server-info 07070100055edd000081ed0000000000000000000000014c5c5b1f000001900000010000010036ffffffffffffffff0000004400000000root/usr/local/share/git-core/templates/hooks/pre-applypatch.sample#!/bin/bash # # An example hook script to verify what is about to be committed # by applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. # # To enable this hook, rename this file to "pre-applypatch". . git-sh-setup test -x "$GIT_DIR/hooks/pre-commit" && exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} : 07070100055ee0000081ed0000000000000000000000014c5c5b20000004d90000010000010036ffffffffffffffff0000004800000000root/usr/local/share/git-core/templates/hooks/prepare-commit-msg.sample#!/bin/bash # # An example hook script to prepare the commit log message. # Called by "git commit" with the name of the file that has the # commit message, followed by the description of the commit # message's source. The hook's purpose is to edit the commit # message file. If the hook fails with a non-zero status, # the commit is aborted. # # To enable this hook, rename this file to "prepare-commit-msg". # This hook includes three examples. The first comments out the # "Conflicts:" part of a merge commit. # # The second includes the output of "git diff --name-status -r" # into the message, just before the "git status" output. It is # commented because it doesn't cope with --amend or with squashed # commits. # # The third example adds a Signed-off-by line to the message, that can # still be edited. This is rarely a good idea. case "$2,$3" in merge,) /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; # ,|template,) # /usr/bin/perl -i.bak -pe ' # print "\n" . `git diff --cached --name-status -r` # if /^#/ && $first++ == 0' "$1" ;; *) ;; esac # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 07070100055ede000081ed0000000000000000000000014c5c5b200000062c0000010000010036ffffffffffffffff0000004000000000root/usr/local/share/git-core/templates/hooks/pre-commit.sample#!/bin/bash # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # If you want to allow non-ascii filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) # Cross platform projects tend to avoid non-ascii filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. test "$(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')" then echo "Error: Attempt to add a non-ascii file name." echo echo "This can cause problems if you want to work" echo "with people on other platforms." echo echo "To be portable it is advisable to rename the file ..." echo echo "If you know what you are doing you can disable this" echo "check using:" echo echo " git config hooks.allownonascii true" echo exit 1 fi exec git diff-index --check --cached $against -- 07070100055ed8000081ed0000000000000000000000014c5c5b1f000001c60000010000010036ffffffffffffffff0000004400000000root/usr/local/share/git-core/templates/hooks/applypatch-msg.sample#!/bin/bash # # An example hook script to check the commit log message taken by # applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. The hook is # allowed to edit the commit message file. # # To enable this hook, rename this file to "applypatch-msg". . git-sh-setup test -x "$GIT_DIR/hooks/commit-msg" && exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} : 07070100055eda000081ed0000000000000000000000014c5c5b1f000000a20000010000010036ffffffffffffffff0000004100000000root/usr/local/share/git-core/templates/hooks/post-commit.sample#!/bin/bash # # An example hook script that is called after a successful # commit is made. # # To enable this hook, rename this file to "post-commit". : Nothing 07070100055ee2000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000002d00000000root/usr/local/share/git-core/templates/info07070100055ee3000081a40000000000000000000000014c5c5b20000000f00000010000010036ffffffffffffffff0000003500000000root/usr/local/share/git-core/templates/info/exclude# git ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ 07070100055eeb000041ed0000000000000000000000034c5c5c83000000000000010000010036ffffffffffffffff0000001900000000root/usr/local/share/man07070100055eec000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000001e00000000root/usr/local/share/man/man307070100055eed000081a40000000000000000000000014c5c5b1f0000639b0000010000010036ffffffffffffffff0000002400000000root/usr/local/share/man/man3/Git.3.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Git 3" .TH Git 3 "2010-07-29" "perl v5.8.4" "User Contributed Perl Documentation" .SH "NAME" Git \- Perl interface to the Git version control system .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Git; .Ve .PP .Vb 1 \& my $version = Git::command_oneline('version'); .Ve .PP .Vb 2 \& git_cmd_try { Git::command_noisy('update-server-info') } \& '%s failed w/ code %d'; .Ve .PP .Vb 1 \& my $repo = Git->repository (Directory => '/srv/git/cogito.git'); .Ve .PP .Vb 1 \& my @revs = $repo->command('rev-list', '--since=last monday', '--all'); .Ve .PP .Vb 3 \& my ($fh, $c) = $repo->command_output_pipe('rev-list', '--since=last monday', '--all'); \& my $lastrev = <$fh>; chomp $lastrev; \& $repo->command_close_pipe($fh, $c); .Ve .PP .Vb 2 \& my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ], \& STDERR => 0 ); .Ve .PP .Vb 3 \& my $sha1 = $repo->hash_and_insert_object('file.txt'); \& my $tempfile = tempfile(); \& my $size = $repo->cat_blob($sha1, $tempfile); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides Perl scripts easy way to interface the Git version control system. The modules have an easy and well-tested way to call arbitrary Git commands; in the future, the interface will also provide specialized methods for doing easily operations which are not totally trivial to do over the generic command interface. .PP While some commands can be executed outside of any context (e.g. 'version' or 'init'), most operations require a repository context, which in practice means getting an instance of the Git object using the \fIrepository()\fR constructor. (In the future, we will also get a \fInew_repository()\fR constructor.) All commands called as methods of the object are then executed in the context of the repository. .PP Part of the \*(L"repository state\*(R" is also information about path to the attached working copy (unless you work with a bare repository). You can also navigate inside of the working copy using the \f(CW\*(C`wc_chdir()\*(C'\fR method. (Note that the repository object is self-contained and will not change working directory of your process.) .PP \&\s-1TODO:\s0 In the future, we might also do .PP .Vb 3 \& my $remoterepo = $repo->remote_repository (Name => 'cogito', Branch => 'master'); \& $remoterepo ||= Git->remote_repository ('http://git.or.cz/cogito.git/'); \& my @refs = $remoterepo->refs(); .Ve .PP Currently, the module merely wraps calls to external Git tools. In the future, it will provide a much faster way to interact with Git by linking directly to libgit. This should be completely opaque to the user, though (performance increase notwithstanding). .SH "CONSTRUCTORS" .IX Header "CONSTRUCTORS" .IP "repository ( \s-1OPTIONS\s0 )" 4 .IX Item "repository ( OPTIONS )" .PD 0 .IP "repository ( \s-1DIRECTORY\s0 )" 4 .IX Item "repository ( DIRECTORY )" .IP "repository ()" 4 .IX Item "repository ()" .PD Construct a new repository object. \&\f(CW\*(C`OPTIONS\*(C'\fR are passed in a hash like fashion, using key and value pairs. Possible options are: .Sp \&\fBRepository\fR \- Path to the Git repository. .Sp \&\fBWorkingCopy\fR \- Path to the associated working copy; not strictly required as many commands will happily crunch on a bare repository. .Sp \&\fBWorkingSubdir\fR \- Subdirectory in the working copy to work inside. Just left undefined if you do not want to limit the scope of operations. .Sp \&\fBDirectory\fR \- Path to the Git working directory in its usual setup. The \f(CW\*(C`.git\*(C'\fR directory is searched in the directory and all the parent directories; if found, \f(CW\*(C`WorkingCopy\*(C'\fR is set to the directory containing it and \f(CW\*(C`Repository\*(C'\fR to the \f(CW\*(C`.git\*(C'\fR directory itself. If no \f(CW\*(C`.git\*(C'\fR directory was found, the \f(CW\*(C`Directory\*(C'\fR is assumed to be a bare repository, \&\f(CW\*(C`Repository\*(C'\fR is set to point at it and \f(CW\*(C`WorkingCopy\*(C'\fR is left undefined. If the \f(CW$GIT_DIR\fR environment variable is set, things behave as expected as well. .Sp You should not use both \f(CW\*(C`Directory\*(C'\fR and either of \f(CW\*(C`Repository\*(C'\fR and \&\f(CW\*(C`WorkingCopy\*(C'\fR \- the results of that are undefined. .Sp Alternatively, a directory path may be passed as a single scalar argument to the constructor; it is equivalent to setting only the \f(CW\*(C`Directory\*(C'\fR option field. .Sp Calling the constructor with no options whatsoever is equivalent to calling it with \f(CW\*(C`Directory => '.'\*(C'\fR. In general, if you are building a standard porcelain command, simply doing \f(CW\*(C`Git\->repository()\*(C'\fR should do the right thing and setup the object to reflect exactly where the user is right now. .SH "METHODS" .IX Header "METHODS" .IP "command ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command ( COMMAND [, ARGUMENTS... ] )" .PD 0 .IP "command ( [ \s-1COMMAND\s0, \s-1ARGUMENTS\s0... ], { Opt => Val ... } )" 4 .IX Item "command ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )" .PD Execute the given Git \f(CW\*(C`COMMAND\*(C'\fR (specify it without the 'git\-' prefix), optionally with the specified extra \f(CW\*(C`ARGUMENTS\*(C'\fR. .Sp The second more elaborate form can be used if you want to further adjust the command execution. Currently, only one option is supported: .Sp \&\fB\s-1STDERR\s0\fR \- How to deal with the command's error output. By default (\f(CW\*(C`undef\*(C'\fR) it is delivered to the caller's \f(CW\*(C`STDERR\*(C'\fR. A false value (0 or '') will cause it to be thrown away. If you want to process it, you can get it in a filehandle you specify, but you must be extremely careful; if the error output is not very short and you want to read it in the same process as where you called \&\f(CW\*(C`command()\*(C'\fR, you are set up for a nice deadlock! .Sp The method can be called without any instance or on a specified Git repository (in that case the command will be run in the repository context). .Sp In scalar context, it returns all the command output in a single string (verbatim). .Sp In array context, it returns an array containing lines printed to the command's stdout (without trailing newlines). .Sp In both cases, the command's stdin and stderr are the same as the caller's. .IP "command_oneline ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command_oneline ( COMMAND [, ARGUMENTS... ] )" .PD 0 .IP "command_oneline ( [ \s-1COMMAND\s0, \s-1ARGUMENTS\s0... ], { Opt => Val ... } )" 4 .IX Item "command_oneline ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )" .PD Execute the given \f(CW\*(C`COMMAND\*(C'\fR in the same way as \fIcommand()\fR does but always return a scalar string containing the first line of the command's standard output. .IP "command_output_pipe ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command_output_pipe ( COMMAND [, ARGUMENTS... ] )" .PD 0 .IP "command_output_pipe ( [ \s-1COMMAND\s0, \s-1ARGUMENTS\s0... ], { Opt => Val ... } )" 4 .IX Item "command_output_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )" .PD Execute the given \f(CW\*(C`COMMAND\*(C'\fR in the same way as \fIcommand()\fR does but return a pipe filehandle from which the command output can be read. .Sp The function can return \f(CW\*(C`($pipe, $ctx)\*(C'\fR in array context. See \f(CW\*(C`command_close_pipe()\*(C'\fR for details. .IP "command_input_pipe ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command_input_pipe ( COMMAND [, ARGUMENTS... ] )" .PD 0 .IP "command_input_pipe ( [ \s-1COMMAND\s0, \s-1ARGUMENTS\s0... ], { Opt => Val ... } )" 4 .IX Item "command_input_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )" .PD Execute the given \f(CW\*(C`COMMAND\*(C'\fR in the same way as \fIcommand_output_pipe()\fR does but return an input pipe filehandle instead; the command output is not captured. .Sp The function can return \f(CW\*(C`($pipe, $ctx)\*(C'\fR in array context. See \f(CW\*(C`command_close_pipe()\*(C'\fR for details. .IP "command_close_pipe ( \s-1PIPE\s0 [, \s-1CTX\s0 ] )" 4 .IX Item "command_close_pipe ( PIPE [, CTX ] )" Close the \f(CW\*(C`PIPE\*(C'\fR as returned from \f(CW\*(C`command_*_pipe()\*(C'\fR, checking whether the command finished successfully. The optional \f(CW\*(C`CTX\*(C'\fR argument is required if you want to see the command name in the error message, and it is the second value returned by \f(CW\*(C`command_*_pipe()\*(C'\fR when called in array context. The call idiom is: .Sp .Vb 3 \& my ($fh, $ctx) = $r->command_output_pipe('status'); \& while (<$fh>) { ... } \& $r->command_close_pipe($fh, $ctx); .Ve .Sp Note that you should not rely on whatever actually is in \f(CW\*(C`CTX\*(C'\fR; currently it is simply the command name but in future the context might have more complicated structure. .IP "command_bidi_pipe ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command_bidi_pipe ( COMMAND [, ARGUMENTS... ] )" Execute the given \f(CW\*(C`COMMAND\*(C'\fR in the same way as \fIcommand_output_pipe()\fR does but return both an input pipe filehandle and an output pipe filehandle. .Sp The function will return return \f(CW\*(C`($pid, $pipe_in, $pipe_out, $ctx)\*(C'\fR. See \f(CW\*(C`command_close_bidi_pipe()\*(C'\fR for details. .IP "command_close_bidi_pipe ( \s-1PID\s0, \s-1PIPE_IN\s0, \s-1PIPE_OUT\s0 [, \s-1CTX\s0] )" 4 .IX Item "command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] )" Close the \f(CW\*(C`PIPE_IN\*(C'\fR and \f(CW\*(C`PIPE_OUT\*(C'\fR as returned from \f(CW\*(C`command_bidi_pipe()\*(C'\fR, checking whether the command finished successfully. The optional \f(CW\*(C`CTX\*(C'\fR argument is required if you want to see the command name in the error message, and it is the fourth value returned by \f(CW\*(C`command_bidi_pipe()\*(C'\fR. The call idiom is: .Sp .Vb 4 \& my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check'); \& print "000000000\en" $out; \& while (<$in>) { ... } \& $r->command_close_bidi_pipe($pid, $in, $out, $ctx); .Ve .Sp Note that you should not rely on whatever actually is in \f(CW\*(C`CTX\*(C'\fR; currently it is simply the command name but in future the context might have more complicated structure. .IP "command_noisy ( \s-1COMMAND\s0 [, \s-1ARGUMENTS\s0... ] )" 4 .IX Item "command_noisy ( COMMAND [, ARGUMENTS... ] )" Execute the given \f(CW\*(C`COMMAND\*(C'\fR in the same way as \fIcommand()\fR does but do not capture the command output \- the standard output is not redirected and goes to the standard output of the caller application. .Sp While the method is called \fIcommand_noisy()\fR, you might want to as well use it for the most silent Git commands which you know will never pollute your stdout but you want to avoid the overhead of the pipe setup when calling them. .Sp The function returns only after the command has finished running. .IP "version ()" 4 .IX Item "version ()" Return the Git version in use. .IP "exec_path ()" 4 .IX Item "exec_path ()" Return path to the Git sub-command executables (the same as \&\f(CW\*(C`git \-\-exec\-path\*(C'\fR). Useful mostly only internally. .IP "html_path ()" 4 .IX Item "html_path ()" Return path to the Git html documentation (the same as \&\f(CW\*(C`git \-\-html\-path\*(C'\fR). Useful mostly only internally. .IP "repo_path ()" 4 .IX Item "repo_path ()" Return path to the git repository. Must be called on a repository instance. .IP "wc_path ()" 4 .IX Item "wc_path ()" Return path to the working copy. Must be called on a repository instance. .IP "wc_subdir ()" 4 .IX Item "wc_subdir ()" Return path to the subdirectory inside of a working copy. Must be called on a repository instance. .IP "wc_chdir ( \s-1SUBDIR\s0 )" 4 .IX Item "wc_chdir ( SUBDIR )" Change the working copy subdirectory to work within. The \f(CW\*(C`SUBDIR\*(C'\fR is relative to the working copy root directory (not the current subdirectory). Must be called on a repository instance attached to a working copy and the directory must exist. .IP "config ( \s-1VARIABLE\s0 )" 4 .IX Item "config ( VARIABLE )" Retrieve the configuration \f(CW\*(C`VARIABLE\*(C'\fR in the same manner as \f(CW\*(C`config\*(C'\fR does. In scalar context requires the variable to be set only one time (exception is thrown otherwise), in array context returns allows the variable to be set multiple times and returns all the values. .Sp This currently wraps command('config') so it is not so fast. .IP "config_bool ( \s-1VARIABLE\s0 )" 4 .IX Item "config_bool ( VARIABLE )" Retrieve the bool configuration \f(CW\*(C`VARIABLE\*(C'\fR. The return value is usable as a boolean in perl (and \f(CW\*(C`undef\*(C'\fR if it's not defined, of course). .Sp This currently wraps command('config') so it is not so fast. .IP "config_int ( \s-1VARIABLE\s0 )" 4 .IX Item "config_int ( VARIABLE )" Retrieve the integer configuration \f(CW\*(C`VARIABLE\*(C'\fR. The return value is simple decimal number. An optional value suffix of 'k', 'm', or 'g' in the config file will cause the value to be multiplied by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. It would return \f(CW\*(C`undef\*(C'\fR if configuration variable is not defined, .Sp This currently wraps command('config') so it is not so fast. .IP "get_colorbool ( \s-1NAME\s0 )" 4 .IX Item "get_colorbool ( NAME )" Finds if color should be used for NAMEd operation from the configuration, and returns boolean (true for \*(L"use color\*(R", false for \*(L"do not use color\*(R"). .IP "get_color ( \s-1SLOT\s0, \s-1COLOR\s0 )" 4 .IX Item "get_color ( SLOT, COLOR )" Finds color for \s-1SLOT\s0 from the configuration, while defaulting to \s-1COLOR\s0, and returns the \s-1ANSI\s0 color escape sequence: .Sp .Vb 3 \& print $repo->get_color("color.interactive.prompt", "underline blue white"); \& print "some text"; \& print $repo->get_color("", "normal"); .Ve .IP "remote_refs ( \s-1REPOSITORY\s0 [, \s-1GROUPS\s0 [, \s-1REFGLOBS\s0 ] ] )" 4 .IX Item "remote_refs ( REPOSITORY [, GROUPS [, REFGLOBS ] ] )" This function returns a hashref of refs stored in a given remote repository. The hash is in the format \f(CW\*(C`refname =\e\*(C'\fR hash>. For tags, the \f(CW\*(C`refname\*(C'\fR entry contains the tag object while a \f(CW\*(C`refname^{}\*(C'\fR entry gives the tagged objects. .Sp \&\f(CW\*(C`REPOSITORY\*(C'\fR has the same meaning as the appropriate \f(CW\*(C`git\-ls\-remote\*(C'\fR argument; either an \s-1URL\s0 or a remote name (if called on a repository instance). \&\f(CW\*(C`GROUPS\*(C'\fR is an optional arrayref that can contain 'tags' to return all the tags and/or 'heads' to return all the heads. \f(CW\*(C`REFGLOB\*(C'\fR is an optional array of strings containing a shell-like glob to further limit the refs returned in the hash; the meaning is again the same as the appropriate \f(CW\*(C`git\-ls\-remote\*(C'\fR argument. .Sp This function may or may not be called on a repository instance. In the former case, remote names as defined in the repository are recognized as repository specifiers. .IP "ident ( \s-1TYPE\s0 | \s-1IDENTSTR\s0 )" 4 .IX Item "ident ( TYPE | IDENTSTR )" .PD 0 .IP "ident_person ( \s-1TYPE\s0 | \s-1IDENTSTR\s0 | \s-1IDENTARRAY\s0 )" 4 .IX Item "ident_person ( TYPE | IDENTSTR | IDENTARRAY )" .PD This suite of functions retrieves and parses ident information, as stored in the commit and tag objects or produced by \f(CW\*(C`var GIT_type_IDENT\*(C'\fR (thus \&\f(CW\*(C`TYPE\*(C'\fR can be either \fIauthor\fR or \fIcommitter\fR; case is insignificant). .Sp The \f(CW\*(C`ident\*(C'\fR method retrieves the ident information from \f(CW\*(C`git var\*(C'\fR and either returns it as a scalar string or as an array with the fields parsed. Alternatively, it can take a prepared ident string (e.g. from the commit object) and just parse it. .Sp \&\f(CW\*(C`ident_person\*(C'\fR returns the person part of the ident \- name and email; it can take the same arguments as \f(CW\*(C`ident\*(C'\fR or the array returned by \f(CW\*(C`ident\*(C'\fR. .Sp The synopsis is like: .Sp .Vb 4 \& my ($name, $email, $time_tz) = ident('author'); \& "$name <$email>" eq ident_person('author'); \& "$name <$email>" eq ident_person($name); \& $time_tz =~ /^\ed+ [+-]\ed{4}$/; .Ve .IP "hash_object ( \s-1TYPE\s0, \s-1FILENAME\s0 )" 4 .IX Item "hash_object ( TYPE, FILENAME )" Compute the \s-1SHA1\s0 object id of the given \f(CW\*(C`FILENAME\*(C'\fR considering it is of the \f(CW\*(C`TYPE\*(C'\fR object type (\f(CW\*(C`blob\*(C'\fR, \f(CW\*(C`commit\*(C'\fR, \f(CW\*(C`tree\*(C'\fR). .Sp The method can be called without any instance or on a specified Git repository, it makes zero difference. .Sp The function returns the \s-1SHA1\s0 hash. .IP "hash_and_insert_object ( \s-1FILENAME\s0 )" 4 .IX Item "hash_and_insert_object ( FILENAME )" Compute the \s-1SHA1\s0 object id of the given \f(CW\*(C`FILENAME\*(C'\fR and add the object to the object database. .Sp The function returns the \s-1SHA1\s0 hash. .IP "cat_blob ( \s-1SHA1\s0, \s-1FILEHANDLE\s0 )" 4 .IX Item "cat_blob ( SHA1, FILEHANDLE )" Prints the contents of the blob identified by \f(CW\*(C`SHA1\*(C'\fR to \f(CW\*(C`FILEHANDLE\*(C'\fR and returns the number of bytes printed. .IP "temp_acquire ( \s-1NAME\s0 )" 4 .IX Item "temp_acquire ( NAME )" Attempts to retreive the temporary file mapped to the string \f(CW\*(C`NAME\*(C'\fR. If an associated temp file has not been created this session or was closed, it is created, cached, and set for autoflush and binmode. .Sp Internally locks the file mapped to \f(CW\*(C`NAME\*(C'\fR. This lock must be released with \&\f(CW\*(C`temp_release()\*(C'\fR when the temp file is no longer needed. Subsequent attempts to retrieve temporary files mapped to the same \f(CW\*(C`NAME\*(C'\fR while still locked will cause an error. This locking mechanism provides a weak guarantee and is not threadsafe. It does provide some error checking to help prevent temp file refs writing over one another. .Sp In general, the File::Handle returned should not be closed by consumers as it defeats the purpose of this caching mechanism. If you need to close the temp file handle, then you should use File::Temp or another temp file faculty directly. If a handle is closed and then requested again, then a warning will issue. .IP "temp_release ( \s-1NAME\s0 )" 4 .IX Item "temp_release ( NAME )" .PD 0 .IP "temp_release ( \s-1FILEHANDLE\s0 )" 4 .IX Item "temp_release ( FILEHANDLE )" .PD Releases a lock acquired through \f(CW\*(C`temp_acquire()\*(C'\fR. Can be called either with the \f(CW\*(C`NAME\*(C'\fR mapping used when acquiring the temp file or with the \f(CW\*(C`FILEHANDLE\*(C'\fR referencing a locked temp file. .Sp Warns if an attempt is made to release a file that is not locked. .Sp The temp file will be truncated before being released. This can help to reduce disk I/O where the system is smart enough to detect the truncation while data is in the output buffers. Beware that after the temp file is released and truncated, any operations on that file may fail miserably until it is re\-acquired. All contents are lost between each release and acquire mapped to the same string. .IP "temp_reset ( \s-1FILEHANDLE\s0 )" 4 .IX Item "temp_reset ( FILEHANDLE )" Truncates and resets the position of the \f(CW\*(C`FILEHANDLE\*(C'\fR. .IP "temp_path ( \s-1NAME\s0 )" 4 .IX Item "temp_path ( NAME )" .PD 0 .IP "temp_path ( \s-1FILEHANDLE\s0 )" 4 .IX Item "temp_path ( FILEHANDLE )" .PD Returns the filename associated with the given tempfile. .SH "ERROR HANDLING" .IX Header "ERROR HANDLING" All functions are supposed to throw Perl exceptions in case of errors. See the Error module on how to catch those. Most exceptions are mere Error::Simple instances. .PP However, the \f(CW\*(C`command()\*(C'\fR, \f(CW\*(C`command_oneline()\*(C'\fR and \f(CW\*(C`command_noisy()\*(C'\fR functions suite can throw \f(CW\*(C`Git::Error::Command\*(C'\fR exceptions as well: those are thrown when the external command returns an error code and contain the error code as well as access to the captured command's output. The exception class provides the usual \f(CW\*(C`stringify\*(C'\fR and \f(CW\*(C`value\*(C'\fR (command's exit code) methods and in addition also a \f(CW\*(C`cmd_output\*(C'\fR method that returns either an array or a string with the captured command output (depending on the original function call context; \f(CW\*(C`command_noisy()\*(C'\fR returns \f(CW\*(C`undef\*(C'\fR) and $ which returns the command and its arguments (but without proper quoting). .PP Note that the \f(CW\*(C`command_*_pipe()\*(C'\fR functions cannot throw this exception since it has no idea whether the command failed or not. You will only find out at the time you \f(CW\*(C`close\*(C'\fR the pipe; if you want to have that automated, use \f(CW\*(C`command_close_pipe()\*(C'\fR, which can throw the exception. .IP "git_cmd_try { \s-1CODE\s0 } \s-1ERRMSG\s0" 4 .IX Item "git_cmd_try { CODE } ERRMSG" This magical statement will automatically catch any \f(CW\*(C`Git::Error::Command\*(C'\fR exceptions thrown by \f(CW\*(C`CODE\*(C'\fR and make your program die with \f(CW\*(C`ERRMSG\*(C'\fR on its lips; the message will have \f(CW%s\fR substituted for the command line and \f(CW%d\fR for the exit status. This statement is useful mostly for producing more user-friendly error messages. .Sp In case of no exception caught the statement returns \f(CW\*(C`CODE\*(C'\fR's return value. .Sp Note that this is the only auto-exported function. .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright 2006 by Petr Baudis . .PP This module is free software; it may be used, copied, modified and distributed under the terms of the \s-1GNU\s0 General Public Licence, either version 2, or (at your option) any later version. 07070100055eee000081a40000000000000000000000014c5c5b1f000038480000010000010036ffffffffffffffff0000002e00000000root/usr/local/share/man/man3/private-Error.3.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "private-Error 3" .TH private-Error 3 "2010-07-29" "perl v5.8.4" "User Contributed Perl Documentation" .SH "NAME" Error \- Error/exception handling in an OO\-ish way .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Error qw(:try); .Ve .PP .Vb 1 \& throw Error::Simple( "A simple error"); .Ve .PP .Vb 5 \& sub xyz { \& ... \& record Error::Simple("A simple error") \& and return; \& } .Ve .PP .Vb 1 \& unlink($file) or throw Error::Simple("$file: $!",$!); .Ve .PP .Vb 23 \& try { \& do_some_stuff(); \& die "error!" if $condition; \& throw Error::Simple -text => "Oops!" if $other_condition; \& } \& catch Error::IO with { \& my $E = shift; \& print STDERR "File ", $E->{'-file'}, " had a problem\en"; \& } \& except { \& my $E = shift; \& my $general_handler=sub {send_message $E->{-description}}; \& return { \& UserException1 => $general_handler, \& UserException2 => $general_handler \& }; \& } \& otherwise { \& print STDERR "Well I don't know what to say\en"; \& } \& finally { \& close_the_garage_door_already(); # Should be reliable \& }; # Don't forget the trailing ; or you might be surprised .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \f(CW\*(C`Error\*(C'\fR package provides two interfaces. Firstly \f(CW\*(C`Error\*(C'\fR provides a procedural interface to exception handling. Secondly \f(CW\*(C`Error\*(C'\fR is a base class for errors/exceptions that can either be thrown, for subsequent catch, or can simply be recorded. .PP Errors in the class \f(CW\*(C`Error\*(C'\fR should not be thrown directly, but the user should throw errors from a sub-class of \f(CW\*(C`Error\*(C'\fR. .SH "PROCEDURAL INTERFACE" .IX Header "PROCEDURAL INTERFACE" \&\f(CW\*(C`Error\*(C'\fR exports subroutines to perform exception handling. These will be exported if the \f(CW\*(C`:try\*(C'\fR tag is used in the \f(CW\*(C`use\*(C'\fR line. .IP "try \s-1BLOCK\s0 \s-1CLAUSES\s0" 4 .IX Item "try BLOCK CLAUSES" \&\f(CW\*(C`try\*(C'\fR is the main subroutine called by the user. All other subroutines exported are clauses to the try subroutine. .Sp The \s-1BLOCK\s0 will be evaluated and, if no error is throw, try will return the result of the block. .Sp \&\f(CW\*(C`CLAUSES\*(C'\fR are the subroutines below, which describe what to do in the event of an error being thrown within \s-1BLOCK\s0. .IP "catch \s-1CLASS\s0 with \s-1BLOCK\s0" 4 .IX Item "catch CLASS with BLOCK" This clauses will cause all errors that satisfy \f(CW\*(C`$err\->isa(CLASS)\*(C'\fR to be caught and handled by evaluating \f(CW\*(C`BLOCK\*(C'\fR. .Sp \&\f(CW\*(C`BLOCK\*(C'\fR will be passed two arguments. The first will be the error being thrown. The second is a reference to a scalar variable. If this variable is set by the catch block then, on return from the catch block, try will continue processing as if the catch block was never found. .Sp To propagate the error the catch block may call \f(CW\*(C`$err\->throw\*(C'\fR .Sp If the scalar reference by the second argument is not set, and the error is not thrown. Then the current try block will return with the result from the catch block. .IP "except \s-1BLOCK\s0" 4 .IX Item "except BLOCK" When \f(CW\*(C`try\*(C'\fR is looking for a handler, if an except clause is found \&\f(CW\*(C`BLOCK\*(C'\fR is evaluated. The return value from this block should be a \&\s-1HASHREF\s0 or a list of key-value pairs, where the keys are class names and the values are \s-1CODE\s0 references for the handler of errors of that type. .IP "otherwise \s-1BLOCK\s0" 4 .IX Item "otherwise BLOCK" Catch any error by executing the code in \f(CW\*(C`BLOCK\*(C'\fR .Sp When evaluated \f(CW\*(C`BLOCK\*(C'\fR will be passed one argument, which will be the error being processed. .Sp Only one otherwise block may be specified per try block .IP "finally \s-1BLOCK\s0" 4 .IX Item "finally BLOCK" Execute the code in \f(CW\*(C`BLOCK\*(C'\fR either after the code in the try block has successfully completed, or if the try block throws an error then \&\f(CW\*(C`BLOCK\*(C'\fR will be executed after the handler has completed. .Sp If the handler throws an error then the error will be caught, the finally block will be executed and the error will be re\-thrown. .Sp Only one finally block may be specified per try block .SH "CLASS INTERFACE" .IX Header "CLASS INTERFACE" .Sh "\s-1CONSTRUCTORS\s0" .IX Subsection "CONSTRUCTORS" The \f(CW\*(C`Error\*(C'\fR object is implemented as a \s-1HASH\s0. This \s-1HASH\s0 is initialized with the arguments that are passed to it's constructor. The elements that are used by, or are retrievable by the \f(CW\*(C`Error\*(C'\fR class are listed below, other classes may add to these. .PP .Vb 5 \& -file \& -line \& -text \& -value \& -object .Ve .PP If \f(CW\*(C`\-file\*(C'\fR or \f(CW\*(C`\-line\*(C'\fR are not specified in the constructor arguments then these will be initialized with the file name and line number where the constructor was called from. .PP If the error is associated with an object then the object should be passed as the \f(CW\*(C`\-object\*(C'\fR argument. This will allow the \f(CW\*(C`Error\*(C'\fR package to associate the error with the object. .PP The \f(CW\*(C`Error\*(C'\fR package remembers the last error created, and also the last error associated with a package. This could either be the last error created by a sub in that package, or the last error which passed an object blessed into that package as the \f(CW\*(C`\-object\*(C'\fR argument. .IP "throw ( [ \s-1ARGS\s0 ] )" 4 .IX Item "throw ( [ ARGS ] )" Create a new \f(CW\*(C`Error\*(C'\fR object and throw an error, which will be caught by a surrounding \f(CW\*(C`try\*(C'\fR block, if there is one. Otherwise it will cause the program to exit. .Sp \&\f(CW\*(C`throw\*(C'\fR may also be called on an existing error to re-throw it. .IP "with ( [ \s-1ARGS\s0 ] )" 4 .IX Item "with ( [ ARGS ] )" Create a new \f(CW\*(C`Error\*(C'\fR object and returns it. This is defined for syntactic sugar, eg .Sp .Vb 1 \& die with Some::Error ( ... ); .Ve .IP "record ( [ \s-1ARGS\s0 ] )" 4 .IX Item "record ( [ ARGS ] )" Create a new \f(CW\*(C`Error\*(C'\fR object and returns it. This is defined for syntactic sugar, eg .Sp .Vb 2 \& record Some::Error ( ... ) \& and return; .Ve .Sh "\s-1STATIC\s0 \s-1METHODS\s0" .IX Subsection "STATIC METHODS" .IP "prior ( [ \s-1PACKAGE\s0 ] )" 4 .IX Item "prior ( [ PACKAGE ] )" Return the last error created, or the last error associated with \&\f(CW\*(C`PACKAGE\*(C'\fR .IP "flush ( [ \s-1PACKAGE\s0 ] )" 4 .IX Item "flush ( [ PACKAGE ] )" Flush the last error created, or the last error associated with \&\f(CW\*(C`PACKAGE\*(C'\fR.It is necessary to clear the error stack before exiting the package or uncaught errors generated using \f(CW\*(C`record\*(C'\fR will be reported. .Sp .Vb 1 \& $Error->flush; .Ve .Sh "\s-1OBJECT\s0 \s-1METHODS\s0" .IX Subsection "OBJECT METHODS" .IP "stacktrace" 4 .IX Item "stacktrace" If the variable \f(CW$Error::Debug\fR was non-zero when the error was created, then \f(CW\*(C`stacktrace\*(C'\fR returns a string created by calling \&\f(CW\*(C`Carp::longmess\*(C'\fR. If the variable was zero the \f(CW\*(C`stacktrace\*(C'\fR returns the text of the error appended with the filename and line number of where the error was created, providing the text does not end with a newline. .IP "object" 4 .IX Item "object" The object this error was associated with .IP "file" 4 .IX Item "file" The file where the constructor of this error was called from .IP "line" 4 .IX Item "line" The line where the constructor of this error was called from .IP "text" 4 .IX Item "text" The text of the error .Sh "\s-1OVERLOAD\s0 \s-1METHODS\s0" .IX Subsection "OVERLOAD METHODS" .IP "stringify" 4 .IX Item "stringify" A method that converts the object into a string. This method may simply return the same as the \f(CW\*(C`text\*(C'\fR method, or it may append more information. For example the file name and line number. .Sp By default this method returns the \f(CW\*(C`\-text\*(C'\fR argument that was passed to the constructor, or the string \f(CW"Died"\fR if none was given. .IP "value" 4 .IX Item "value" A method that will return a value that can be associated with the error. For example if an error was created due to the failure of a system call, then this may return the numeric value of \f(CW$!\fR at the time. .Sp By default this method returns the \f(CW\*(C`\-value\*(C'\fR argument that was passed to the constructor. .SH "PRE-DEFINED ERROR CLASSES" .IX Header "PRE-DEFINED ERROR CLASSES" .IP "Error::Simple" 4 .IX Item "Error::Simple" This class can be used to hold simple error strings and values. It's constructor takes two arguments. The first is a text value, the second is a numeric value. These values are what will be returned by the overload methods. .Sp If the text value ends with \f(CW\*(C`at file line 1\*(C'\fR as $@ strings do, then this infomation will be used to set the \f(CW\*(C`\-file\*(C'\fR and \f(CW\*(C`\-line\*(C'\fR arguments of the error object. .Sp This class is used internally if an eval'd block die's with an error that is a plain string. (Unless \f(CW$Error::ObjectifyCallback\fR is modified) .SH "$Error::ObjectifyCallback" .IX Header "$Error::ObjectifyCallback" This variable holds a reference to a subroutine that converts errors that are plain strings to objects. It is used by Error.pm to convert textual errors to objects, and can be overridden by the user. .PP It accepts a single argument which is a hash reference to named parameters. Currently the only named parameter passed is \f(CW'text'\fR which is the text of the error, but others may be available in the future. .PP For example the following code will cause Error.pm to throw objects of the class MyError::Bar by default: .PP .Vb 7 \& sub throw_MyError_Bar \& { \& my $args = shift; \& my $err = MyError::Bar->new(); \& $err->{'MyBarText'} = $args->{'text'}; \& return $err; \& } .Ve .PP .Vb 2 \& { \& local $Error::ObjectifyCallback = \e&throw_MyError_Bar; .Ve .PP .Vb 2 \& # Error handling here. \& } .Ve .SH "KNOWN BUGS" .IX Header "KNOWN BUGS" None, but that does not mean there are not any. .SH "AUTHORS" .IX Header "AUTHORS" Graham Barr .PP The code that inspired me to write this was originally written by Peter Seibel and adapted by Jesse Glick . .SH "MAINTAINER" .IX Header "MAINTAINER" Shlomi Fish .SH "PAST MAINTAINERS" .IX Header "PAST MAINTAINERS" Arun Kumar U 07070100055e87000041ed0000000000000000000000054c5c5c81000000000000010000010036ffffffffffffffff0000001300000000root/usr/local/lib07070100055ea5000041ed0000000000000000000000024c5c5c81000000000000010000010036ffffffffffffffff0000002600000000root/usr/local/lib/sun4-solaris-64int07070100055ea6000081a40000000000000000000000014c5c5b3a000000d00000010000010036ffffffffffffffff0000003400000000root/usr/local/lib/sun4-solaris-64int/perllocal.pod=head2 Sat Aug 7 04:58:02 2010: C L =over 4 =item * C =item * C =item * C =item * C =back 07070100055e9d000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000001d00000000root/usr/local/lib/site_perl07070100055ea4000081a40000000000000000000000014c5062ef00008c620000010000010036ffffffffffffffff0000002400000000root/usr/local/lib/site_perl/Git.pm=head1 NAME Git - Perl interface to the Git version control system =cut package Git; use strict; BEGIN { our ($VERSION, @ISA, @EXPORT, @EXPORT_OK); # Totally unstable API. $VERSION = '0.01'; =head1 SYNOPSIS use Git; my $version = Git::command_oneline('version'); git_cmd_try { Git::command_noisy('update-server-info') } '%s failed w/ code %d'; my $repo = Git->repository (Directory => '/srv/git/cogito.git'); my @revs = $repo->command('rev-list', '--since=last monday', '--all'); my ($fh, $c) = $repo->command_output_pipe('rev-list', '--since=last monday', '--all'); my $lastrev = <$fh>; chomp $lastrev; $repo->command_close_pipe($fh, $c); my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ], STDERR => 0 ); my $sha1 = $repo->hash_and_insert_object('file.txt'); my $tempfile = tempfile(); my $size = $repo->cat_blob($sha1, $tempfile); =cut require Exporter; @ISA = qw(Exporter); @EXPORT = qw(git_cmd_try); # Methods which can be called as standalone functions as well: @EXPORT_OK = qw(command command_oneline command_noisy command_output_pipe command_input_pipe command_close_pipe command_bidi_pipe command_close_bidi_pipe version exec_path html_path hash_object git_cmd_try remote_refs temp_acquire temp_release temp_reset temp_path); =head1 DESCRIPTION This module provides Perl scripts easy way to interface the Git version control system. The modules have an easy and well-tested way to call arbitrary Git commands; in the future, the interface will also provide specialized methods for doing easily operations which are not totally trivial to do over the generic command interface. While some commands can be executed outside of any context (e.g. 'version' or 'init'), most operations require a repository context, which in practice means getting an instance of the Git object using the repository() constructor. (In the future, we will also get a new_repository() constructor.) All commands called as methods of the object are then executed in the context of the repository. Part of the "repository state" is also information about path to the attached working copy (unless you work with a bare repository). You can also navigate inside of the working copy using the C method. (Note that the repository object is self-contained and will not change working directory of your process.) TODO: In the future, we might also do my $remoterepo = $repo->remote_repository (Name => 'cogito', Branch => 'master'); $remoterepo ||= Git->remote_repository ('http://git.or.cz/cogito.git/'); my @refs = $remoterepo->refs(); Currently, the module merely wraps calls to external Git tools. In the future, it will provide a much faster way to interact with Git by linking directly to libgit. This should be completely opaque to the user, though (performance increase notwithstanding). =cut use Carp qw(carp croak); # but croak is bad - throw instead use Error qw(:try); use Cwd qw(abs_path); use IPC::Open2 qw(open2); use Fcntl qw(SEEK_SET SEEK_CUR); } =head1 CONSTRUCTORS =over 4 =item repository ( OPTIONS ) =item repository ( DIRECTORY ) =item repository () Construct a new repository object. C are passed in a hash like fashion, using key and value pairs. Possible options are: B - Path to the Git repository. B - Path to the associated working copy; not strictly required as many commands will happily crunch on a bare repository. B - Subdirectory in the working copy to work inside. Just left undefined if you do not want to limit the scope of operations. B - Path to the Git working directory in its usual setup. The C<.git> directory is searched in the directory and all the parent directories; if found, C is set to the directory containing it and C to the C<.git> directory itself. If no C<.git> directory was found, the C is assumed to be a bare repository, C is set to point at it and C is left undefined. If the C<$GIT_DIR> environment variable is set, things behave as expected as well. You should not use both C and either of C and C - the results of that are undefined. Alternatively, a directory path may be passed as a single scalar argument to the constructor; it is equivalent to setting only the C option field. Calling the constructor with no options whatsoever is equivalent to calling it with C<< Directory => '.' >>. In general, if you are building a standard porcelain command, simply doing C<< Git->repository() >> should do the right thing and setup the object to reflect exactly where the user is right now. =cut sub repository { my $class = shift; my @args = @_; my %opts = (); my $self; if (defined $args[0]) { if ($#args % 2 != 1) { # Not a hash. $#args == 0 or throw Error::Simple("bad usage"); %opts = ( Directory => $args[0] ); } else { %opts = @args; } } if (not defined $opts{Repository} and not defined $opts{WorkingCopy} and not defined $opts{Directory}) { $opts{Directory} = '.'; } if (defined $opts{Directory}) { -d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!"); my $search = Git->repository(WorkingCopy => $opts{Directory}); my $dir; try { $dir = $search->command_oneline(['rev-parse', '--git-dir'], STDERR => 0); } catch Git::Error::Command with { $dir = undef; }; if ($dir) { $dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir; $opts{Repository} = abs_path($dir); # If --git-dir went ok, this shouldn't die either. my $prefix = $search->command_oneline('rev-parse', '--show-prefix'); $dir = abs_path($opts{Directory}) . '/'; if ($prefix) { if (substr($dir, -length($prefix)) ne $prefix) { throw Error::Simple("rev-parse confused me - $dir does not have trailing $prefix"); } substr($dir, -length($prefix)) = ''; } $opts{WorkingCopy} = $dir; $opts{WorkingSubdir} = $prefix; } else { # A bare repository? Let's see... $dir = $opts{Directory}; unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") { # Mimic git-rev-parse --git-dir error message: throw Error::Simple("fatal: Not a git repository: $dir"); } my $search = Git->repository(Repository => $dir); try { $search->command('symbolic-ref', 'HEAD'); } catch Git::Error::Command with { # Mimic git-rev-parse --git-dir error message: throw Error::Simple("fatal: Not a git repository: $dir"); } $opts{Repository} = abs_path($dir); } delete $opts{Directory}; } $self = { opts => \%opts }; bless $self, $class; } =back =head1 METHODS =over 4 =item command ( COMMAND [, ARGUMENTS... ] ) =item command ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } ) Execute the given Git C (specify it without the 'git-' prefix), optionally with the specified extra C. The second more elaborate form can be used if you want to further adjust the command execution. Currently, only one option is supported: B - How to deal with the command's error output. By default (C) it is delivered to the caller's C. A false value (0 or '') will cause it to be thrown away. If you want to process it, you can get it in a filehandle you specify, but you must be extremely careful; if the error output is not very short and you want to read it in the same process as where you called C, you are set up for a nice deadlock! The method can be called without any instance or on a specified Git repository (in that case the command will be run in the repository context). In scalar context, it returns all the command output in a single string (verbatim). In array context, it returns an array containing lines printed to the command's stdout (without trailing newlines). In both cases, the command's stdin and stderr are the same as the caller's. =cut sub command { my ($fh, $ctx) = command_output_pipe(@_); if (not defined wantarray) { # Nothing to pepper the possible exception with. _cmd_close($fh, $ctx); } elsif (not wantarray) { local $/; my $text = <$fh>; try { _cmd_close($fh, $ctx); } catch Git::Error::Command with { # Pepper with the output: my $E = shift; $E->{'-outputref'} = \$text; throw $E; }; return $text; } else { my @lines = <$fh>; defined and chomp for @lines; try { _cmd_close($fh, $ctx); } catch Git::Error::Command with { my $E = shift; $E->{'-outputref'} = \@lines; throw $E; }; return @lines; } } =item command_oneline ( COMMAND [, ARGUMENTS... ] ) =item command_oneline ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } ) Execute the given C in the same way as command() does but always return a scalar string containing the first line of the command's standard output. =cut sub command_oneline { my ($fh, $ctx) = command_output_pipe(@_); my $line = <$fh>; defined $line and chomp $line; try { _cmd_close($fh, $ctx); } catch Git::Error::Command with { # Pepper with the output: my $E = shift; $E->{'-outputref'} = \$line; throw $E; }; return $line; } =item command_output_pipe ( COMMAND [, ARGUMENTS... ] ) =item command_output_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } ) Execute the given C in the same way as command() does but return a pipe filehandle from which the command output can be read. The function can return C<($pipe, $ctx)> in array context. See C for details. =cut sub command_output_pipe { _command_common_pipe('-|', @_); } =item command_input_pipe ( COMMAND [, ARGUMENTS... ] ) =item command_input_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } ) Execute the given C in the same way as command_output_pipe() does but return an input pipe filehandle instead; the command output is not captured. The function can return C<($pipe, $ctx)> in array context. See C for details. =cut sub command_input_pipe { _command_common_pipe('|-', @_); } =item command_close_pipe ( PIPE [, CTX ] ) Close the C as returned from C, checking whether the command finished successfully. The optional C argument is required if you want to see the command name in the error message, and it is the second value returned by C when called in array context. The call idiom is: my ($fh, $ctx) = $r->command_output_pipe('status'); while (<$fh>) { ... } $r->command_close_pipe($fh, $ctx); Note that you should not rely on whatever actually is in C; currently it is simply the command name but in future the context might have more complicated structure. =cut sub command_close_pipe { my ($self, $fh, $ctx) = _maybe_self(@_); $ctx ||= ''; _cmd_close($fh, $ctx); } =item command_bidi_pipe ( COMMAND [, ARGUMENTS... ] ) Execute the given C in the same way as command_output_pipe() does but return both an input pipe filehandle and an output pipe filehandle. The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>. See C for details. =cut sub command_bidi_pipe { my ($pid, $in, $out); $pid = open2($in, $out, 'git', @_); return ($pid, $in, $out, join(' ', @_)); } =item command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] ) Close the C and C as returned from C, checking whether the command finished successfully. The optional C argument is required if you want to see the command name in the error message, and it is the fourth value returned by C. The call idiom is: my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check'); print "000000000\n" $out; while (<$in>) { ... } $r->command_close_bidi_pipe($pid, $in, $out, $ctx); Note that you should not rely on whatever actually is in C; currently it is simply the command name but in future the context might have more complicated structure. =cut sub command_close_bidi_pipe { local $?; my ($pid, $in, $out, $ctx) = @_; foreach my $fh ($in, $out) { unless (close $fh) { if ($!) { carp "error closing pipe: $!"; } elsif ($? >> 8) { throw Git::Error::Command($ctx, $? >>8); } } } waitpid $pid, 0; if ($? >> 8) { throw Git::Error::Command($ctx, $? >>8); } } =item command_noisy ( COMMAND [, ARGUMENTS... ] ) Execute the given C in the same way as command() does but do not capture the command output - the standard output is not redirected and goes to the standard output of the caller application. While the method is called command_noisy(), you might want to as well use it for the most silent Git commands which you know will never pollute your stdout but you want to avoid the overhead of the pipe setup when calling them. The function returns only after the command has finished running. =cut sub command_noisy { my ($self, $cmd, @args) = _maybe_self(@_); _check_valid_cmd($cmd); my $pid = fork; if (not defined $pid) { throw Error::Simple("fork failed: $!"); } elsif ($pid == 0) { _cmd_exec($self, $cmd, @args); } if (waitpid($pid, 0) > 0 and $?>>8 != 0) { throw Git::Error::Command(join(' ', $cmd, @args), $? >> 8); } } =item version () Return the Git version in use. =cut sub version { my $verstr = command_oneline('--version'); $verstr =~ s/^git version //; $verstr; } =item exec_path () Return path to the Git sub-command executables (the same as C). Useful mostly only internally. =cut sub exec_path { command_oneline('--exec-path') } =item html_path () Return path to the Git html documentation (the same as C). Useful mostly only internally. =cut sub html_path { command_oneline('--html-path') } =item repo_path () Return path to the git repository. Must be called on a repository instance. =cut sub repo_path { $_[0]->{opts}->{Repository} } =item wc_path () Return path to the working copy. Must be called on a repository instance. =cut sub wc_path { $_[0]->{opts}->{WorkingCopy} } =item wc_subdir () Return path to the subdirectory inside of a working copy. Must be called on a repository instance. =cut sub wc_subdir { $_[0]->{opts}->{WorkingSubdir} ||= '' } =item wc_chdir ( SUBDIR ) Change the working copy subdirectory to work within. The C is relative to the working copy root directory (not the current subdirectory). Must be called on a repository instance attached to a working copy and the directory must exist. =cut sub wc_chdir { my ($self, $subdir) = @_; $self->wc_path() or throw Error::Simple("bare repository"); -d $self->wc_path().'/'.$subdir or throw Error::Simple("subdir not found: $subdir $!"); # Of course we will not "hold" the subdirectory so anyone # can delete it now and we will never know. But at least we tried. $self->{opts}->{WorkingSubdir} = $subdir; } =item config ( VARIABLE ) Retrieve the configuration C in the same manner as C does. In scalar context requires the variable to be set only one time (exception is thrown otherwise), in array context returns allows the variable to be set multiple times and returns all the values. This currently wraps command('config') so it is not so fast. =cut sub config { my ($self, $var) = _maybe_self(@_); try { my @cmd = ('config'); unshift @cmd, $self if $self; if (wantarray) { return command(@cmd, '--get-all', $var); } else { return command_oneline(@cmd, '--get', $var); } } catch Git::Error::Command with { my $E = shift; if ($E->value() == 1) { # Key not found. return; } else { throw $E; } }; } =item config_bool ( VARIABLE ) Retrieve the bool configuration C. The return value is usable as a boolean in perl (and C if it's not defined, of course). This currently wraps command('config') so it is not so fast. =cut sub config_bool { my ($self, $var) = _maybe_self(@_); try { my @cmd = ('config', '--bool', '--get', $var); unshift @cmd, $self if $self; my $val = command_oneline(@cmd); return undef unless defined $val; return $val eq 'true'; } catch Git::Error::Command with { my $E = shift; if ($E->value() == 1) { # Key not found. return undef; } else { throw $E; } }; } =item config_int ( VARIABLE ) Retrieve the integer configuration C. The return value is simple decimal number. An optional value suffix of 'k', 'm', or 'g' in the config file will cause the value to be multiplied by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. It would return C if configuration variable is not defined, This currently wraps command('config') so it is not so fast. =cut sub config_int { my ($self, $var) = _maybe_self(@_); try { my @cmd = ('config', '--int', '--get', $var); unshift @cmd, $self if $self; return command_oneline(@cmd); } catch Git::Error::Command with { my $E = shift; if ($E->value() == 1) { # Key not found. return undef; } else { throw $E; } }; } =item get_colorbool ( NAME ) Finds if color should be used for NAMEd operation from the configuration, and returns boolean (true for "use color", false for "do not use color"). =cut sub get_colorbool { my ($self, $var) = @_; my $stdout_to_tty = (-t STDOUT) ? "true" : "false"; my $use_color = $self->command_oneline('config', '--get-colorbool', $var, $stdout_to_tty); return ($use_color eq 'true'); } =item get_color ( SLOT, COLOR ) Finds color for SLOT from the configuration, while defaulting to COLOR, and returns the ANSI color escape sequence: print $repo->get_color("color.interactive.prompt", "underline blue white"); print "some text"; print $repo->get_color("", "normal"); =cut sub get_color { my ($self, $slot, $default) = @_; my $color = $self->command_oneline('config', '--get-color', $slot, $default); if (!defined $color) { $color = ""; } return $color; } =item remote_refs ( REPOSITORY [, GROUPS [, REFGLOBS ] ] ) This function returns a hashref of refs stored in a given remote repository. The hash is in the format C hash>. For tags, the C entry contains the tag object while a C entry gives the tagged objects. C has the same meaning as the appropriate C argument; either an URL or a remote name (if called on a repository instance). C is an optional arrayref that can contain 'tags' to return all the tags and/or 'heads' to return all the heads. C is an optional array of strings containing a shell-like glob to further limit the refs returned in the hash; the meaning is again the same as the appropriate C argument. This function may or may not be called on a repository instance. In the former case, remote names as defined in the repository are recognized as repository specifiers. =cut sub remote_refs { my ($self, $repo, $groups, $refglobs) = _maybe_self(@_); my @args; if (ref $groups eq 'ARRAY') { foreach (@$groups) { if ($_ eq 'heads') { push (@args, '--heads'); } elsif ($_ eq 'tags') { push (@args, '--tags'); } else { # Ignore unknown groups for future # compatibility } } } push (@args, $repo); if (ref $refglobs eq 'ARRAY') { push (@args, @$refglobs); } my @self = $self ? ($self) : (); # Ultra trickery my ($fh, $ctx) = Git::command_output_pipe(@self, 'ls-remote', @args); my %refs; while (<$fh>) { chomp; my ($hash, $ref) = split(/\t/, $_, 2); $refs{$ref} = $hash; } Git::command_close_pipe(@self, $fh, $ctx); return \%refs; } =item ident ( TYPE | IDENTSTR ) =item ident_person ( TYPE | IDENTSTR | IDENTARRAY ) This suite of functions retrieves and parses ident information, as stored in the commit and tag objects or produced by C (thus C can be either I or I; case is insignificant). The C method retrieves the ident information from C and either returns it as a scalar string or as an array with the fields parsed. Alternatively, it can take a prepared ident string (e.g. from the commit object) and just parse it. C returns the person part of the ident - name and email; it can take the same arguments as C or the array returned by C. The synopsis is like: my ($name, $email, $time_tz) = ident('author'); "$name <$email>" eq ident_person('author'); "$name <$email>" eq ident_person($name); $time_tz =~ /^\d+ [+-]\d{4}$/; =cut sub ident { my ($self, $type) = _maybe_self(@_); my $identstr; if (lc $type eq lc 'committer' or lc $type eq lc 'author') { my @cmd = ('var', 'GIT_'.uc($type).'_IDENT'); unshift @cmd, $self if $self; $identstr = command_oneline(@cmd); } else { $identstr = $type; } if (wantarray) { return $identstr =~ /^(.*) <(.*)> (\d+ [+-]\d{4})$/; } else { return $identstr; } } sub ident_person { my ($self, @ident) = _maybe_self(@_); $#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]); return "$ident[0] <$ident[1]>"; } =item hash_object ( TYPE, FILENAME ) Compute the SHA1 object id of the given C considering it is of the C object type (C, C, C). The method can be called without any instance or on a specified Git repository, it makes zero difference. The function returns the SHA1 hash. =cut # TODO: Support for passing FILEHANDLE instead of FILENAME sub hash_object { my ($self, $type, $file) = _maybe_self(@_); command_oneline('hash-object', '-t', $type, $file); } =item hash_and_insert_object ( FILENAME ) Compute the SHA1 object id of the given C and add the object to the object database. The function returns the SHA1 hash. =cut # TODO: Support for passing FILEHANDLE instead of FILENAME sub hash_and_insert_object { my ($self, $filename) = @_; carp "Bad filename \"$filename\"" if $filename =~ /[\r\n]/; $self->_open_hash_and_insert_object_if_needed(); my ($in, $out) = ($self->{hash_object_in}, $self->{hash_object_out}); unless (print $out $filename, "\n") { $self->_close_hash_and_insert_object(); throw Error::Simple("out pipe went bad"); } chomp(my $hash = <$in>); unless (defined($hash)) { $self->_close_hash_and_insert_object(); throw Error::Simple("in pipe went bad"); } return $hash; } sub _open_hash_and_insert_object_if_needed { my ($self) = @_; return if defined($self->{hash_object_pid}); ($self->{hash_object_pid}, $self->{hash_object_in}, $self->{hash_object_out}, $self->{hash_object_ctx}) = command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters)); } sub _close_hash_and_insert_object { my ($self) = @_; return unless defined($self->{hash_object_pid}); my @vars = map { 'hash_object_' . $_ } qw(pid in out ctx); command_close_bidi_pipe(@$self{@vars}); delete @$self{@vars}; } =item cat_blob ( SHA1, FILEHANDLE ) Prints the contents of the blob identified by C to C and returns the number of bytes printed. =cut sub cat_blob { my ($self, $sha1, $fh) = @_; $self->_open_cat_blob_if_needed(); my ($in, $out) = ($self->{cat_blob_in}, $self->{cat_blob_out}); unless (print $out $sha1, "\n") { $self->_close_cat_blob(); throw Error::Simple("out pipe went bad"); } my $description = <$in>; if ($description =~ / missing$/) { carp "$sha1 doesn't exist in the repository"; return -1; } if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) { carp "Unexpected result returned from git cat-file"; return -1; } my $size = $1; my $blob; my $bytesRead = 0; while (1) { my $bytesLeft = $size - $bytesRead; last unless $bytesLeft; my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024; my $read = read($in, $blob, $bytesToRead, $bytesRead); unless (defined($read)) { $self->_close_cat_blob(); throw Error::Simple("in pipe went bad"); } $bytesRead += $read; } # Skip past the trailing newline. my $newline; my $read = read($in, $newline, 1); unless (defined($read)) { $self->_close_cat_blob(); throw Error::Simple("in pipe went bad"); } unless ($read == 1 && $newline eq "\n") { $self->_close_cat_blob(); throw Error::Simple("didn't find newline after blob"); } unless (print $fh $blob) { $self->_close_cat_blob(); throw Error::Simple("couldn't write to passed in filehandle"); } return $size; } sub _open_cat_blob_if_needed { my ($self) = @_; return if defined($self->{cat_blob_pid}); ($self->{cat_blob_pid}, $self->{cat_blob_in}, $self->{cat_blob_out}, $self->{cat_blob_ctx}) = command_bidi_pipe(qw(cat-file --batch)); } sub _close_cat_blob { my ($self) = @_; return unless defined($self->{cat_blob_pid}); my @vars = map { 'cat_blob_' . $_ } qw(pid in out ctx); command_close_bidi_pipe(@$self{@vars}); delete @$self{@vars}; } { # %TEMP_* Lexical Context my (%TEMP_FILEMAP, %TEMP_FILES); =item temp_acquire ( NAME ) Attempts to retreive the temporary file mapped to the string C. If an associated temp file has not been created this session or was closed, it is created, cached, and set for autoflush and binmode. Internally locks the file mapped to C. This lock must be released with C when the temp file is no longer needed. Subsequent attempts to retrieve temporary files mapped to the same C while still locked will cause an error. This locking mechanism provides a weak guarantee and is not threadsafe. It does provide some error checking to help prevent temp file refs writing over one another. In general, the L returned should not be closed by consumers as it defeats the purpose of this caching mechanism. If you need to close the temp file handle, then you should use L or another temp file faculty directly. If a handle is closed and then requested again, then a warning will issue. =cut sub temp_acquire { my $temp_fd = _temp_cache(@_); $TEMP_FILES{$temp_fd}{locked} = 1; $temp_fd; } =item temp_release ( NAME ) =item temp_release ( FILEHANDLE ) Releases a lock acquired through C. Can be called either with the C mapping used when acquiring the temp file or with the C referencing a locked temp file. Warns if an attempt is made to release a file that is not locked. The temp file will be truncated before being released. This can help to reduce disk I/O where the system is smart enough to detect the truncation while data is in the output buffers. Beware that after the temp file is released and truncated, any operations on that file may fail miserably until it is re-acquired. All contents are lost between each release and acquire mapped to the same string. =cut sub temp_release { my ($self, $temp_fd, $trunc) = _maybe_self(@_); if (exists $TEMP_FILEMAP{$temp_fd}) { $temp_fd = $TEMP_FILES{$temp_fd}; } unless ($TEMP_FILES{$temp_fd}{locked}) { carp "Attempt to release temp file '", $temp_fd, "' that has not been locked"; } temp_reset($temp_fd) if $trunc and $temp_fd->opened; $TEMP_FILES{$temp_fd}{locked} = 0; undef; } sub _temp_cache { my ($self, $name) = _maybe_self(@_); _verify_require(); my $temp_fd = \$TEMP_FILEMAP{$name}; if (defined $$temp_fd and $$temp_fd->opened) { if ($TEMP_FILES{$$temp_fd}{locked}) { throw Error::Simple("Temp file with moniker '" . $name . "' already in use"); } } else { if (defined $$temp_fd) { # then we're here because of a closed handle. carp "Temp file '", $name, "' was closed. Opening replacement."; } my $fname; my $tmpdir; if (defined $self) { $tmpdir = $self->repo_path(); } ($$temp_fd, $fname) = File::Temp->tempfile( 'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir, ) or throw Error::Simple("couldn't open new temp file"); $$temp_fd->autoflush; binmode $$temp_fd; $TEMP_FILES{$$temp_fd}{fname} = $fname; } $$temp_fd; } sub _verify_require { eval { require File::Temp; require File::Spec; }; $@ and throw Error::Simple($@); } =item temp_reset ( FILEHANDLE ) Truncates and resets the position of the C. =cut sub temp_reset { my ($self, $temp_fd) = _maybe_self(@_); truncate $temp_fd, 0 or throw Error::Simple("couldn't truncate file"); sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET) or throw Error::Simple("couldn't seek to beginning of file"); sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0 or throw Error::Simple("expected file position to be reset"); } =item temp_path ( NAME ) =item temp_path ( FILEHANDLE ) Returns the filename associated with the given tempfile. =cut sub temp_path { my ($self, $temp_fd) = _maybe_self(@_); if (exists $TEMP_FILEMAP{$temp_fd}) { $temp_fd = $TEMP_FILEMAP{$temp_fd}; } $TEMP_FILES{$temp_fd}{fname}; } sub END { unlink values %TEMP_FILEMAP if %TEMP_FILEMAP; } } # %TEMP_* Lexical Context =back =head1 ERROR HANDLING All functions are supposed to throw Perl exceptions in case of errors. See the L module on how to catch those. Most exceptions are mere L instances. However, the C, C and C functions suite can throw C exceptions as well: those are thrown when the external command returns an error code and contain the error code as well as access to the captured command's output. The exception class provides the usual C and C (command's exit code) methods and in addition also a C method that returns either an array or a string with the captured command output (depending on the original function call context; C returns C) and $ which returns the command and its arguments (but without proper quoting). Note that the C functions cannot throw this exception since it has no idea whether the command failed or not. You will only find out at the time you C the pipe; if you want to have that automated, use C, which can throw the exception. =cut { package Git::Error::Command; @Git::Error::Command::ISA = qw(Error); sub new { my $self = shift; my $cmdline = '' . shift; my $value = 0 + shift; my $outputref = shift; my(@args) = (); local $Error::Depth = $Error::Depth + 1; push(@args, '-cmdline', $cmdline); push(@args, '-value', $value); push(@args, '-outputref', $outputref); $self->SUPER::new(-text => 'command returned error', @args); } sub stringify { my $self = shift; my $text = $self->SUPER::stringify; $self->cmdline() . ': ' . $text . ': ' . $self->value() . "\n"; } sub cmdline { my $self = shift; $self->{'-cmdline'}; } sub cmd_output { my $self = shift; my $ref = $self->{'-outputref'}; defined $ref or undef; if (ref $ref eq 'ARRAY') { return @$ref; } else { # SCALAR return $$ref; } } } =over 4 =item git_cmd_try { CODE } ERRMSG This magical statement will automatically catch any C exceptions thrown by C and make your program die with C on its lips; the message will have %s substituted for the command line and %d for the exit status. This statement is useful mostly for producing more user-friendly error messages. In case of no exception caught the statement returns C's return value. Note that this is the only auto-exported function. =cut sub git_cmd_try(&$) { my ($code, $errmsg) = @_; my @result; my $err; my $array = wantarray; try { if ($array) { @result = &$code; } else { $result[0] = &$code; } } catch Git::Error::Command with { my $E = shift; $err = $errmsg; $err =~ s/\%s/$E->cmdline()/ge; $err =~ s/\%d/$E->value()/ge; # We can't croak here since Error.pm would mangle # that to Error::Simple. }; $err and croak $err; return $array ? @result : $result[0]; } =back =head1 COPYRIGHT Copyright 2006 by Petr Baudis Epasky@suse.czE. This module is free software; it may be used, copied, modified and distributed under the terms of the GNU General Public Licence, either version 2, or (at your option) any later version. =cut # Take raw method argument list and return ($obj, @args) in case # the method was called upon an instance and (undef, @args) if # it was called directly. sub _maybe_self { UNIVERSAL::isa($_[0], 'Git') ? @_ : (undef, @_); } # Check if the command id is something reasonable. sub _check_valid_cmd { my ($cmd) = @_; $cmd =~ /^[a-z0-9A-Z_-]+$/ or throw Error::Simple("bad command: $cmd"); } # Common backend for the pipe creators. sub _command_common_pipe { my $direction = shift; my ($self, @p) = _maybe_self(@_); my (%opts, $cmd, @args); if (ref $p[0]) { ($cmd, @args) = @{shift @p}; %opts = ref $p[0] ? %{$p[0]} : @p; } else { ($cmd, @args) = @p; } _check_valid_cmd($cmd); my $fh; if ($^O eq 'MSWin32') { # ActiveState Perl #defined $opts{STDERR} and # warn 'ignoring STDERR option - running w/ ActiveState'; $direction eq '-|' or die 'input pipe for ActiveState not implemented'; # the strange construction with *ACPIPE is just to # explain the tie below that we want to bind to # a handle class, not scalar. It is not known if # it is something specific to ActiveState Perl or # just a Perl quirk. tie (*ACPIPE, 'Git::activestate_pipe', $cmd, @args); $fh = *ACPIPE; } else { my $pid = open($fh, $direction); if (not defined $pid) { throw Error::Simple("open failed: $!"); } elsif ($pid == 0) { if (defined $opts{STDERR}) { close STDERR; } if ($opts{STDERR}) { open (STDERR, '>&', $opts{STDERR}) or die "dup failed: $!"; } _cmd_exec($self, $cmd, @args); } } return wantarray ? ($fh, join(' ', $cmd, @args)) : $fh; } # When already in the subprocess, set up the appropriate state # for the given repository and execute the git command. sub _cmd_exec { my ($self, @args) = @_; if ($self) { $self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path(); $self->repo_path() and $self->wc_path() and $ENV{'GIT_WORK_TREE'} = $self->wc_path(); $self->wc_path() and chdir($self->wc_path()); $self->wc_subdir() and chdir($self->wc_subdir()); } _execv_git_cmd(@args); die qq[exec "@args" failed: $!]; } # Execute the given Git command ($_[0]) with arguments ($_[1..]) # by searching for it at proper places. sub _execv_git_cmd { exec('git', @_); } # Close pipe to a subprocess. sub _cmd_close { my ($fh, $ctx) = @_; if (not close $fh) { if ($!) { # It's just close, no point in fatalities carp "error closing pipe: $!"; } elsif ($? >> 8) { # The caller should pepper this. throw Git::Error::Command($ctx, $? >> 8); } # else we might e.g. closed a live stream; the command # dying of SIGPIPE would drive us here. } } sub DESTROY { my ($self) = @_; $self->_close_hash_and_insert_object(); $self->_close_cat_blob(); } # Pipe implementation for ActiveState Perl. package Git::activestate_pipe; use strict; sub TIEHANDLE { my ($class, @params) = @_; # FIXME: This is probably horrible idea and the thing will explode # at the moment you give it arguments that require some quoting, # but I have no ActiveState clue... --pasky # Let's just hope ActiveState Perl does at least the quoting # correctly. my @data = qx{git @params}; bless { i => 0, data => \@data }, $class; } sub READLINE { my $self = shift; if ($self->{i} >= scalar @{$self->{data}}) { return undef; } my $i = $self->{i}; if (wantarray) { $self->{i} = $#{$self->{'data'}} + 1; return splice(@{$self->{'data'}}, $i); } $self->{i} = $i + 1; return $self->{'data'}->[ $i ]; } sub CLOSE { my $self = shift; delete $self->{data}; delete $self->{i}; } sub EOF { my $self = shift; return ($self->{i} >= scalar @{$self->{data}}); } 1; # Famous last words 07070100055ea3000081a40000000000000000000000014c5062ef00004a900000010000010036ffffffffffffffff0000002600000000root/usr/local/lib/site_perl/Error.pm# Error.pm # # Copyright (c) 1997-8 Graham Barr . All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # Based on my original Error.pm, and Exceptions.pm by Peter Seibel # and adapted by Jesse Glick . # # but modified ***significantly*** package Error; use strict; use vars qw($VERSION); use 5.004; $VERSION = "0.15009"; use overload ( '""' => 'stringify', '0+' => 'value', 'bool' => sub { return 1; }, 'fallback' => 1 ); $Error::Depth = 0; # Depth to pass to caller() $Error::Debug = 0; # Generate verbose stack traces @Error::STACK = (); # Clause stack for try $Error::THROWN = undef; # last error thrown, a workaround until die $ref works my $LAST; # Last error created my %ERROR; # Last error associated with package sub throw_Error_Simple { my $args = shift; return Error::Simple->new($args->{'text'}); } $Error::ObjectifyCallback = \&throw_Error_Simple; # Exported subs are defined in Error::subs sub import { shift; local $Exporter::ExportLevel = $Exporter::ExportLevel + 1; Error::subs->import(@_); } # I really want to use last for the name of this method, but it is a keyword # which prevent the syntax last Error sub prior { shift; # ignore return $LAST unless @_; my $pkg = shift; return exists $ERROR{$pkg} ? $ERROR{$pkg} : undef unless ref($pkg); my $obj = $pkg; my $err = undef; if($obj->isa('HASH')) { $err = $obj->{'__Error__'} if exists $obj->{'__Error__'}; } elsif($obj->isa('GLOB')) { $err = ${*$obj}{'__Error__'} if exists ${*$obj}{'__Error__'}; } $err; } sub flush { shift; #ignore unless (@_) { $LAST = undef; return; } my $pkg = shift; return unless ref($pkg); undef $ERROR{$pkg} if defined $ERROR{$pkg}; } # Return as much information as possible about where the error # happened. The -stacktrace element only exists if $Error::DEBUG # was set when the error was created sub stacktrace { my $self = shift; return $self->{'-stacktrace'} if exists $self->{'-stacktrace'}; my $text = exists $self->{'-text'} ? $self->{'-text'} : "Died"; $text .= sprintf(" at %s line %d.\n", $self->file, $self->line) unless($text =~ /\n$/s); $text; } # Allow error propagation, ie # # $ber->encode(...) or # return Error->prior($ber)->associate($ldap); sub associate { my $err = shift; my $obj = shift; return unless ref($obj); if($obj->isa('HASH')) { $obj->{'__Error__'} = $err; } elsif($obj->isa('GLOB')) { ${*$obj}{'__Error__'} = $err; } $obj = ref($obj); $ERROR{ ref($obj) } = $err; return; } sub new { my $self = shift; my($pkg,$file,$line) = caller($Error::Depth); my $err = bless { '-package' => $pkg, '-file' => $file, '-line' => $line, @_ }, $self; $err->associate($err->{'-object'}) if(exists $err->{'-object'}); # To always create a stacktrace would be very inefficient, so # we only do it if $Error::Debug is set if($Error::Debug) { require Carp; local $Carp::CarpLevel = $Error::Depth; my $text = defined($err->{'-text'}) ? $err->{'-text'} : "Error"; my $trace = Carp::longmess($text); # Remove try calls from the trace $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog; $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::run_clauses[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog; $err->{'-stacktrace'} = $trace } $@ = $LAST = $ERROR{$pkg} = $err; } # Throw an error. this contains some very gory code. sub throw { my $self = shift; local $Error::Depth = $Error::Depth + 1; # if we are not rethrow-ing then create the object to throw $self = $self->new(@_) unless ref($self); die $Error::THROWN = $self; } # syntactic sugar for # # die with Error( ... ); sub with { my $self = shift; local $Error::Depth = $Error::Depth + 1; $self->new(@_); } # syntactic sugar for # # record Error( ... ) and return; sub record { my $self = shift; local $Error::Depth = $Error::Depth + 1; $self->new(@_); } # catch clause for # # try { ... } catch CLASS with { ... } sub catch { my $pkg = shift; my $code = shift; my $clauses = shift || {}; my $catch = $clauses->{'catch'} ||= []; unshift @$catch, $pkg, $code; $clauses; } # Object query methods sub object { my $self = shift; exists $self->{'-object'} ? $self->{'-object'} : undef; } sub file { my $self = shift; exists $self->{'-file'} ? $self->{'-file'} : undef; } sub line { my $self = shift; exists $self->{'-line'} ? $self->{'-line'} : undef; } sub text { my $self = shift; exists $self->{'-text'} ? $self->{'-text'} : undef; } # overload methods sub stringify { my $self = shift; defined $self->{'-text'} ? $self->{'-text'} : "Died"; } sub value { my $self = shift; exists $self->{'-value'} ? $self->{'-value'} : undef; } package Error::Simple; @Error::Simple::ISA = qw(Error); sub new { my $self = shift; my $text = "" . shift; my $value = shift; my(@args) = (); local $Error::Depth = $Error::Depth + 1; @args = ( -file => $1, -line => $2) if($text =~ s/\s+at\s+(\S+)\s+line\s+(\d+)(?:,\s*<[^>]*>\s+line\s+\d+)?\.?\n?$//s); push(@args, '-value', 0 + $value) if defined($value); $self->SUPER::new(-text => $text, @args); } sub stringify { my $self = shift; my $text = $self->SUPER::stringify; $text .= sprintf(" at %s line %d.\n", $self->file, $self->line) unless($text =~ /\n$/s); $text; } ########################################################################## ########################################################################## # Inspired by code from Jesse Glick and # Peter Seibel package Error::subs; use Exporter (); use vars qw(@EXPORT_OK @ISA %EXPORT_TAGS); @EXPORT_OK = qw(try with finally except otherwise); %EXPORT_TAGS = (try => \@EXPORT_OK); @ISA = qw(Exporter); sub blessed { my $item = shift; local $@; # don't kill an outer $@ ref $item and eval { $item->can('can') }; } sub run_clauses ($$$\@) { my($clauses,$err,$wantarray,$result) = @_; my $code = undef; $err = $Error::ObjectifyCallback->({'text' =>$err}) unless ref($err); CATCH: { # catch my $catch; if(defined($catch = $clauses->{'catch'})) { my $i = 0; CATCHLOOP: for( ; $i < @$catch ; $i += 2) { my $pkg = $catch->[$i]; unless(defined $pkg) { #except splice(@$catch,$i,2,$catch->[$i+1]->()); $i -= 2; next CATCHLOOP; } elsif(blessed($err) && $err->isa($pkg)) { $code = $catch->[$i+1]; while(1) { my $more = 0; local($Error::THROWN); my $ok = eval { if($wantarray) { @{$result} = $code->($err,\$more); } elsif(defined($wantarray)) { @{$result} = (); $result->[0] = $code->($err,\$more); } else { $code->($err,\$more); } 1; }; if( $ok ) { next CATCHLOOP if $more; undef $err; } else { $err = defined($Error::THROWN) ? $Error::THROWN : $@; $err = $Error::ObjectifyCallback->({'text' =>$err}) unless ref($err); } last CATCH; }; } } } # otherwise my $owise; if(defined($owise = $clauses->{'otherwise'})) { my $code = $clauses->{'otherwise'}; my $more = 0; my $ok = eval { if($wantarray) { @{$result} = $code->($err,\$more); } elsif(defined($wantarray)) { @{$result} = (); $result->[0] = $code->($err,\$more); } else { $code->($err,\$more); } 1; }; if( $ok ) { undef $err; } else { $err = defined($Error::THROWN) ? $Error::THROWN : $@; $err = $Error::ObjectifyCallback->({'text' =>$err}) unless ref($err); } } } $err; } sub try (&;$) { my $try = shift; my $clauses = @_ ? shift : {}; my $ok = 0; my $err = undef; my @result = (); unshift @Error::STACK, $clauses; my $wantarray = wantarray(); do { local $Error::THROWN = undef; local $@ = undef; $ok = eval { if($wantarray) { @result = $try->(); } elsif(defined $wantarray) { $result[0] = $try->(); } else { $try->(); } 1; }; $err = defined($Error::THROWN) ? $Error::THROWN : $@ unless $ok; }; shift @Error::STACK; $err = run_clauses($clauses,$err,wantarray,@result) unless($ok); $clauses->{'finally'}->() if(defined($clauses->{'finally'})); if (defined($err)) { if (blessed($err) && $err->can('throw')) { throw $err; } else { die $err; } } wantarray ? @result : $result[0]; } # Each clause adds a sub to the list of clauses. The finally clause is # always the last, and the otherwise clause is always added just before # the finally clause. # # All clauses, except the finally clause, add a sub which takes one argument # this argument will be the error being thrown. The sub will return a code ref # if that clause can handle that error, otherwise undef is returned. # # The otherwise clause adds a sub which unconditionally returns the users # code reference, this is why it is forced to be last. # # The catch clause is defined in Error.pm, as the syntax causes it to # be called as a method sub with (&;$) { @_ } sub finally (&) { my $code = shift; my $clauses = { 'finally' => $code }; $clauses; } # The except clause is a block which returns a hashref or a list of # key-value pairs, where the keys are the classes and the values are subs. sub except (&;$) { my $code = shift; my $clauses = shift || {}; my $catch = $clauses->{'catch'} ||= []; my $sub = sub { my $ref; my(@array) = $code->($_[0]); if(@array == 1 && ref($array[0])) { $ref = $array[0]; $ref = [ %$ref ] if(UNIVERSAL::isa($ref,'HASH')); } else { $ref = \@array; } @$ref }; unshift @{$catch}, undef, $sub; $clauses; } sub otherwise (&;$) { my $code = shift; my $clauses = shift || {}; if(exists $clauses->{'otherwise'}) { require Carp; Carp::croak("Multiple otherwise clauses"); } $clauses->{'otherwise'} = $code; $clauses; } 1; __END__ =head1 NAME Error - Error/exception handling in an OO-ish way =head1 SYNOPSIS use Error qw(:try); throw Error::Simple( "A simple error"); sub xyz { ... record Error::Simple("A simple error") and return; } unlink($file) or throw Error::Simple("$file: $!",$!); try { do_some_stuff(); die "error!" if $condition; throw Error::Simple -text => "Oops!" if $other_condition; } catch Error::IO with { my $E = shift; print STDERR "File ", $E->{'-file'}, " had a problem\n"; } except { my $E = shift; my $general_handler=sub {send_message $E->{-description}}; return { UserException1 => $general_handler, UserException2 => $general_handler }; } otherwise { print STDERR "Well I don't know what to say\n"; } finally { close_the_garage_door_already(); # Should be reliable }; # Don't forget the trailing ; or you might be surprised =head1 DESCRIPTION The C package provides two interfaces. Firstly C provides a procedural interface to exception handling. Secondly C is a base class for errors/exceptions that can either be thrown, for subsequent catch, or can simply be recorded. Errors in the class C should not be thrown directly, but the user should throw errors from a sub-class of C. =head1 PROCEDURAL INTERFACE C exports subroutines to perform exception handling. These will be exported if the C<:try> tag is used in the C line. =over 4 =item try BLOCK CLAUSES C is the main subroutine called by the user. All other subroutines exported are clauses to the try subroutine. The BLOCK will be evaluated and, if no error is throw, try will return the result of the block. C are the subroutines below, which describe what to do in the event of an error being thrown within BLOCK. =item catch CLASS with BLOCK This clauses will cause all errors that satisfy C<$err-Eisa(CLASS)> to be caught and handled by evaluating C. C will be passed two arguments. The first will be the error being thrown. The second is a reference to a scalar variable. If this variable is set by the catch block then, on return from the catch block, try will continue processing as if the catch block was never found. To propagate the error the catch block may call C<$err-Ethrow> If the scalar reference by the second argument is not set, and the error is not thrown. Then the current try block will return with the result from the catch block. =item except BLOCK When C is looking for a handler, if an except clause is found C is evaluated. The return value from this block should be a HASHREF or a list of key-value pairs, where the keys are class names and the values are CODE references for the handler of errors of that type. =item otherwise BLOCK Catch any error by executing the code in C When evaluated C will be passed one argument, which will be the error being processed. Only one otherwise block may be specified per try block =item finally BLOCK Execute the code in C either after the code in the try block has successfully completed, or if the try block throws an error then C will be executed after the handler has completed. If the handler throws an error then the error will be caught, the finally block will be executed and the error will be re-thrown. Only one finally block may be specified per try block =back =head1 CLASS INTERFACE =head2 CONSTRUCTORS The C object is implemented as a HASH. This HASH is initialized with the arguments that are passed to it's constructor. The elements that are used by, or are retrievable by the C class are listed below, other classes may add to these. -file -line -text -value -object If C<-file> or C<-line> are not specified in the constructor arguments then these will be initialized with the file name and line number where the constructor was called from. If the error is associated with an object then the object should be passed as the C<-object> argument. This will allow the C package to associate the error with the object. The C package remembers the last error created, and also the last error associated with a package. This could either be the last error created by a sub in that package, or the last error which passed an object blessed into that package as the C<-object> argument. =over 4 =item throw ( [ ARGS ] ) Create a new C object and throw an error, which will be caught by a surrounding C block, if there is one. Otherwise it will cause the program to exit. C may also be called on an existing error to re-throw it. =item with ( [ ARGS ] ) Create a new C object and returns it. This is defined for syntactic sugar, eg die with Some::Error ( ... ); =item record ( [ ARGS ] ) Create a new C object and returns it. This is defined for syntactic sugar, eg record Some::Error ( ... ) and return; =back =head2 STATIC METHODS =over 4 =item prior ( [ PACKAGE ] ) Return the last error created, or the last error associated with C =item flush ( [ PACKAGE ] ) Flush the last error created, or the last error associated with C.It is necessary to clear the error stack before exiting the package or uncaught errors generated using C will be reported. $Error->flush; =cut =back =head2 OBJECT METHODS =over 4 =item stacktrace If the variable C<$Error::Debug> was non-zero when the error was created, then C returns a string created by calling C. If the variable was zero the C returns the text of the error appended with the filename and line number of where the error was created, providing the text does not end with a newline. =item object The object this error was associated with =item file The file where the constructor of this error was called from =item line The line where the constructor of this error was called from =item text The text of the error =back =head2 OVERLOAD METHODS =over 4 =item stringify A method that converts the object into a string. This method may simply return the same as the C method, or it may append more information. For example the file name and line number. By default this method returns the C<-text> argument that was passed to the constructor, or the string C<"Died"> if none was given. =item value A method that will return a value that can be associated with the error. For example if an error was created due to the failure of a system call, then this may return the numeric value of C<$!> at the time. By default this method returns the C<-value> argument that was passed to the constructor. =back =head1 PRE-DEFINED ERROR CLASSES =over 4 =item Error::Simple This class can be used to hold simple error strings and values. It's constructor takes two arguments. The first is a text value, the second is a numeric value. These values are what will be returned by the overload methods. If the text value ends with C as $@ strings do, then this infomation will be used to set the C<-file> and C<-line> arguments of the error object. This class is used internally if an eval'd block die's with an error that is a plain string. (Unless C<$Error::ObjectifyCallback> is modified) =back =head1 $Error::ObjectifyCallback This variable holds a reference to a subroutine that converts errors that are plain strings to objects. It is used by Error.pm to convert textual errors to objects, and can be overridden by the user. It accepts a single argument which is a hash reference to named parameters. Currently the only named parameter passed is C<'text'> which is the text of the error, but others may be available in the future. For example the following code will cause Error.pm to throw objects of the class MyError::Bar by default: sub throw_MyError_Bar { my $args = shift; my $err = MyError::Bar->new(); $err->{'MyBarText'} = $args->{'text'}; return $err; } { local $Error::ObjectifyCallback = \&throw_MyError_Bar; # Error handling here. } =head1 KNOWN BUGS None, but that does not mean there are not any. =head1 AUTHORS Graham Barr The code that inspired me to write this was originally written by Peter Seibel and adapted by Jesse Glick . =head1 MAINTAINER Shlomi Fish =head1 PAST MAINTAINERS Arun Kumar U =cut 07070100055e9e000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000002300000000root/usr/local/lib/site_perl/5.8.407070100055e9f000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000003600000000root/usr/local/lib/site_perl/5.8.4/sun4-solaris-64int07070100055ea0000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000003b00000000root/usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto07070100055ea1000041ed0000000000000000000000024c5c5c81000000000000010000010036ffffffffffffffff0000003f00000000root/usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git07070100055ea2000081a40000000000000000000000014c5c5b390000008c0000010000010036ffffffffffffffff0000004900000000root/usr/local/lib/site_perl/5.8.4/sun4-solaris-64int/auto/Git/.packlist/usr/local/lib/site_perl/Error.pm /usr/local/lib/site_perl/Git.pm /usr/local/share/man/man3/Git.3 /usr/local/share/man/man3/private-Error.3 07070100055e88000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000001d00000000root/usr/local/lib/python2.707070100055e89000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000002b00000000root/usr/local/lib/python2.7/site-packages07070100055e8b000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000003e00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers07070100055e8d000081a40000000000000000000000014c5c5b3b000002950000010000010036ffffffffffffffff0000004b00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.pyc bPLc@s dZdS(sSupport library package for git remote helpers. Git remote helpers are helper commands that interfaces with a non-git repository to provide automatic import of non-git history into a Git repository. This package provides the support library needed by these helpers.. The following modules are included: - git.git - Interaction with Git repositories - util - General utility functionality use by the other modules in this package, and also used directly by the helpers. N(t__doc__(((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.pyts07070100055e8c000081a40000000000000000000000014c5062ef000002000000010000010036ffffffffffffffff0000004a00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/__init__.py#!/usr/bin/env python """Support library package for git remote helpers. Git remote helpers are helper commands that interfaces with a non-git repository to provide automatic import of non-git history into a Git repository. This package provides the support library needed by these helpers.. The following modules are included: - git.git - Interaction with Git repositories - util - General utility functionality use by the other modules in this package, and also used directly by the helpers. """ 07070100055e8e000041ed0000000000000000000000024c5c5c81000000000000010000010036ffffffffffffffff0000004200000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git07070100055e92000081a40000000000000000000000014c5c5b3b000009020000010000010036ffffffffffffffff0000004f00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyc bPLc@s>ddlZddlZddlZdefdYZdS(iNt GitExportercBs eZdZdZdZRS(saAn exporter for testgit repositories. The exporter simply delegates to git fast-export. cCs ||_dS(s7Creates a new exporter for the specified repo. N(trepo(tselfR((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyt__init__ scCse|jj|}tjjtjj|d}tjj|sUtj|ndGHtjjtjj|drd|jjGHnd|jjGHt j j dd|jj dd |g}tjj|r|j d |n|j d tj|d tj}d d|jjdg}tj|d|j }|jdkratndS(s^Exports a fast-export stream for the given directory. Simply delegates to git fast-epxort and pipes it through sed to make the refs show up under the prefix rather than the default refs/heads. This is to demonstrate how the export data can be stored under it's own ref (using the refspec capability). s testgit.markssfeature relative-markss git.markss!feature import-marks=%s/git.markss!feature export-marks=%s/git.markstgits --git-dir=s fast-exports--export-marks=s--import-marks=tHEADtstdouttsedss_refs/heads/_t_gtstdiniN(Rt get_base_pathtostpathtabspathtjointexiststmakedirsthashtsysRtflushtgitpathtappendt subprocesstPopentPIPEtprefixtwaittCalledProcessError(RtbasetdirnameR targstp1tchild((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyt export_repos$ !!   (t__name__t __module__t__doc__RR!(((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyRs (R RRtobjectR(((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyts   07070100055e91000081a40000000000000000000000014c5062ef0000065d0000010000010036ffffffffffffffff0000004e00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/exporter.pyimport os import subprocess import sys class GitExporter(object): """An exporter for testgit repositories. The exporter simply delegates to git fast-export. """ def __init__(self, repo): """Creates a new exporter for the specified repo. """ self.repo = repo def export_repo(self, base): """Exports a fast-export stream for the given directory. Simply delegates to git fast-epxort and pipes it through sed to make the refs show up under the prefix rather than the default refs/heads. This is to demonstrate how the export data can be stored under it's own ref (using the refspec capability). """ dirname = self.repo.get_base_path(base) path = os.path.abspath(os.path.join(dirname, 'testgit.marks')) if not os.path.exists(dirname): os.makedirs(dirname) print "feature relative-marks" if os.path.exists(os.path.join(dirname, 'git.marks')): print "feature import-marks=%s/git.marks" % self.repo.hash print "feature export-marks=%s/git.marks" % self.repo.hash sys.stdout.flush() args = ["git", "--git-dir=" + self.repo.gitpath, "fast-export", "--export-marks=" + path] if os.path.exists(path): args.append("--import-marks=" + path) args.append("HEAD") p1 = subprocess.Popen(args, stdout=subprocess.PIPE) args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"] child = subprocess.Popen(args, stdin=p1.stdout) if child.wait() != 0: raise CalledProcessError 07070100055e94000081a40000000000000000000000014c5c5b3b000070870000010000010036ffffffffffffffff0000004a00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyc bPLc@s\dZddlZddlZddlZddlmZddlmZddlZddl m Z m Z m Z m Z mZdZdZdZd Zd efd YZd efd YZdefdYZdejfdYZdefdYZdefdYZdefdYZedkrXejndS(szFunctionality for interacting with Git repositories. This module provides classes for interfacing with a Git repository. iN(thexlify(tStringIO(tdebugterrortdiet start_commandt run_commandcCsEd}t|\}}}|r.tdn| s;t|jS(s-Return the path to the GIT_DIR for this repo.tgits rev-parses --git-dirsFailed to retrieve git dir(Rs rev-parses --git-dir(RRtAssertionErrortstrip(targst exit_codetoutputterrors((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt get_git_dirs   cCsmtd \}}}|r(tdn| s5ttg|jdD]}|rH|jdd^qHS( s?Return a dict containing the parsed version of 'git config -l'.Rtconfigs-zs-ls$Failed to retrieve git configurationts i(sgitsconfigs-zs-l(RRRtdicttsplit(R R R te((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytparse_git_configs   cCsLt|jj}|d kr(tS|dkr8tStd |d S(sConvert the given git config string value to True or False. Raise ValueError if the given string was not recognized as a boolean value. ttruet1tyestonttfalset0tnotofftnones)Failed to parse '%s' into a boolean valueN(strueRsyessonR(sfalseRsnosoffR(tstrR tlowertTruetFalset ValueError(tvaluet norm_value((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytgit_config_bool%s   c Cs|jdsK|jdsK|jdsK|jdsK|jdrOtSxB|D]:}t|dkst|dks|dkrVtSqVWtS( s;Return True iff the given ref name is a valid git ref name.t/t.s/.s..s.locki is ~^:?*[(tendswitht startswithtcountR"tordR!(tref_nametc((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt valid_git_ref4s 0tGitObjectFetchercBsbeZdZdZdZdZdZdZdZdZ dZ d d Z RS( s{Provide parsed access to 'git cat-file --batch'. This provides a read-only interface to the Git object database. cCsjg|_d|_tj\}}tj|d}tj|d}tdd||_||_dS( s*Initiate a 'git cat-file --batch' session.trbtwbRscat-files--batchtstdoutN(sgitscat-files--batch( tqueuetNonet in_transittostpipetfdopenRtproctf(tselft pipe_r_fdt pipe_w_fdtpipe_rtpipe_w((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt__init__Ns    cCso|j st|jdks%t|jjj|jjdksPt|jj dksktdS(s;Verify completed communication with 'git cat-file --batch'.iRN( R4RR6R5R:tstdintclosetwaitR;tread(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt__del__`s cCsK|jrG|jdkrG|jjd|_|jj|jdIJndS(sSubmit queue items to the 'git cat-file --batch' process. If there are items in the queue, and there is currently no item currently in 'transit', then pop the first item off the queue, and submit it. iN(R4R6R5tpopR:RB(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt_submit_next_objecthscCs$|jj||f|jdS(sPush the given object name onto the queue. The given callback function will at some point in the future be called exactly once with the following arguments: - self - this GitObjectFetcher instance - obj - the object name provided to push() - sha1 - the SHA1 of the object, if 'None' obj is missing - t - the type of the object (tag/commit/tree/blob) - size - the size of the object in bytes - data - the object contents N(R4tappendRH(R<tobjtcallback((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytpushts cCs|j\}}d|_|jj}|d|krT|||dddddS|jd\}}}t|dkst|d kst|jd stt|j }|jj |}|jj d d kst||||||||j dS( s6Read the next entry off the queue and invoke callback.s %s missing Nt i(ttagtcommitttreetblobs i(RNscommitRPsblob( R6R5R;treadlineRtlenRR)tintR RERH(R<RJtcbtheadertsha1tttsizetdata((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytprocess_next_entrys cCs$x|jdk r|jqWdS(s&Process the current queue until empty.N(R6R5R[(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytprocessscCsCdtfdY}||}|j|||j|jS(sReturn the SHA1 of the object specified by 'objspec'. Return None if 'objspec' does not specify an existing object. t _ObjHandlercBs eZdZdZdZRS(s+Helper class for getting the returned SHA1.cSs||_d|_dS(N(tparserR5RW(R<R^((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAs cSs"||jkst||_dS(N(R^RRW(R<R^RJRWRXRYRZ((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt__call__s(t__name__t __module__t__doc__RAR_(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR]s (tobjectRLR\RW(R<tobjspecR]thandler((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytget_sha1s   cCsndtfdY}||}|j|||j|jrWt|jn|jjd|jS(sReturn a file object wrapping the contents of a named object. The caller is responsible for calling .close() on the returned file object. Raise KeyError if 'objspec' does not exist in the repo. R]cBs eZdZdZdZRS(s1Helper class for parsing the returned git object.cSs"||_t|_d|_dS(sSet up helper.N(R^RtcontentsR5terr(R<R^((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAs  cSsW||jkst|s+d||_n(|t|ksCt|jj|dS(s9Git object callback (see GitObjectFetcher documentation).sMissing object '%s'N(R^RRhRSRgtwrite(R<R^RJRWRXRYRZ((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR_s (R`RaRbRAR_(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR]s i(RcRLR\RhtKeyErrorRgtseek(R<RdR]Re((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytopen_objs    RcCs@dtfdY}|j||||||jdS(s:Recursively walk the given Git tree object. Recursively walk all subtrees of the given tree object, and invoke the given callback passing three arguments: (path, mode, data) with the path, permission bits, and contents of all the blobs found in the entire tree structure. R]cBs,eZdZddZdZdZRS(s.Helper class for walking a git tree structure.cSs1||_||_||_||_d|_dS(sSet up helper.N(R^RUtpathtmodeR5Rh(R<R^RURmRn((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAs     cssx|rt|d d}|dkr5|d8}n|ddksKt|jdd}|dksot|d|!}t||d|d !}|||j|fV||d }qWd S( sParse tree object data, yield tree entries. Each tree entry is a 3-tuple (mode, sha1, path) self.path is prepended to all paths yielded from this method. ii iRMRiiiiN(RTRtfindRRm(R<ttreedataRntiRmRW((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt parse_trees    c Ss||jkst|s,d||_dS|t|ksDt|dkr|jrk|jd7_nx|j|D]7\}}} |j||j|j|j| |q{Wn8|dkr|j|j|j |nt d|dS(s9Git object callback (see GitObjectFetcher documentation).sMissing object '%s'NRPR'RQsUnknown object type '%s'( R^RRhRSRmRrRLt __class__RURnR#( R<R^RJRWRXRYRZtmtstp((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR_s    & N(R`RaRbR5RARrR_(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR]s  N(RcRLR\(R<t tree_objspecRKtprefixR]((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt walk_trees 3( R`RaRbRARFRHRLR[R\RfRlRy(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR0Fs       "t GitRefMapcBs>eZdZdZdZdZdZddZRS(sMap Git ref names to the Git object names they currently point to. Behaves like a dictionary of Git ref names -> Git object names. cCs||_i|_dS(s#Create a new Git ref -> object map.N(t obj_fetchert_cache(R<R{((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAs cCs6||jkr+|jj||j||jdd}|jdd}|jdd}d|S(s*Return a quoted version of the given path.s\s\\s s\nt"s\"s"%s"(treplace(RRm((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt quote_pathWscCst|t st|jd s,t|jd sBt|jd sXt|jd snt|jd st|jd st|jd st|jd st|jd st|jd |jd |jd r|j|S|S( s=Verify that the given path is valid, and quote it, if needed.s//R's/./s/../s/.s/..s./s../Rs s\(RRTRR+R)R*R(RRm((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt parse_path_s) cCs:||_||_||_||_||_g|_dS(s=Create a new Git fast-import commit, with the given metadata.N(tnametemailt timestampttimezonetmessagetpathops(R<RRRRR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAss      cCs;|jjd|j||j||j|fdS(s7Add a file modification to this Git fast-import commit.tMN(RRIRRR(R<RntblobnameRm((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytmodify|s   cCs#|jjd|j|fdS(s3Add a file deletion to this Git fast-import commit.tDN(RRIR(R<Rm((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytdeletescCs/|jjd|j||j|fdS(s/Add a file copy to this Git fast-import commit.tCN(RRIR(R<Rmtnewpath((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytcopys  cCs/|jjd|j||j|fdS(s1Add a file rename to this Git fast-import commit.tRN(RRIR(R<RmR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytrenames  cCs/|jjd|j||j|fdS(s1Add a note object to this Git fast-import commit.tNN(RRIR(R<RRO((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytnotes  cCs|jjddS(s0Delete all files in this Git fast-import commit.t deleteallN(RRI(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRs(R`RaRbtretcompileRt classmethodRRRRRARRRRRR(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR@s      tTestGitFICommitcBs2eZdZdZdZdZdZRS(sGitFICommit selftests.cCs d}dS(sGitFICommit basic selftests.cSsFy||Wntk r"dSXtd|jt|fdS(s6Verify that the method(data) raises an AssertionError.Ns%Failed test for invalid data '%s(%s)'(RR`trepr(tmethodRZ((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt expect_fails  N((R<R((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt test_basicscCs|jtjdd|jtjdd|jtjdd|jtjdd|jtjd d |jttjd |jttjd |jttjd |jttjd|jttjddS(s#GitFICommit.parse_mode() selftests.it644it755i$t100644it100755it120000ii{iXtabcN(t assertEqualRRt assertRaisesR(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyttest_parse_modescCs|jtjdd|jttjd|jttjd|jtjdddd|jtjdddd|jttjddd S( s&GitFICommit.parse_objname() selftests.is:1iit 0123456789it 2468abcdeft abcdefghijN(RRRRR(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyttest_parse_objnames  cCs>|jtjdd|jtjdd|jttjd|jttjd|jttjd|jttjd|jttjd|jttjd |jttjd |jttjd |jttjd |jttjd |jttjd|jttjddS(s#GitFICommit.parse_path() selftests.sfoo/barspath/with and " in its"path/with\n and \" in it"iiisfoo//barsfoo/bar/s/foo/bars foo/./bars foo/../bars foo/bar/.s foo/bar/..s ./foo/bars ../foo/barN(RRRRR(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyttest_parse_paths(R`RaRbRRRR(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRs  t GitFastImportcBs>eZdZddZdZdZdZdZRS(s/Encapsulate communication with git fast-import.icCs/||_||_|d|_t|_dS(s@Set up self to communicate with a fast-import process through f.iN(R;R{t next_marktsettrefs(R<R;R{t last_mark((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAs   cCs1d|kstd||jjd|dS(s2Write the given comment in the fast-import stream.s sMalformed comment: '%s's# %s N(RR;Ri(R<Ru((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytcommentscCs%|jjdi|d6|jd6|jd6|jd6|jd6|jd6t|jd6|jd 6||j kr|j j ||d }|j j |r|jjd |qnx:|j D]/}|jjd j||jjd qW|jjd |j}|jd7_|S(s~Make a commit on the given ref, with the given GitFICommit. Return the mark number identifying this commit. sscommit %(ref)s mark :%(mark)i committer %(name)s <%(email)s> %(timestamp)i %(timezone)s data %(msgLength)i %(msg)s R}tmarkRRRRt msgLengthtmsgs^0sfrom %s RMs i(R;RiRRRRRRSRRtaddR{RfRtjoin(R<R}t commitdatatparenttoptretval((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyROs,        cCsB|jjd|jt||f|j}|jd7_|S(sWImport the given blob. Return the mark number identifying this blob. sblob mark :%i data %i %s i(R;RiRRS(R<RZR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRQs   cCsI|jjd|tj|f||jkrE|jj|ndS(s5Reset the given ref to point at the given Git object.sreset %s from %s N(R;RiRRRR(R<R}R((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pytresets (R`RaRbRARRORQR(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRs    % tGitNotescBsPeZdZdZdZdZdZddZdZ dZ RS( snEncapsulate access to Git notes. Simulates a dictionary of object name (SHA1) -> Git note mappings. cCs||_||_g|_dS(s?Create a new Git notes interface, bound to the given notes ref.N(t notes_refR{timports(R<RR{((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRA+s  cCs0|jr,tdtdt|jndS(s>Verify that self.commit_notes() was called before destruction.s$Missing call to self.commit_notes().s%i notes are not committed!N(RRRS(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRF1s  cCsWy9|jjd|j|f}|j}|jWntk rRd}nX|S(sReturn the note data associated with the given git object. The note data is returned in string form. If no note is found for the given object, None is returned. s%s:%sN(R{RlRRERCRjR5(R<RR;tret((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR~7s   cCs2|j|}|dkr.td|n|S(sReturn the note contents associated with the given object. Raise KeyError if given object has no associated note. sObject '%s' has no noteN(R~R5Rj(R<Rtblobdata((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRFs cCs#|j|}|dkr|S|S(sReturn the note contents associated with the given object. Return given default if given object has no associated note. N(R~R5(R<RRR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRQs cCsV|jds|d7}n|jd||j|}|jj||fdS(sTell git fast-import to store data as a note for objname. This method uses the given GitFastImport object to create a blob containing the given note data. Also an entry mapping the given object name to the created blob is stored until commit_notes() is called. Note that this method only works if it is later followed by a call to self.commit_notes() (which produces the note commit that refers to the blob produced here). s sImporting note for object %sN(R)RRQRRI(R<RRZtgfiR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt import_note\s  cCs|js dSt|d|dtjd|}xi|jD]^\}}t|trd|dksjtt|tr|dkst|j||q=W|j|j|g|_dS(sProduce a git fast-import note commit for the imported notes. This method uses the given GitFastImport object to create a commit on the notes ref, introducing the notes previously submitted to import_note(). Niit0000( RRttimeRRTRRROR(R<RtauthorRRRR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyt commit_notesos !!N( R`RaRbRARFR~RR5RRR(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR#s    tGitCachedNotescBs2eZdZdZdZdZdZRS(sEncapsulate access to Git notes (cached version). Only use this class if no caching is done at a higher level. Simulates a dictionary of object name (SHA1) -> Git note mappings. cCs tj|||i|_dS(s)Set up a caching wrapper around GitNotes.N(RRAR|(R<RR{((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRAscCstj|dS(s+Verify that GitNotes' destructor is called.N(RRF(R<((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRFscCs6||jkr+tj|||j| note cache.(R|RR~(R<R((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyR~scCsX|jds|d7}n||jks1t||j| note cache.s N(R)R|RRR(R<RRZR((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRs   (R`RaRbRARFR~R(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyRs    t__main__(RbR7RRtbinasciiRt cStringIORtunittesttgit_remote_helpers.utilRRRRRRRR&R/RcR0RzRtTestCaseRRRRR`tmain(((sD/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.pyts(    (  +^<I`" 07070100055e99000081a40000000000000000000000014c5062ef0000077b0000010000010036ffffffffffffffff0000004a00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyimport os import subprocess def sanitize(rev, sep='\t'): """Converts a for-each-ref line to a name/value pair. """ splitrev = rev.split(sep) branchval = splitrev[0] branchname = splitrev[1].strip() if branchname.startswith("refs/heads/"): branchname = branchname[11:] return branchname, branchval def is_remote(url): """Checks whether the specified value is a remote url. """ prefixes = ["http", "file", "git"] for prefix in prefixes: if url.startswith(prefix): return True return False class GitRepo(object): """Repo object representing a repo. """ def __init__(self, path): """Initializes a new repo at the given path. """ self.path = path self.head = None self.revmap = {} self.local = not is_remote(self.path) if(self.path.endswith('.git')): self.gitpath = self.path else: self.gitpath = os.path.join(self.path, '.git') if self.local and not os.path.exists(self.gitpath): os.makedirs(self.gitpath) def get_revs(self): """Fetches all revs from the remote. """ args = ["git", "ls-remote", self.gitpath] path = ".cached_revs" ofile = open(path, "w") child = subprocess.Popen(args, stdout=ofile) if child.wait() != 0: raise CalledProcessError output = open(path).readlines() self.revmap = dict(sanitize(i) for i in output) if "HEAD" in self.revmap: del self.revmap["HEAD"] self.revs = self.revmap.keys() ofile.close() def get_head(self): """Determines the head of a local repo. """ if not self.local: return path = os.path.join(self.gitpath, "HEAD") head = open(path).readline() self.head, _ = sanitize(head, ' ') 07070100055e90000081a40000000000000000000000014c5c5b3b000000a00000010000010036ffffffffffffffff0000004f00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.pyc bPLc@sdS(N((((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.pyts07070100055e98000081a40000000000000000000000014c5c5b3b00000aa80000010000010036ffffffffffffffff0000005000000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyc bPLc@sHddlZddlZddlmZmZdefdYZdS(iN(tdietwarnt NonLocalGitcBs2eZdZdZdZdZdZRS(s.Handler to interact with non-local repos. cCs ||_dS(s@Creates a new non-local handler for the specified repo. N(trepo(tselfR((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyt__init__ scCstjj|jj|d}tjj|r7|Stj|dddd|jj|g}tj |}|j dkrt n|S(s\Clones the non-local repo to base. Does nothing if a clone already exists. s.gittgittclones--bares--quieti( tostpathtjoinRt get_base_pathtexiststmakedirstgitpatht subprocesstPopentwaittCalledProcessError(RtbaseR targstchild((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyRs!  cCstjj|jj|d}tjj|sCtd|ndd|dd|jjg}tj |}|j dkrt ndd|dd d g}tj |}|j dkrt nd S( s8Updates checkout of the non-local repo in base. s.gitscould not find repo at %sRs --git-dir=tfetchs--quietis update-refsrefs/heads/mastert FETCH_HEADN( RR R RR R RRRRRR(RRR RR((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pytupdate&s! cCstjj|jj|d}tjj|sCtd|ndd|dd|jjg}tj |}|j dkrt ndS( s0Pushes from the non-local repo to base. s.gitscould not find repo at %sRs --git-dir=tpushs--quietiN( RR R RR R RRRRRR(RRR RR((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyR9s!(t__name__t __module__t__doc__RRRR(((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyRs    (RRtgit_remote_helpers.utilRRtobjectR(((sJ/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyts  07070100055e96000081a40000000000000000000000014c5c5b3b000006c40000010000010036ffffffffffffffff0000004f00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyc bPLc@s2ddlZddlZdefdYZdS(iNt GitImportercBs eZdZdZdZRS(sbAn importer for testgit repositories. This importer simply delegates to git fast-import. cCs ||_dS(s7Creates a new importer for the specified repo. N(trepo(tselfR((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyt__init__ scCs|jj|}|jjr-|jj}n!tjjtjj|d}tjjtjj|d}tjj|stj |ndd|ddd|g}tjj|r|j d|nt j |}|j d krtnd S( skImports a fast-import stream to the given directory. Simply delegates to git fast-import. s.gits git.markstgits --git-dir=s fast-imports--quiets--export-marks=s--import-marks=iN(Rt get_base_pathtlocaltgitpathtostpathtabspathtjointexiststmakedirstappendt subprocesstPopentwaittCalledProcessError(RtbasetdirnametgitdirR targstchild((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyt do_imports !!(t__name__t __module__t__doc__RR(((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyRs (RRtobjectR(((sI/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyts  07070100055e9a000081a40000000000000000000000014c5c5b3b00000c000000010000010036ffffffffffffffff0000004b00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyc bPLc@sGddlZddlZddZdZdefdYZdS(iNs cCsO|j|}|d}|dj}|jdrE|d}n||fS(s7Converts a for-each-ref line to a name/value pair. iis refs/heads/i (tsplittstript startswith(trevtseptsplitrevt branchvalt branchname((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pytsanitizes   cCs7dddg}x!|D]}|j|rtSqWtS(s8Checks whether the specified value is a remote url. thttptfiletgit(RtTruetFalse(turltprefixestprefix((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyt is_remotes  tGitRepocBs)eZdZdZdZdZRS(s%Repo object representing a repo. cCs||_d|_i|_t|j |_|jjdrO|j|_ntjj |jd|_|jrtjj |j rtj |jndS(s2Initializes a new repo at the given path. s.gitN( tpathtNonetheadtrevmapRtlocaltendswithtgitpathtostjointexiststmakedirs(tselfR((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyt__init__s   cCsdd|jg}d}t|d}tj|d|}|jdkrWtnt|j}td|D|_d|jkr|jd=n|jj |_ |j d S( s*Fetches all revs from the remote. R s ls-remotes .cached_revstwtstdouticss|]}t|VqdS(N(R(t.0ti((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pys <stHEADN( Rtopent subprocesstPopentwaittCalledProcessErrort readlinestdictRtkeystrevstclose(RtargsRtofiletchildtoutput((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pytget_revs0s  cCsS|js dStjj|jd}t|j}t|d\|_}dS(s-Determines the head of a local repo. NR$t ( RRRRRR%treadlineRR(RRRt_((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pytget_headBs  (t__name__t __module__t__doc__RR3R7(((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyRs  (RR&RRtobjectR(((sE/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/repo.pyts   07070100055e8f000081a40000000000000000000000014c5062ef000000000000010000010036ffffffffffffffff0000004e00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/__init__.py07070100055e95000081a40000000000000000000000014c5062ef000004340000010000010036ffffffffffffffff0000004e00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/importer.pyimport os import subprocess class GitImporter(object): """An importer for testgit repositories. This importer simply delegates to git fast-import. """ def __init__(self, repo): """Creates a new importer for the specified repo. """ self.repo = repo def do_import(self, base): """Imports a fast-import stream to the given directory. Simply delegates to git fast-import. """ dirname = self.repo.get_base_path(base) if self.repo.local: gitdir = self.repo.gitpath else: gitdir = os.path.abspath(os.path.join(dirname, '.git')) path = os.path.abspath(os.path.join(dirname, 'git.marks')) if not os.path.exists(dirname): os.makedirs(dirname) args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path] if os.path.exists(path): args.append("--import-marks=" + path) child = subprocess.Popen(args) if child.wait() != 0: raise CalledProcessError 07070100055e97000081a40000000000000000000000014c5062ef0000076b0000010000010036ffffffffffffffff0000004f00000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/non_local.pyimport os import subprocess from git_remote_helpers.util import die, warn class NonLocalGit(object): """Handler to interact with non-local repos. """ def __init__(self, repo): """Creates a new non-local handler for the specified repo. """ self.repo = repo def clone(self, base): """Clones the non-local repo to base. Does nothing if a clone already exists. """ path = os.path.join(self.repo.get_base_path(base), '.git') # already cloned if os.path.exists(path): return path os.makedirs(path) args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path] child = subprocess.Popen(args) if child.wait() != 0: raise CalledProcessError return path def update(self, base): """Updates checkout of the non-local repo in base. """ path = os.path.join(self.repo.get_base_path(base), '.git') if not os.path.exists(path): die("could not find repo at %s", path) args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath] child = subprocess.Popen(args) if child.wait() != 0: raise CalledProcessError args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"] child = subprocess.Popen(args) if child.wait() != 0: raise CalledProcessError def push(self, base): """Pushes from the non-local repo to base. """ path = os.path.join(self.repo.get_base_path(base), '.git') if not os.path.exists(path): die("could not find repo at %s", path) args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath] child = subprocess.Popen(args) if child.wait() != 0: raise CalledProcessError 07070100055e93000081a40000000000000000000000014c5062ef000060840000010000010036ffffffffffffffff0000004900000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/git/git.py#!/usr/bin/env python """Functionality for interacting with Git repositories. This module provides classes for interfacing with a Git repository. """ import os import re import time from binascii import hexlify from cStringIO import StringIO import unittest from git_remote_helpers.util import debug, error, die, start_command, run_command def get_git_dir (): """Return the path to the GIT_DIR for this repo.""" args = ("git", "rev-parse", "--git-dir") exit_code, output, errors = run_command(args) if exit_code: die("Failed to retrieve git dir") assert not errors return output.strip() def parse_git_config (): """Return a dict containing the parsed version of 'git config -l'.""" exit_code, output, errors = run_command(("git", "config", "-z", "-l")) if exit_code: die("Failed to retrieve git configuration") assert not errors return dict([e.split('\n', 1) for e in output.split("\0") if e]) def git_config_bool (value): """Convert the given git config string value to True or False. Raise ValueError if the given string was not recognized as a boolean value. """ norm_value = str(value).strip().lower() if norm_value in ("true", "1", "yes", "on", ""): return True if norm_value in ("false", "0", "no", "off", "none"): return False raise ValueError("Failed to parse '%s' into a boolean value" % (value)) def valid_git_ref (ref_name): """Return True iff the given ref name is a valid git ref name.""" # The following is a reimplementation of the git check-ref-format # command. The rules were derived from the git check-ref-format(1) # manual page. This code should be replaced by a call to # check_ref_format() in the git library, when such is available. if ref_name.endswith('/') or \ ref_name.startswith('.') or \ ref_name.count('/.') or \ ref_name.count('..') or \ ref_name.endswith('.lock'): return False for c in ref_name: if ord(c) < 0x20 or ord(c) == 0x7f or c in " ~^:?*[": return False return True class GitObjectFetcher(object): """Provide parsed access to 'git cat-file --batch'. This provides a read-only interface to the Git object database. """ def __init__ (self): """Initiate a 'git cat-file --batch' session.""" self.queue = [] # List of object names to be submitted self.in_transit = None # Object name currently in transit # 'git cat-file --batch' produces binary output which is likely # to be corrupted by the default "rU"-mode pipe opened by # start_command. (Mode == "rU" does universal new-line # conversion, which mangles carriage returns.) Therefore, we # open an explicitly binary-safe pipe for transferring the # output from 'git cat-file --batch'. pipe_r_fd, pipe_w_fd = os.pipe() pipe_r = os.fdopen(pipe_r_fd, "rb") pipe_w = os.fdopen(pipe_w_fd, "wb") self.proc = start_command(("git", "cat-file", "--batch"), stdout = pipe_w) self.f = pipe_r def __del__ (self): """Verify completed communication with 'git cat-file --batch'.""" assert not self.queue assert self.in_transit is None self.proc.stdin.close() assert self.proc.wait() == 0 # Zero exit code assert self.f.read() == "" # No remaining output def _submit_next_object (self): """Submit queue items to the 'git cat-file --batch' process. If there are items in the queue, and there is currently no item currently in 'transit', then pop the first item off the queue, and submit it. """ if self.queue and self.in_transit is None: self.in_transit = self.queue.pop(0) print >> self.proc.stdin, self.in_transit[0] def push (self, obj, callback): """Push the given object name onto the queue. The given callback function will at some point in the future be called exactly once with the following arguments: - self - this GitObjectFetcher instance - obj - the object name provided to push() - sha1 - the SHA1 of the object, if 'None' obj is missing - t - the type of the object (tag/commit/tree/blob) - size - the size of the object in bytes - data - the object contents """ self.queue.append((obj, callback)) self._submit_next_object() # (Re)start queue processing def process_next_entry (self): """Read the next entry off the queue and invoke callback.""" obj, cb = self.in_transit self.in_transit = None header = self.f.readline() if header == "%s missing\n" % (obj): cb(self, obj, None, None, None, None) return sha1, t, size = header.split(" ") assert len(sha1) == 40 assert t in ("tag", "commit", "tree", "blob") assert size.endswith("\n") size = int(size.strip()) data = self.f.read(size) assert self.f.read(1) == "\n" cb(self, obj, sha1, t, size, data) self._submit_next_object() def process (self): """Process the current queue until empty.""" while self.in_transit is not None: self.process_next_entry() # High-level convenience methods: def get_sha1 (self, objspec): """Return the SHA1 of the object specified by 'objspec'. Return None if 'objspec' does not specify an existing object. """ class _ObjHandler(object): """Helper class for getting the returned SHA1.""" def __init__ (self, parser): self.parser = parser self.sha1 = None def __call__ (self, parser, obj, sha1, t, size, data): # FIXME: Many unused arguments. Could this be cheaper? assert parser == self.parser self.sha1 = sha1 handler = _ObjHandler(self) self.push(objspec, handler) self.process() return handler.sha1 def open_obj (self, objspec): """Return a file object wrapping the contents of a named object. The caller is responsible for calling .close() on the returned file object. Raise KeyError if 'objspec' does not exist in the repo. """ class _ObjHandler(object): """Helper class for parsing the returned git object.""" def __init__ (self, parser): """Set up helper.""" self.parser = parser self.contents = StringIO() self.err = None def __call__ (self, parser, obj, sha1, t, size, data): """Git object callback (see GitObjectFetcher documentation).""" assert parser == self.parser if not sha1: # Missing object self.err = "Missing object '%s'" % obj else: assert size == len(data) self.contents.write(data) handler = _ObjHandler(self) self.push(objspec, handler) self.process() if handler.err: raise KeyError(handler.err) handler.contents.seek(0) return handler.contents def walk_tree (self, tree_objspec, callback, prefix = ""): """Recursively walk the given Git tree object. Recursively walk all subtrees of the given tree object, and invoke the given callback passing three arguments: (path, mode, data) with the path, permission bits, and contents of all the blobs found in the entire tree structure. """ class _ObjHandler(object): """Helper class for walking a git tree structure.""" def __init__ (self, parser, cb, path, mode = None): """Set up helper.""" self.parser = parser self.cb = cb self.path = path self.mode = mode self.err = None def parse_tree (self, treedata): """Parse tree object data, yield tree entries. Each tree entry is a 3-tuple (mode, sha1, path) self.path is prepended to all paths yielded from this method. """ while treedata: mode = int(treedata[:6], 10) # Turn 100xxx into xxx if mode > 100000: mode -= 100000 assert treedata[6] == " " i = treedata.find("\0", 7) assert i > 0 path = treedata[7:i] sha1 = hexlify(treedata[i + 1: i + 21]) yield (mode, sha1, self.path + path) treedata = treedata[i + 21:] def __call__ (self, parser, obj, sha1, t, size, data): """Git object callback (see GitObjectFetcher documentation).""" assert parser == self.parser if not sha1: # Missing object self.err = "Missing object '%s'" % (obj) return assert size == len(data) if t == "tree": if self.path: self.path += "/" # Recurse into all blobs and subtrees for m, s, p in self.parse_tree(data): parser.push(s, self.__class__(self.parser, self.cb, p, m)) elif t == "blob": self.cb(self.path, self.mode, data) else: raise ValueError("Unknown object type '%s'" % (t)) self.push(tree_objspec, _ObjHandler(self, callback, prefix)) self.process() class GitRefMap(object): """Map Git ref names to the Git object names they currently point to. Behaves like a dictionary of Git ref names -> Git object names. """ def __init__ (self, obj_fetcher): """Create a new Git ref -> object map.""" self.obj_fetcher = obj_fetcher self._cache = {} # dict: refname -> objname def _load (self, ref): """Retrieve the object currently bound to the given ref. The name of the object pointed to by the given ref is stored into this mapping, and also returned. """ if ref not in self._cache: self._cache[ref] = self.obj_fetcher.get_sha1(ref) return self._cache[ref] def __contains__ (self, refname): """Return True if the given refname is present in this cache.""" return bool(self._load(refname)) def __getitem__ (self, refname): """Return the git object name pointed to by the given refname.""" commit = self._load(refname) if commit is None: raise KeyError("Unknown ref '%s'" % (refname)) return commit def get (self, refname, default = None): """Return the git object name pointed to by the given refname.""" commit = self._load(refname) if commit is None: return default return commit class GitFICommit(object): """Encapsulate the data in a Git fast-import commit command.""" SHA1RE = re.compile(r'^[0-9a-f]{40}$') @classmethod def parse_mode (cls, mode): """Verify the given git file mode, and return it as a string.""" assert mode in (644, 755, 100644, 100755, 120000) return "%i" % (mode) @classmethod def parse_objname (cls, objname): """Return the given object name (or mark number) as a string.""" if isinstance(objname, int): # Object name is a mark number assert objname > 0 return ":%i" % (objname) # No existence check is done, only checks for valid format assert cls.SHA1RE.match(objname) # Object name is valid SHA1 return objname @classmethod def quote_path (cls, path): """Return a quoted version of the given path.""" path = path.replace("\\", "\\\\") path = path.replace("\n", "\\n") path = path.replace('"', '\\"') return '"%s"' % (path) @classmethod def parse_path (cls, path): """Verify that the given path is valid, and quote it, if needed.""" assert not isinstance(path, int) # Cannot be a mark number # These checks verify the rules on the fast-import man page assert not path.count("//") assert not path.endswith("/") assert not path.startswith("/") assert not path.count("/./") assert not path.count("/../") assert not path.endswith("/.") assert not path.endswith("/..") assert not path.startswith("./") assert not path.startswith("../") if path.count('"') + path.count('\n') + path.count('\\'): return cls.quote_path(path) return path def __init__ (self, name, email, timestamp, timezone, message): """Create a new Git fast-import commit, with the given metadata.""" self.name = name self.email = email self.timestamp = timestamp self.timezone = timezone self.message = message self.pathops = [] # List of path operations in this commit def modify (self, mode, blobname, path): """Add a file modification to this Git fast-import commit.""" self.pathops.append(("M", self.parse_mode(mode), self.parse_objname(blobname), self.parse_path(path))) def delete (self, path): """Add a file deletion to this Git fast-import commit.""" self.pathops.append(("D", self.parse_path(path))) def copy (self, path, newpath): """Add a file copy to this Git fast-import commit.""" self.pathops.append(("C", self.parse_path(path), self.parse_path(newpath))) def rename (self, path, newpath): """Add a file rename to this Git fast-import commit.""" self.pathops.append(("R", self.parse_path(path), self.parse_path(newpath))) def note (self, blobname, commit): """Add a note object to this Git fast-import commit.""" self.pathops.append(("N", self.parse_objname(blobname), self.parse_objname(commit))) def deleteall (self): """Delete all files in this Git fast-import commit.""" self.pathops.append("deleteall") class TestGitFICommit(unittest.TestCase): """GitFICommit selftests.""" def test_basic (self): """GitFICommit basic selftests.""" def expect_fail (method, data): """Verify that the method(data) raises an AssertionError.""" try: method(data) except AssertionError: return raise AssertionError("Failed test for invalid data '%s(%s)'" % (method.__name__, repr(data))) def test_parse_mode (self): """GitFICommit.parse_mode() selftests.""" self.assertEqual(GitFICommit.parse_mode(644), "644") self.assertEqual(GitFICommit.parse_mode(755), "755") self.assertEqual(GitFICommit.parse_mode(100644), "100644") self.assertEqual(GitFICommit.parse_mode(100755), "100755") self.assertEqual(GitFICommit.parse_mode(120000), "120000") self.assertRaises(AssertionError, GitFICommit.parse_mode, 0) self.assertRaises(AssertionError, GitFICommit.parse_mode, 123) self.assertRaises(AssertionError, GitFICommit.parse_mode, 600) self.assertRaises(AssertionError, GitFICommit.parse_mode, "644") self.assertRaises(AssertionError, GitFICommit.parse_mode, "abc") def test_parse_objname (self): """GitFICommit.parse_objname() selftests.""" self.assertEqual(GitFICommit.parse_objname(1), ":1") self.assertRaises(AssertionError, GitFICommit.parse_objname, 0) self.assertRaises(AssertionError, GitFICommit.parse_objname, -1) self.assertEqual(GitFICommit.parse_objname("0123456789" * 4), "0123456789" * 4) self.assertEqual(GitFICommit.parse_objname("2468abcdef" * 4), "2468abcdef" * 4) self.assertRaises(AssertionError, GitFICommit.parse_objname, "abcdefghij" * 4) def test_parse_path (self): """GitFICommit.parse_path() selftests.""" self.assertEqual(GitFICommit.parse_path("foo/bar"), "foo/bar") self.assertEqual(GitFICommit.parse_path("path/with\n and \" in it"), '"path/with\\n and \\" in it"') self.assertRaises(AssertionError, GitFICommit.parse_path, 1) self.assertRaises(AssertionError, GitFICommit.parse_path, 0) self.assertRaises(AssertionError, GitFICommit.parse_path, -1) self.assertRaises(AssertionError, GitFICommit.parse_path, "foo//bar") self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/") self.assertRaises(AssertionError, GitFICommit.parse_path, "/foo/bar") self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/./bar") self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/../bar") self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/.") self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/..") self.assertRaises(AssertionError, GitFICommit.parse_path, "./foo/bar") self.assertRaises(AssertionError, GitFICommit.parse_path, "../foo/bar") class GitFastImport(object): """Encapsulate communication with git fast-import.""" def __init__ (self, f, obj_fetcher, last_mark = 0): """Set up self to communicate with a fast-import process through f.""" self.f = f # File object where fast-import stream is written self.obj_fetcher = obj_fetcher # GitObjectFetcher instance self.next_mark = last_mark + 1 # Next mark number self.refs = set() # Keep track of the refnames we've seen def comment (self, s): """Write the given comment in the fast-import stream.""" assert "\n" not in s, "Malformed comment: '%s'" % (s) self.f.write("# %s\n" % (s)) def commit (self, ref, commitdata): """Make a commit on the given ref, with the given GitFICommit. Return the mark number identifying this commit. """ self.f.write("""\ commit %(ref)s mark :%(mark)i committer %(name)s <%(email)s> %(timestamp)i %(timezone)s data %(msgLength)i %(msg)s """ % { 'ref': ref, 'mark': self.next_mark, 'name': commitdata.name, 'email': commitdata.email, 'timestamp': commitdata.timestamp, 'timezone': commitdata.timezone, 'msgLength': len(commitdata.message), 'msg': commitdata.message, }) if ref not in self.refs: self.refs.add(ref) parent = ref + "^0" if self.obj_fetcher.get_sha1(parent): self.f.write("from %s\n" % (parent)) for op in commitdata.pathops: self.f.write(" ".join(op)) self.f.write("\n") self.f.write("\n") retval = self.next_mark self.next_mark += 1 return retval def blob (self, data): """Import the given blob. Return the mark number identifying this blob. """ self.f.write("blob\nmark :%i\ndata %i\n%s\n" % (self.next_mark, len(data), data)) retval = self.next_mark self.next_mark += 1 return retval def reset (self, ref, objname): """Reset the given ref to point at the given Git object.""" self.f.write("reset %s\nfrom %s\n\n" % (ref, GitFICommit.parse_objname(objname))) if ref not in self.refs: self.refs.add(ref) class GitNotes(object): """Encapsulate access to Git notes. Simulates a dictionary of object name (SHA1) -> Git note mappings. """ def __init__ (self, notes_ref, obj_fetcher): """Create a new Git notes interface, bound to the given notes ref.""" self.notes_ref = notes_ref self.obj_fetcher = obj_fetcher # Used to get objects from repo self.imports = [] # list: (objname, note data blob name) tuples def __del__ (self): """Verify that self.commit_notes() was called before destruction.""" if self.imports: error("Missing call to self.commit_notes().") error("%i notes are not committed!", len(self.imports)) def _load (self, objname): """Return the note data associated with the given git object. The note data is returned in string form. If no note is found for the given object, None is returned. """ try: f = self.obj_fetcher.open_obj("%s:%s" % (self.notes_ref, objname)) ret = f.read() f.close() except KeyError: ret = None return ret def __getitem__ (self, objname): """Return the note contents associated with the given object. Raise KeyError if given object has no associated note. """ blobdata = self._load(objname) if blobdata is None: raise KeyError("Object '%s' has no note" % (objname)) return blobdata def get (self, objname, default = None): """Return the note contents associated with the given object. Return given default if given object has no associated note. """ blobdata = self._load(objname) if blobdata is None: return default return blobdata def import_note (self, objname, data, gfi): """Tell git fast-import to store data as a note for objname. This method uses the given GitFastImport object to create a blob containing the given note data. Also an entry mapping the given object name to the created blob is stored until commit_notes() is called. Note that this method only works if it is later followed by a call to self.commit_notes() (which produces the note commit that refers to the blob produced here). """ if not data.endswith("\n"): data += "\n" gfi.comment("Importing note for object %s" % (objname)) mark = gfi.blob(data) self.imports.append((objname, mark)) def commit_notes (self, gfi, author, message): """Produce a git fast-import note commit for the imported notes. This method uses the given GitFastImport object to create a commit on the notes ref, introducing the notes previously submitted to import_note(). """ if not self.imports: return commitdata = GitFICommit(author[0], author[1], time.time(), "0000", message) for objname, blobname in self.imports: assert isinstance(objname, int) and objname > 0 assert isinstance(blobname, int) and blobname > 0 commitdata.note(blobname, objname) gfi.commit(self.notes_ref, commitdata) self.imports = [] class GitCachedNotes(GitNotes): """Encapsulate access to Git notes (cached version). Only use this class if no caching is done at a higher level. Simulates a dictionary of object name (SHA1) -> Git note mappings. """ def __init__ (self, notes_ref, obj_fetcher): """Set up a caching wrapper around GitNotes.""" GitNotes.__init__(self, notes_ref, obj_fetcher) self._cache = {} # Cache: object name -> note data def __del__ (self): """Verify that GitNotes' destructor is called.""" GitNotes.__del__(self) def _load (self, objname): """Extend GitNotes._load() with a local objname -> note cache.""" if objname not in self._cache: self._cache[objname] = GitNotes._load(self, objname) return self._cache[objname] def import_note (self, objname, data, gfi): """Extend GitNotes.import_note() with a local objname -> note cache.""" if not data.endswith("\n"): data += "\n" assert objname not in self._cache self._cache[objname] = data GitNotes.import_note(self, objname, data, gfi) if __name__ == '__main__': unittest.main() 07070100055e9c000081a40000000000000000000000014c5c5b3b00001f470000010000010036ffffffffffffffff0000004700000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyc bPLc@sdZddlZddlZddlZeZdZdZdZdZ dZ de fd YZ dedejejejd Zdeded Zed Zd ZdS(sMisc. useful functionality used by the rest of this package. This module provides common functionality used by the other modules in this package. iNcGstj||IJdS(sPrint a message to stderr.N(tsyststderr(tmsgtargs((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytnotifyscGstrtj||IJndS(s6Print a debug message to stderr when DEBUG is enabled.N(tDEBUGRR(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytdebugscGstjdI||IJdS(s!Print an error message to stderr.sERROR:N(RR(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyterrorscGstjdI||IJdS(s"Print a warning message to stderr.swarning:N(RR(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytwarnscGst||tjddS(s6Print as error message to stderr and exit the program.iN(RRtexit(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytdie#s tProgressIndicatorcBsVeZdZd ZdejdZdZdZd e d Z d e d Z RS(sSimple progress indicator. Displayed as a spinning character by default, but can be customized by passing custom messages that overrides the spinning character. t|t/t-s\tcCs1d|_||_d|_||_g|_dS(s?Create a new ProgressIndicator, bound to the given file object.iN(tntftprev_lentprefixt prefix_lens(tselfRR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt__init__4s     cCs,|jjt|j|j|7_dS(s.Append the given prefix onto the prefix stack.N(RtappendtlenR(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt pushprefix<scCs#|jj}|j| |_dS(s-Remove the last prefix from the prefix stack.N(RtpopR(RR((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt popprefixAscCs|dkr,|j|jt|j}n|j|}|jd|j|fIt|j|_|r|jJd|_n|jd7_dS(s2Indicate progress, possibly with a custom message.s %-*siiN(tNonetStatesRRRRRt expandtabs(RRtlf((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt__call__Fs    tdonecCs#|rd|_n||tdS(s4Finalize progress indication with the given message.RN(RtTrue(RRtnoprefix((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytfinishRs (R R Rs\N( t__name__t __module__t__doc__RRtstdoutRRRRtFalseR R$(((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyR )s   cCsnd }|d k r1tjj}|j|ntj|ddd|d|d|d|d|d|d tS( sStart the given command, and return a subprocess object. This provides a simpler interface to the subprocess module. tbufsizeitstdinR(Rtcwdtshelltenvtuniversal_newlinesN(Rtostenvirontcopytupdatet subprocesstPopenR"(RR,R-tadd_envR+R(RR.((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt start_commandYs c Cst||||}|j\}}|j}|rX|rXtddj||n|r|rtddj||n|||fS(sRun the given command to completion, and return its results. This provides a simpler interface to the subprocess module. The results are formatted as a 3-tuple: (exit_code, output, errors) If flag_error is enabled, Error messages will be produced if the subprocess terminated with a non-zero exit code and/or stderr output. The other arguments are passed on to start_command(). s'%s' returned errors: --- %s---t s'%s' returned exit code %i(R7t communicatet returncodeRtjoin( RR,R-R6t flag_errortprocesstoutputterrorst exit_code((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt run_commandjs   csfd}|S(sDecorator for simplifying reading of files. If missing_ok is True, a failure to open a file for reading will not raise the usual IOError, but instead the wrapped method will be called with f == None. The method must in this case properly handle f == None. csfd}|S(s9Teach given method to handle both filenames and file objects. The given method must take a file object as its second argument (the first argument being 'self', of course). This decorator will take a filename given as the second argument and promote it to a file object. cst|tr|}n9yt|d}Wn#tk rPrJd}qQnXz||||SWdt|t r|r|jnXdS(Ntr(t isinstancetfiletopentIOErrorRtclose(RtfilenameRtkwargsR(t missing_oktmethod(sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt_wrapped_methods   ((RKRL(RJ(RKsA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt_wraps ((RJRM((RJsA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytfile_reader_methods csfd}|S(s\Decorator for simplifying writing of files. Enables the given method to handle both filenames and file objects. The given method must take a file object as its second argument (the first argument being 'self', of course). This decorator will take a filename given as the second argument and promote it to a file object. cst|tr|}nCtjj|}tjj|sLtj|nt|d}z||||SWdt|ts|jnXdS(Ntw( RCRDR0tpathtdirnametisdirtmakedirsRERG(RRHRRIRt parent_dir(RK(sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyt _new_methods ((RKRU((RKsA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pytfile_writer_methods (R'RR0R4R)RRRRRR tobjectR RtPIPER7R"RARNRV(((sA/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.pyts"        0     &07070100055e9b000081a40000000000000000000000014c5062ef000018bd0000010000010036ffffffffffffffff0000004600000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers/util.py#!/usr/bin/env python """Misc. useful functionality used by the rest of this package. This module provides common functionality used by the other modules in this package. """ import sys import os import subprocess # Whether or not to show debug messages DEBUG = False def notify(msg, *args): """Print a message to stderr.""" print >> sys.stderr, msg % args def debug (msg, *args): """Print a debug message to stderr when DEBUG is enabled.""" if DEBUG: print >> sys.stderr, msg % args def error (msg, *args): """Print an error message to stderr.""" print >> sys.stderr, "ERROR:", msg % args def warn(msg, *args): """Print a warning message to stderr.""" print >> sys.stderr, "warning:", msg % args def die (msg, *args): """Print as error message to stderr and exit the program.""" error(msg, *args) sys.exit(1) class ProgressIndicator(object): """Simple progress indicator. Displayed as a spinning character by default, but can be customized by passing custom messages that overrides the spinning character. """ States = ("|", "/", "-", "\\") def __init__ (self, prefix = "", f = sys.stdout): """Create a new ProgressIndicator, bound to the given file object.""" self.n = 0 # Simple progress counter self.f = f # Progress is written to this file object self.prev_len = 0 # Length of previous msg (to be overwritten) self.prefix = prefix # Prefix prepended to each progress message self.prefix_lens = [] # Stack of prefix string lengths def pushprefix (self, prefix): """Append the given prefix onto the prefix stack.""" self.prefix_lens.append(len(self.prefix)) self.prefix += prefix def popprefix (self): """Remove the last prefix from the prefix stack.""" prev_len = self.prefix_lens.pop() self.prefix = self.prefix[:prev_len] def __call__ (self, msg = None, lf = False): """Indicate progress, possibly with a custom message.""" if msg is None: msg = self.States[self.n % len(self.States)] msg = self.prefix + msg print >> self.f, "\r%-*s" % (self.prev_len, msg), self.prev_len = len(msg.expandtabs()) if lf: print >> self.f self.prev_len = 0 self.n += 1 def finish (self, msg = "done", noprefix = False): """Finalize progress indication with the given message.""" if noprefix: self.prefix = "" self(msg, True) def start_command (args, cwd = None, shell = False, add_env = None, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE): """Start the given command, and return a subprocess object. This provides a simpler interface to the subprocess module. """ env = None if add_env is not None: env = os.environ.copy() env.update(add_env) return subprocess.Popen(args, bufsize = 1, stdin = stdin, stdout = stdout, stderr = stderr, cwd = cwd, shell = shell, env = env, universal_newlines = True) def run_command (args, cwd = None, shell = False, add_env = None, flag_error = True): """Run the given command to completion, and return its results. This provides a simpler interface to the subprocess module. The results are formatted as a 3-tuple: (exit_code, output, errors) If flag_error is enabled, Error messages will be produced if the subprocess terminated with a non-zero exit code and/or stderr output. The other arguments are passed on to start_command(). """ process = start_command(args, cwd, shell, add_env) (output, errors) = process.communicate() exit_code = process.returncode if flag_error and errors: error("'%s' returned errors:\n---\n%s---", " ".join(args), errors) if flag_error and exit_code: error("'%s' returned exit code %i", " ".join(args), exit_code) return (exit_code, output, errors) def file_reader_method (missing_ok = False): """Decorator for simplifying reading of files. If missing_ok is True, a failure to open a file for reading will not raise the usual IOError, but instead the wrapped method will be called with f == None. The method must in this case properly handle f == None. """ def _wrap (method): """Teach given method to handle both filenames and file objects. The given method must take a file object as its second argument (the first argument being 'self', of course). This decorator will take a filename given as the second argument and promote it to a file object. """ def _wrapped_method (self, filename, *args, **kwargs): if isinstance(filename, file): f = filename else: try: f = open(filename, 'r') except IOError: if missing_ok: f = None else: raise try: return method(self, f, *args, **kwargs) finally: if not isinstance(filename, file) and f: f.close() return _wrapped_method return _wrap def file_writer_method (method): """Decorator for simplifying writing of files. Enables the given method to handle both filenames and file objects. The given method must take a file object as its second argument (the first argument being 'self', of course). This decorator will take a filename given as the second argument and promote it to a file object. """ def _new_method (self, filename, *args, **kwargs): if isinstance(filename, file): f = filename else: # Make sure the containing directory exists parent_dir = os.path.dirname(filename) if not os.path.isdir(parent_dir): os.makedirs(parent_dir) f = open(filename, 'w') try: return method(self, f, *args, **kwargs) finally: if not isinstance(filename, file): f.close() return _new_method 07070100055e8a000081a40000000000000000000000014c5c5b3b0000010f0000010000010036ffffffffffffffff0000005300000000root/usr/local/lib/python2.7/site-packages/git_remote_helpers-0.1.0-py2.7.egg-infoMetadata-Version: 1.0 Name: git_remote_helpers Version: 0.1.0 Summary: Git remote helper program for non-git repositories Home-page: http://www.git-scm.com/ Author: The Git Community Author-email: git@vger.kernel.org License: GPLv2 Description: UNKNOWN Platform: UNKNOWN 07070100055ea7000041ed0000000000000000000000034c5c5c81000000000000010000010036ffffffffffffffff0000001700000000root/usr/local/libexec07070100055ea8000041ed0000000000000000000000024c5c5c83000000000000010000010036ffffffffffffffff0000002000000000root/usr/local/libexec/git-core07070100055eb3000081ed0000000000000000000000014c5c5b36004299d00000010000010036ffffffffffffffff0000003000000000root/usr/local/libexec/git-core/git-fast-importELF+"@B@8@! @@Xjjp#p LKh$Kh/usr/lib/sparcv9/ld.so.1QC !"$%&()*+-/0123456789<=>BCDEFGIKLPQSTUWXYZ[\]^_`abfjklmoqrtuvwxy{}~    "#%&()*+,-./4567:<=?@ADFGIJKLMNOPQTUVXY[\^`ceghiklmnpqrtuvz{}~    "$%'()*,-0135678:<=>?@BDEFIKMNOPRTVXYZ\]`bcegiklmnoqtuvwxyz|}~      !#&'*,-01234568:<=>@BEJKMNPTVWXZ[\]^_`aceghijlnopqruwx{}~   !"#&()*,-.013589:=>ACDEGIKLMOPQRTWXY[]_abdefhijlmpqrstvyz{|}      !"#$%&')+.1679:<=?@AB  #',.:;?@AHJMNORVcdeghinpsz|  !$'012389;>BCEHRSWZ]_abdfjoswxy|  !#&+./249;ACGHJLQSUW[^_adfhjprs{"$%()+./79;?ACDFGHILOQRSUYbdfkmstvyz|   $%'+/2467;<?@BFHJNSUVZ\^`cgknouwx~(*,-/023458;>$:   $G+ VH`@-}L V$Ij \<x$M #f  P8,$C  ih'$>-$T@8A$;H *`([ (th,~x  <$RH -X | iP (,8J  <8\  Pl$>@r-P0 #  l  H PD$ND$BM Uh #~-uH8$N Y$ Cd R T,) 09  pP-Po V@|  h,$9   D  Xx-G KX  Ę4! |3,S @`a$?g  ̨y (p, ŘD Op$E@$;`$H l$U-s1  @$Tl$R$N-u NH, (\  ; <$B  @ \x&  P80$B@9$H ?-uQ-~p S|-y 8$T ,,y  hP%-XF$<`P k$Rv  0p-~ @-t,,  +$A@ $, F, a$< n !x  \ $D  R  -8  h -t $I`  Š4 ,q  (-s B ` Y$> `$C@ i Ę s (PP $F@ -P8 -~8P , $O  H (  @ 1   = X X Ut K$A R ` _$D` f  q , $:`  F\   a  P  X  @@ -~0  g -y   H 9 GH D E p M$I V, r$:@ x#p  8 -/  )0 -t $X  p  $ 5 v J X ^-y r X $@    &h\   P O$A  8 $M $< $F`  x$H@& hT5 MPF$eHc$OH-G   ), H  %P  Pp 0 P  ' Zx5 ]B +  s \Z  e(|b n0r   0  $R,$P8-X l$Rh$D  T,z5 hpJ-yR$DY$R@m )|  ,(U$U ڀ-i$F  ld-G# `1  PPS Yf$T  |,~P ! l S@,y(-P@N  _ t`<u$A$Q88-j$R  pX ,l-}$R(*$=@4,pO  ]$Oy T@$R8  L< Vp @(-~ t\ ! 0`+ L> (G 5xX Xk-sx  `   0   ; "0$@@    ",?  `DL$: T-Xy$W$R | ,h p$Kh (  lPP @T `   ' <> NHH  `4\ f 0|z,l$Mp$D  ܐH$H,q $; p$< $U1-OJ$R<^ l z $Vp"$K@    -P$9   /$^ M n0X #@q 0   CxH$lx HH p V$H (  8X G+ PD7 I `V ^he,~p u0 C``  9  ,~ -} W8( H6  j,?$C`E$R`\ hj    x ,$l @ t8 |h 8,4  TB PP  [  Xk 0v ,$l  4 $G@$U-G$R|% J-$H2 ǀ-~B M l[ K8l D 8 P8,   Qx  # V3 7C 4h[  8f 8@| a0 H,~h m @ $l$^x4 (X&$FB x4Q \e cw$`  l O  D$N  8  @ X g8d,$TT$EY  8tc  <w$C  C j \0 L   t  $U .$?@ :-u@ _ : l  u,` $U $N ,~ $H` -X!-a!:$V!L$b!{ ,!  4H! S`H!$X!  k|! I <!  nD"  s(d"-C"D$>"K-?"l " s" :@\" ("  .P"$M(" ","$X# ֨L#+  8#@ p#Q,p#|$W# j# \#  <#$E# ^h`# m( #-G$ ,yp$' ll$9 W$E  $T$XP$u qh4$-G$-7$ yxH$ xl$  _`%$N(%/,y@%T  H%f$=%k-G% % '% %-t%  |% % &,~&' dd&3 ,&A$l&\ ,@ &v$D &~  pt&-G&  Հ& Tp-~ P&$U& &$M' H'<$A'C  'T 'o  ' H',~T',','$lp'-s($-3(X  (c p(q-P($8(  zh,(,~0( Ix(($T(($>)$R4) ,$Rp))$J@)2 X)D,~X)a  )p$O)  ) F) ݐ) b)-P)  ``*,*5 P*E-X*s  *-X* N,*  \*$M* * x*$@+$l+',+F  4+^ X8+y-P+  @\+ W8`+ <(+  p+$O,  p,  pH,($8,B,S !@,~$T, X U@l,,~$,-3,-5@- @-6 -I-`-u$V- )l-$?`-,~t- .   .$XM$D@.4  XX.M ;`.b,:  \. (L. ]p.  xL. K@. 6@.$I@.$X/-X/, /5$RP/L (/f /v  /$E /,y/ `/ L</$;/  0/$D/!$Mh/ \PD0   (0$J0'  L02 k0O (Pt0_$T0$G($I0 <0  `x0$:0 M@0 (0  px0 ]1 6p@1$_(13  ψ1C  1O 1d$l1{ F 1 zH1,l1  1 1$G 1 .2 @|2$F2 d21 )2D   2X$;2^ 2o$i2$@2-a2 x2$92 V2  52  3 xd3 $< 3' X37-t3V-O3q H3| 9P<3$@`3 f`3-u3 ذ3-P 3$f4 Vx4  T 4, 8,4<  4I-u4h  X4 54 `4, 4-G4 s45$<'% pD5 $R5$ 5F$E5M-P5e  P!  <5o$H5w 5 R`5-/5 5 X@5 - l5,yx@6,6@ @ $J 6S !0t6b @6k ƀ86r$T6-a6 ex6 L6 6 &`7 [@L7) 8|7:$Zp7d @7p #r7,7-u(7  PL7,q$8 +8+$Tx 8;$B` s$J8A-~8T-u8c$J8j ||8w  <8-P8-~8 8,8$R09-u9 $jH9$? /$Mh9& x91 T9>-PX9]  8X9m$O9 049  `9 ax9 9 9$U9  \:   0: Ȱd:2-}:M NH:X w(L:b  d:j :x-j:  :$O0: Vh: pl;  T8; Rpd;+ ]t;9  H;L;R-t;n hH5$;@;  ; @;,@;  K$J; 2<  Ԁ<  <,,0l=-O=9-~ =R 7`=g$Qp`= X = @\= u\=$M=  @=  Px>  `v VXl>-}0>" zx>/ >@ Ǩ>T  H>^ OHL>f j>r  HT>~$X> ^D>  !>$g>$G> L?  ?$N ?,$R??,?g  H?u,?,l? `? X? &d?  @$RD@$Ox@.  <@3 @F @a$8@h,Y@$\@  t@ d@$UA UA A4-yAO ShAa #@A  A  A$T A A$bB$9`B  xB5-shBZ  ѸBq (Bw kPB$fB PB$SB j(B$ZPC  xC 8C%$IC8 Y(CI 8C\ PCk-7@@C C QC ]`C$?C bC zH>_$A`C C-aD  @PD$lD=$MD_$lH(D$lD,lD LD  D$J`D,E ) pE"  xxEA QEG,qEb$NE DXE-~E <E W(E 2(E E E$WF  lF F"$NF<$WFQ-GFu$C F{ F ,F  3pF  kF$WF$fF$Sp W "G$$TGJ iG\$IGk-3)G$MG <G 3@G + G-uH TH ¸pH$FH% @H2 ,pHF  pHV X(Hj vTHy H$=H$BH$FH {H  H  'H e@H 1( F$;I  +I, [`I8$FIF  8xIU  I_ t\Is I I  #pI XI-PI I wHI,hJ$?J#   XJ1  X FHJF Ja hJo J} (J xtJ-PPJ$RtJ !J K @tK xK K2 (KD 10,K\  Kq$B Kx$RXK-~K +K,xK (K tK  L 0L l0XL0  xL?-tL^ \pLr-tL hL bL$R($@ L$V*$AL pL$@L,L 8M -GM)$_ML,HMr,qM-PM$bM-Px: M$XN,N>-t<Ni $Nw _N  N-uN$QXN-/N clN$lpO kO' q O:,u$# $X0Oa,lO{ O,~LO-iO hO |O$NpP-sP0,PP  Pg 0@Pz r`P-iP, $GP  PP,2P$WQ  gQ,8Q: QK QY @%$> Qn  c Q~,Q$8Q jQ$Z0Q$fR-}R%,RF <RU$JRb,lR$:R-\R$A R +R (<R$OR$S S$?S  S),~SH-GSn  TS,qS$>`S |S [S `S S-i T  0T0$=T8 TK  PTZ Tm-iT  ː8T  T A T H> ]T$OT J\U-XU4$fUP U_ Ut,lU U |U ,U$EU 7XU$GU U T8V 8$V-6@V@ '`PVQ $V^$VV|$W(V  `V$UV \`V V$9W-sW yxW#$TWE,~(W^ hXWm$PW  %W  PW W$CW$RlW mhW V`W 8X$<@X $RX" ,XJ `X[ OHXXn LXz$OX PHDX  pX xTX }X X$SY-/Y,,~8YO Mh|YX-XY Y K(;$BY  Y p4Y ʀY "Y,~`Z  YZ+ }P|Z< G ZE$CZQ  dZj @$Zz HZ$jh`($8Z HZ Z WPZ,Z X8[ WH[ [$,0[;$C[E Q,[Q Lx[Z sX[j 4[x-~([$=`[$E`[,[ (0[,l[$B[  \ X\ +\&$O\2 ud\G X\X Ud\p-G\,~H\$Z\ \,\$Xh] @]# 6]3,]P$S]i  d1$>2W$; ]v ],y0@S$=0 Yhd]$Rx]-s] ]  ] J@^ ,^. [xH^G$W^_ R^f b^p-X= e@<^$9^$O`^  ^,l^ ^ ^ 0_ VH_) h_4 HP_I X5 <_S-sp8 T,_s   _ `_-`_$9_  b_,_ (_-P(`   `P`-$lH`V$NX`p  t`|$K `  !H` h`-GW 8`$K` Wp4`,a  "@a% Ya6 jP\a?  ~ptaL  |aV ˠ8aj Pa~  Űa-sa @``a 'xa ha ``a R a  Ub  #b1$?b8 ptbJ bW,~bx ,b$:b,  $b,`b  b-Pb dc Dc#,cK-PHcq$G`c~$=c$I c$Ec-/c,~@c Hc H,d 'd$W d>-68d{ xd )8dd 9d (Pxd d,d e -Ge/ (@e= #xeM$ZXew$@e-s`e$W0`e F`De  He @/` <e$= e 8f-`f.  ,f=-Gff Hfx ^ f Tf 8f  \*$:f-yf Xf$Mg$Mg +g5,gM$Z(g{  g g JW$Xg$Ng Xg$Wg (h$Mh4 Wh= oP(hM  h] QHphk$9@hr Ph~ P8 xHh$f8Hstrerrorparse_opt_with_commitinflateEndget_object_directoryignore_casedatestamppthread_getspecificstrbuf_remove$XAoAMiKMoFXM07T.object_entry_alloc$XBoAMiKlpFXMUzW.rename_ref.renamed_refrename_index_entry_at$XAoAMiKhoFXM0bU.config_parameterschmodxdi_diff_outfgit_config_maybe_boolfgetcref_rev_parse_rulesstrcmpclear_commit_markssort_by_date$XAoAMiKhoFXM0bU.config_fileadd_object_arrayzlib_compression_levelfill_tree_descriptor$XAoAMiKmpFXMk1W.current_branchprettify_refnamegit_config_ulongparse_opt_abbrev_cb$XBoAMiKlpFXMUzW.shorten_unambiguous_ref.nr_ruleslookup_decorationpacket_get_linefgets$XBoAMiKkoFXMUgU.setup_convert_check.attr_eol$XBoAMiKxpFXM0MX.unpack_sha1_header.valid_loose_object_typeget_textconvadd_object_array_with_modecolor_parse$XBoAMiKMoFXM07T.read_next_command.stdin_eofstrbuf_readlinkgit_config_parse_parameterformat_commit_message$XAoAMiKWpFXMUKW.pager_process$XBoAMiKMoFXM07T.file_change_m.uqdiff_addremovegit_pathdupinit_display_notesread_sha1_file_repl$XBoAMiKlpFXMUzW.resolve_ref.ref_bufferappend_grep_patdiff_populate_filespec$XAoAMiKmpFXMk1W.remotes_allocdiffcore_stdre_match$XAoAMiKxpFXM0MX.pack_open_windowsmemcpyget_revisiongit_diff_basic_config$XBoAMiKRoFXMECU.alloc_blob_node.blockxmalloctrace_argv_printfgitstrcasestris_inside_git_dir$XAoAMiKGpFXMU0V.lock_file_listre_set_syntaxftellguess_remote_headlookup_commit_reference_gently$XAoAMiK5oFXM0bV.git_index_fileclose_lock_filestrbuf_initsymlinkstrcpystrspnpeel_ref$XAoAMiKGqFXM0DY.error_routine$XAoAMiKHqFXMEFY.drivers_allocfill_directory$XBoAMiKxpFXM0MX.find_pack_entry.last_found$XBoAMiK6oFXMUeV.system_path.prefix$XBoAMiKMoFXM07T.update_branch.msgcore_compression_levelunuse_packinit_tree_descadd_decorationin_merge_basesunlinkparse_tree_bufferfree_notesfree_hashgetpwuidabortshow_early_outputapply_default_ignorewhitespacestrbuf_trimassume_unchangedset_die_routine$XBoAMiKxpFXM0MX.find_pack_entry_one.debug_lookupget_commit_format$XAoAMiKxpFXM0MX.pack_mmap_calls$XAoAMiKMoFXM07T.failurealloc_ref$XAoAMiKspFXMUFX.main_thread_setSHA1_Initsetup_git_directory_gentlyautorebasehas_symlink_or_noent_leading_pathalt_odb_listget_max_object_index$XAoAMiKvoFXMEAV.rename_src_alloc$XAoAMiKQpFXMEEW.nr_objs$XAoAMiK5oFXM0bV.git_graft_filegit_attr__true$XAoAMiKxpFXM0MX.peak_pack_mapped$XAoAMiK5oFXM0bV.work_treedeflateInit_approxidate_relativefor_each_reflogclosedirget_reflog_messageis_utf8graph_show_commit$XAoAMiKvoFXMEAV.rename_dst_allocpthread_equalcompile_grep_patterns$XAoAMiKMoFXM07T.pack_id$XAoAMiKHqFXMEFY.ndriverslookup_unknown_objectxfdopengetpwnamsha1writesafe_create_leading_directories_constutime$XBoAMiKkoFXMUgU.setup_convert_check.attr_identthe_index$XAoAMiKQpFXMEEW.obj_hash$XBoAMiKMoFXM07T.file_change_d.uqforce_object_looseexcludedstart_asyncparse_optionsmktimexdl_recs_cmpstrstrgit_config$XBoAMiKHpFXM02V.append_signoff.signed_off_byfputccombine_notes_concatenatere_compile_patternxmmapgit_inflate_endsha1closecore_compression_seengit_parse_ulong__xargvgit_config_permlog_tree_diff_flushgit_pathunsetenv$XAoAMiK6oFXMUeV.argv0_pathfputs_GLOBAL_OFFSET_TABLE_has_loose_object_nonlocal$XAoAMiKxpFXM0MX.cached_object_nrpop_most_recent_commit$XAoAMiKtoFXMU6U.orderdiff_use_color_defaultfsync_or_dienth_packed_object_offsetfor_each_glob_ref_inparse_options_startprefer_symlink_refsunable_to_lock_index_dielocaltime_rnth_packed_object_sha1commit_list_insertgit_attr_set_directionxdl_get_hunk$XAoAMiKMoFXM07T.cmd_histSHA1_Updateatoicopy_ref_listtolowerhas_pack_indexvalidate_headref$XAoAMiKHqFXMEFY.driver_true$XBoAMiKMoFXM07T.note_change_n.uq$XBoAMiKRoFXMECU.alloc_tree_node.blockbranch_merge_matchessha1_file_namemake_cache_entrydiff_tree_release_pathsfind_unique_abbrevgit_config_from_parametersstrbuf_splicediff_unmergereencode_commit_messageregcompnotes_cache_putcheck_pack_crcstart_progress_delaytrust_executable_bitsane_ctype$XAoAMiKopFXM04W.replace_object_nrgit_env_boolpager_use_colorfstatcache_name_compare$XBoAMiKMoFXM07T.dump_marks.mark_lockget_sha1_with_mode_1__xargcsignalwarn_ambiguous_refsunmerged_indexdiffcore_count_changes$XAoAMiKWoFXMULU.en85tag_typeunmerge_index_entry_at$XAoAMiKFpFXMEzV.default_ll_mergegit_config_string$XBoAMiKRoFXMECU.alloc_object_node.nrfill_textconvthreaded_has_symlink_leading_pathmake_relative_path$XBoAMiKvpFXMUIX.setup_work_tree.initializedfor_each_remote$XAoAMiKMoFXM07T.pack_compression_seenis_repository_shallowgit_config_pathnameread_in_full$XAoAMiKMoFXM07T.pack_edges$XBoAMiKkoFXMUgU.setup_convert_check.attr_filterget_relative_cwdis_submodule_modifiedgethostname$XAoAMiKooFXMUnU.weekday_names$XBoAMiKFpFXMEzV.ll_merge_marker_size.checkpush_defaultrun_commandsha1_pack_namediff_queued_diff$XAoAMiKQpFXMEEW.object_type_stringsftruncate$XAoAMiKMoFXM07T.pack_fileget_fetch_map$XAoAMiKMoFXM07T.show_statswrite_in_fulltrust_ctimeparse_date_formatgitmkstempsadd_extra_refgitmemmemgit_mailmap_filefor_each_tag_ref$XBoAMiKapFXM0YW.git_mkstemps_mode.num_lettersdisplay_progressstring_list_appendvreportfis_encoding_utf8parse_opt_tertiary$XAoAMiKepFXM0hW.commit_formats_lenalloc_commit_nodecopy_fdcrc32_endunquote_c_stylediscard_revindexapproxidate_carefulmunmapgit_default_advice_configget_shallow_commits$XBoAMiKCpFXM0rV.fmt_ident.bufferfile_existsvfprintf$XAoAMiKmpFXMk1W.default_remote_name$XAoAMiKVoFXMEKU.builtin_attr$XAoAMiKCpFXM0rV.env_hintbase_name_compareutf8_widthindex_path_DYNAMICdiscard_indexconvert_to_gitws_checkstrbuf_expand_dict_cbsha1_posremove_file_from_indexprefixcmpparse_commit_bufferfmt_identlookup_commit_graft$XAoAMiKMoFXM07T.avail_tree_entry__environ_lockreaddirfill_filespecwrite_or_whine_pipe$XAoAMiKMoFXM07T.pack_sizeexecv_git_cmdSHA1_Final$XBoAMiKypFXMEPX.get_sha1_basic.warning$XAoAMiKVoFXMEKU.attr_nrlog_all_ref_updateshas_non_asciilock_ref_sha1xdl_build_script$XAoAMiKMqFXMkQY.whitespace_rule_names_get_exit_frame_monitorgraph_show_onelinegrep_bufferupdate_tree_entry$XAoAMiKkoFXMUgU.user_convertstrtolfree_patch_ids$XAoAMiK9oFXMUkV.header_fieldsetup_path$XAoAMiK8pFXMklX.err_msgxdiff_set_find_func__ctype_maskget_sha1_mbpatch_delta$XAoAMiKMoFXM07T.active_branchesparse_opt_approxidate_cbusagefstrbuf_ltrimputenvpp_title_lineshow_decorationsgit_mkstempssha1_to_hexparse_hunk_headerxdl_cha_nextremove_or_warn$XAoAMiKgoFXMkZU.commit_graft_allocwrite_or_whinegit_snpathfind_pack_revindexalloc_object_node$XAoAMiKMoFXM07T.unread_command_bufuser_ident_explicitly_givendiff_tree_combinedwrite_name_quoted_relativeregerrorchdirdelta_base_cache_limitws_check_emitcheck_repository_format_versionhas_dirs_only_pathencode_in_pack_object_header$XAoAMiKMoFXM07T.export_marks_fileparse_tagclear_delta_base_cachegraph_is_commit_finished$XAoAMiKlpFXMUzW.cached_refsstart_commandstrbuf_detachmatch_refssimplify_commitdiff_setupsq_dequote$XAoAMiKMoFXM07T.blocksdiff_free_filespec_dataremove_marked_cache_entriesxdup$XAoAMiKKqFXM0LY.try_to_free_routine$XAoAMiKRoFXMECU.blob_allocsgit_branch_trackxstrdupforktcl_quote_printdo_nothinggit_exec_pathstring_list_add_refs_from_colon_sepdiff_flush_patch_iddie_errnohas_commit_patch_id$XAoAMiKGpFXMU0V.alternate_index_outputparse_object_bufferparse_opt_verbosity_cbis_bare_repositoryws_fix_copyset_shared_permprepare_alt_odbget_octopus_merge_basesread_indexuserdiff_find_by_nameformat_display_notessq_dequote_to_argv$XAoAMiKgoFXMkZU.commit_graftnotes_cache_getopen_pack_index$XAoAMiKMoFXM07T.object_table$XBoAMiK8oFXMEjV.diff_output_prefix_callback.msgbufhas_sha1_filewrite_ref_sha1append_grep_patternstrip_path_suffixname_decorationgit_checkattrsuffixcmpread_ref_atuser_ident_sufficiently_given$XAoAMiKMoFXM07T.big_file_thresholdalloc_tree_nodere_match_2find_sha1_packoffset_1st_component$XAoAMiKvpFXMUIX.inside_git_dirstrbuf_freaddiff_treeadd_pending_objectxmkstempwrite_sha1_filerelease_pack_memorynotes_cache_initodb_pack_keepstring_list_clearremote_find_trackingget_tree_entryparse_opt_color_flag_cb$XAoAMiKGqFXM0DY.warn_routinegetpagesize$XAoAMiKOpFXMEAW.display_notes_treessigchain_popxdi_diff$XBoAMiKxpFXM0MX.sha1_pack_name.name$XAoAMiKGqFXM0DY.die_routine$XAoAMiKMoFXM07T.cmd_tail$XBoAMiKhoFXM0bU.parse_value.valuedup2$XAoAMiKopFXM04W.replace_object_alloc$XAoAMiKzoFXMEMV.diff_tempre_syntax_options$XBoAMiK5pFXM0hX.show_submodule_summary.formatparse_treeprime_cache_treeshow_reflog_message$XAoAMiKspFXMUFX.child_notifierdetermine_actioncombine_notes_overwriteconvert_to_working_treefor_each_ref$XBoAMiKOoFXME9T.make_nonrelative_path.bufungetc$XBoAMiKOoFXME9T.get_pwd_cwd.cwdll_merge_marker_sizewrite_or_diepack_reportcolor_parse_memcache_tree_read$XAoAMiKMoFXM07T.last_blobupdate_ref$XAoAMiK5oFXM0bV.git_dir$XAoAMiKmpFXMk1W.s_tag_refspecresolve_undo_writedie_resolve_conflictassert_sha1_type$XBoAMiKxpFXM0MX.sha1_pack_index_name.nameadvice_push_nonfastforward_etextwrite_name_quotedpfxparse_objectsscanfrmdir_or_warnshow_submodule_summary$XAoAMiKRoFXMECU.tree_allocs$XAoAMiKMoFXM07T.max_packsizenotes_cache_writeset_git_dirsha1_entry_pos$XAoAMiKrpFXMk$W.tree_differenceexecl_git_cmd$XBoAMiKRoFXMECU.alloc_tag_node.nr$XBoAMiKOoFXME9T.make_absolute_path.bufscheck_ref_formatsetup_git_directoryparse_revision_opt$XAoAMiKMoFXM07T.pack_compression_level$XAoAMiKMoFXM07T.delta_count_by_typeparse_blob_bufferfeof$XAoAMiKRoFXMECU.object_allocsshow_date_relativefixup_pack_header_footerpretend_sha1_file$XAoAMiKtoFXMU6U.order_cntferrorgrep_threads_okread_tree$XAoAMiKMoFXM07T.import_marks_fileresolve_refutf8_strwidth$XAoAMiKMoFXM07T.first_tagsort_in_topological_orderstrcspnvalid_fetch_refspec$XBoAMiKRoFXMECU.alloc_blob_node.nrremove_pathpython_quote_print$XAoAMiKGqFXM0DY.usage_routinedwim_log$XAoAMiKMoFXM07T.atom_table_szunsorted_string_list_has_stringexecvppacket_read_line$XAoAMiKgoFXMkZU.all_flagsmake_absolute_pathshorten_unambiguous_ref$XAoAMiKMoFXM07T.global_argc$XAoAMiK5oFXM0bV.git_refs_dir$XAoAMiKipFXM0vW.write_buffer_len_edata$XAoAMiKepFXM0hW.commit_formats_alloc$XBoAMiKypFXMEPX.find_short_object_filename.fakeentlookup_tagie_match_stat$XAoAMiKmpFXMk1W.branches_nrpthread_exitprepare_revision_walk$XAoAMiKMoFXM07T.next_markcombine_notes_ignore$XAoAMiKxpFXM0MX.empty_treestrrchris_bare_repository_cfgxdl_free_envbasenamegit_config_global$XAoAMiKMoFXM07T.global_argvformat_subjectsave_commit_bufferdiff_free_filespec_blobgit_mkstempunmerge_indexstrbuf_addbuf_percentquote$XAoAMiKkoFXMUgU.user_convert_tailadd_excludes_from_file_to_list$XBoAMiKooFXMUnU.show_date.timebufread_graft_line$XAoAMiKmpFXMk1W.explicit_default_remote_nameremove_scheduled_dirs$XAoAMiK6pFXMEjX.default_cachexreallocread_directory$XAoAMiKMoFXM07T.max_active_branchesxdl_do_diffget_sha1_hexfopen$XBoAMiKMoFXM07T.die_nicely.zombie$XAoAMiKxpFXM0MX.pack_used_ctrset_ref_status_for_pushset_alternate_index_output$XAoAMiKVoFXMEKU.directionbranch_has_merge_configget_graft_fileis_descendant_ofmark_parents_uninteresting$XBoAMiKMoFXM07T.file_change_cr.s_uqusage_msg_optis_inside_dir_PROCEDURE_LINKAGE_TABLE___fsr_init_value$XBoAMiKapFXM0YW.git_mkstemps_mode.letters$XAoAMiKxpFXM0MX.prepare_packed_git_run_oncexdl_num_out$XAoAMiKMoFXM07T.seen_data_command$XAoAMiK2pFXMkcX.signals$XBoAMiKCqFXMUvX.setup_traverse_info.dummylock_any_ref_for_updatemake_traverse_path$XAoAMiKzoFXMEMV.diff_detect_rename_default$XBoAMiKUqFXM0cY.init_syntax_once.donepp_remainderstrlcpy$XBoAMiKgoFXMkZU.prepare_commit_graft.commit_graft_preparedstring_list_find_insert_indexclear_mailmap$XAoAMiKspFXMUFX.child_errdiff_set_mnemonic_prefixsigchain_push_common$XBoAMiKBpFXMkqV.sha1_to_hex.bufnoget_indexed_objectstrbuf_addrun_command_v_optadd_reflog_for_walkreencode_stringpthread_self$XAoAMiKzoFXMEMV.diff_rename_limit_default$XAoAMiKspFXMUFX.async_keyindex_fdpacked_git_window_sizeparse_tree_indirectparse_whitespace_rulematch_pathspecfnmatch$XAoAMiKMoFXM07T.force_updatexdl_do_patience_diffgit_mkstemp_modestrncpywrite_cache_as_treeopendir_environstrbuf_insertremote_has_urlstrncasecmpbranch_gethandle_ignore_submodules_argis_index_unborn$XBoAMiKxpFXM0MX.git_open_noatime.sha1_file_open_flaggit_inflatefmt_nameobject_list_containsstrpbrkxmemdupzwhitespace_error_stringre_search_2format_noteget_merge_bases_many$XAoAMiKFpFXMEzV.ll_merge_drvget_stale_headslookup_treegit_config_from_file$XAoAMiKMoFXM07T.marksfree_pack_by_namepacket_buf_flush$XAoAMiKMoFXM07T.avail_tree_tableadd_head_to_pendingcommit_lock_filefwritediff_unmodified_pairunable_to_lock_errorlibiconvgit_xmerge_configcreate_delta_indexre_compile_fastmapfclosetype_from_string$XAoAMiKooFXMUnU.specialgettimeofday$XAoAMiKzoFXMEMV.diff_suppress_blank_emptyget_pathspecstrncmpremove_notecache_tree_matches_traversalindex_name_existsfor_each_rawreffflushdf_name_compare$XAoAMiKvoFXMEAV.rename_src_nr$XAoAMiKVoFXMEKU.use_indexparse_dateindex_name_is_othercrc32strbuf_getwholelinenull_sha1read_tree_recursive$XBoAMiKkoFXMUgU.setup_convert_check.attr_text$XAoAMiKfoFXMUXU.contextxmkstemp_modefree_filespecxdl_hash_recordis_empty_dir$XAoAMiKWpFXMUKW.spawned_pagermark_tree_uninterestingadd_to_alternates_fileprefix_filename$XAoAMiKipFXM0vW.write_buffer$XAoAMiKRoFXMECU.tag_allocspacked_object_info_detail__ctypevsnprintfgrafts_replace_parents$XAoAMiKgoFXMkZU.commit_utf8_warngetcwd$XAoAMiKmpFXMk1W.bufferfree_refswaitpidmap_userget_git_dir$XAoAMiKxpFXM0MX.delta_base_cachedread_mmfile$XAoAMiKMoFXM07T.fast_import_usagesha1_pack_index_name$XAoAMiKMoFXM07T.object_count_by_type$XAoAMiKhoFXM0bU.zlib_compression_seendisplay_throughputwrite_idx_filemksnpathsha1fd$XBoAMiKxpFXM0MX.find_pack_entry_one.use_lookup$XAoAMiKzoFXMEMV.diff_word_regex_cfgdaemon_avoid_aliasuserdiff_find_by_pathpeel_to_typesafe_create_leading_directoriesdiff_changefind_ref_by_name$XBoAMiKzoFXMEMV.emit_rewrite_diff.b_namecommit_tree$XBoAMiKxpFXM0MX.sha1_get_pack_name.hex$XBoAMiKapFXM0YW.get_pathname.pathname_array$XAoAMiKOpFXMEAW.display_notes_refsadd_object$XBoAMiKMoFXM07T.keep_pack.name$XAoAMiKVoFXMEKU.blankref_fetch_rulesraisefsync_object_filesstrbuf_slopbufisattypacket_writeparse_fetch_refspec$XAoAMiKmpFXMk1W.branches_allocgit_log_output_encodinggraph_init$XAoAMiKxpFXM0MX.pack_mappedread_replace_refsadd_packed_gitlocal_repo_envstrcasecmprename_refstrbuf_rtrim$XAoAMiKmpFXMk1W.rewrites_pushget_local_heads$XAoAMiKhoFXM0bU.config_parameters_tailparse_options_concatxdl_bogosqrtcheck_sha1_signatureget_sha1_with_context_1add_index_entrytree_typeget_remote_refverify_pack_indexrollback_lock_filecore_apply_sparse_checkoutinit_notesgit_pagerre_execlookup_commit$XBoAMiKHpFXM02V.load_ref_decorations.loadedgit_default_config$XBoAMiKMoFXM07T.file_change_cr.d_uqshow_combined_diffunsorted_string_list_lookupget_git_work_treehave_git_dirstrbuf_adddupparse_date_toffset__iob$XAoAMiKvoFXMEAV.rename_srcinterpret_branch_namecache_tree_invalidate_pathget_sha1$XBoAMiKlpFXMUzW.shorten_unambiguous_ref.scanf_fmtsdiffcore_renamediff_queue_is_emptysetup_standard_excludesquote_path_relative$XBoAMiKlpFXMUzW.update_ref.locksq_quote_buf$XBoAMiKMoFXM07T.dump_tags.msggit_xmerge_styledeflateBoundlookup_replace_objectpager_programremove_index_entry_atget_merge_basescache_tree_write$XAoAMiKmpFXMk1W.rewritesreduce_heads$XAoAMiKVoFXMEKU.check_all_attrapply_default_whitespaceload_ref_decorations$XAoAMiKooFXMUnU.month_namesfor_each_notestring_list_clear_funcfor_each_remote_ref$XAoAMiKMoFXM07T.mem_pool_allocmake_nonrelative_pathfinish_asyncalloc_filespecdefault_notes_treepager_in_usecolor_fprintf_lncommit_locked_indexcopy_filexcallocsystem_pathlookup_hash$XBoAMiKmpFXMk1W.match_refs.default_refspecreprepare_packed_gitverify_pack$XAoAMiKooFXMUnU.timezone_namesrmdirparse_tag_bufferapply_refspecs$XAoAMiKMoFXM07T.max_depthcore_preload_index$XBoAMiKapFXM0YW.make_relative_path.buffor_each_hash$XAoAMiKQpFXMEEW.obj_hash_size$XAoAMiKMoFXM07T.marks_set_countlongest_ancestor_lengthgit_config_systemcommit_list_countperl_quote_printshared_repository$XAoAMiKMoFXM07T.new_datamainfree_grep_patternsfor_each_recent_reflog_entstrlen$XBoAMiKvpFXMUIX.setup_git_directory_gently.cwd$XAoAMiK8oFXMEjV.column_colorsre_searchhash_sha1_file$XBoAMiKLqFXMENY.maybe_flush_or_die.skip_stdout_flushlog_tree_commit__1cG__CrunMdo_exit_code6F_v_do_check_packed_object_crcset_git_work_tree$XBoAMiKipFXM0vW.is_empty_blob_sha1.empty_blob_sha1get_commit_actionhexval_table$XBoAMiKlpFXMUzW.write_ref_sha1.termhold_lock_file_for_append$XAoAMiKHqFXMEFY.builtin_drivers___errnodiff_filespec_is_binary$XAoAMiKepFXM0hW.builtin_formats_lenremove_dir_recursively_finigit_config_boolgit_use_color_defaultget_reflog_selectorquote_path_fullystrbuf_check_branch_ref$XBoAMiKzoFXMEMV.emit_rewrite_lines.nneofcopy_file_with_timeunpack_entrypthread_key_createwrite_notes_treestring_list_insertcheck_filename$XAoAMiKUqFXM0cY.re_comp_bufsq_quote_argvload_notes_treesunlink_or_warnmemmoveresolve_gitlink_refpacket_flushdecode_85$XAoAMiKzoFXMEMV.external_diff_cmd_cfgcache_tree_sub$XAoAMiKMoFXM07T.branch_table$XAoAMiKMoFXM07T.tree_entry_alloc$XBoAMiKdoFXM0UU.parse_attr.attr_names$XAoAMiKMoFXM07T.alloc_count$XAoAMiKMoFXM07T.branch_countregister_commit_graftre_set_registersputchar$XBoAMiKhoFXM0bU.git_parse_file.varce_path_matchobject_array_remove_duplicatesxread$XAoAMiKMoFXM07T.all_packs$XAoAMiKMoFXM07T.command_bufmkpathnotes_ref_nameusage_with_optionsstrbuf_splitwrite_indexxdl_recmatchappend_header_grep_patternadvice_implicit_identitycolor_fprintfsha1fd_throughput$XAoAMiKMoFXM07T.whenspecadvice_detached_head$XBoAMiKRoFXMECU.alloc_tree_node.nrlstatxdl_diff$XBoAMiKOpFXMEAW.format_note.utf8handle_revision_argregfreeadvice_commit_before_merge$XAoAMiKfoFXMUXU.combine_marker$XBoAMiKbpFXMkbW.format_packet.hexchar$XAoAMiKxpFXM0MX.delta_base_cache_lrustrbuf_branchnamepthread_create$XBoAMiKypFXMEPX.find_unique_abbrev.hex$XBoAMiKMoFXM07T.keep_pack.keep_msgadd_to_indexparse_decorate_color_configmime_boundary_leaderexcludes_filehold_locked_indexsha1flushfcntlce_same_namepretty_print_commitcache_tree_freefind_pack_entry_onediffcore_breakshow_dateqsortgethostbynamelibiconv_closediffcore_merge_brokenquote_two_c_stylecache_tree_updateunpack_object_header_buffercreate_delta$XAoAMiKVoFXMEKU.git_attr__unknownprune_noteslibiconv_opendiff_get_colorxdl_mergefor_each_branch_refgit_committer_infoxdl_prepare_envdiff_root_tree_sha1parse_options_step$XAoAMiKmpFXMk1W.remotesxdi_diff_hunksfor_each_glob_ref$XBoAMiKxpFXM0MX.sha1_pack_name.baseaccessadd_name_hashformat_tracking_infodiffcore_pickaxediff_auto_refresh_indexcreate_symrefgraph_show_commit_msgcreate_object$XAoAMiKUpFXM0HW.pack_revindexsafe_crlf$XBoAMiKBpFXMkqV.sha1_to_hex.hexgit_config_colorboolwrite_name_quotedsetup_pagerverify_non_filenamemove_temp_to_filestrbuf_add_wrapped_textremote_is_configuredgetuidpacked_git_limiteditor_programgit_attr__false$XBoAMiKxpFXM0MX.sha1_pack_index_name.baselog_ref_setupdiff_setup_doneadd_excludes_from_filefree_delta_indexgit_set_argv_exec_pathdiff_opt_parse$XAoAMiKvoFXMEAV.rename_dst_nrnormalize_path_copy$XAoAMiKvoFXMEAV.rename_dstxdl_cha_firsthas_symlinksre_max_failuresgraph_updategmtime$XAoAMiK5oFXM0bV.git_work_tree_initializedparse_options_end$XAoAMiKVoFXMEKU.git_attr_hash$XAoAMiKHpFXM02V.decoration_colors$XBoAMiKvpFXMUIX.prefix_filename.path$XAoAMiKMoFXM07T.pack_data$XAoAMiKVoFXMEKU.attr_stack$XBoAMiKepFXM0hW.logmsg_reencode.utf8$XAoAMiKmpFXMk1W.remotes_nr$XBoAMiKwpFXMkKX.sha1_entry_pos.debug_lookup$XBoAMiKapFXM0YW.enter_repo.used_path$XBoAMiKOpFXMEAW.for_each_note_helper.pathxdl_cha_allocstrbuf_expandremote_getgit_default_email$XAoAMiKooFXMUnU.number_name$XAoAMiKxpFXM0MX.cached_objectsstrbuf_read$XAoAMiKMoFXM07T.cur_active_branchesgit_config_bool_or_intmaybe_flush_or_die$XBoAMiKMoFXM07T.unkeep_all_packs.name$XAoAMiKMoFXM07T.mem_poolverify_path$XAoAMiKMoFXM07T.import_marks_file_from_stream$XAoAMiKFpFXMEzV.ll_user_merge_tailstrbuf_read_filegit_etc_gitconfig$XAoAMiKMoFXM07T.new_tree$XAoAMiKHqFXMEFY.drivers$XAoAMiK5oFXM0bV.git_object_dircache_tree_fully_validsizeof_delta_indexhead_ref$XAoAMiKFpFXMEzV.ll_user_merge$XAoAMiKhoFXM0bU.config_file_nameref_remove_duplicates$XAoAMiKCpFXM0rV.git_default_dateadvice_status_hintsregexec$XAoAMiKxpFXM0MX.delta_base_cacheread_pack_headerws_blank_linegraph_show_remaindersetup_revisions$XAoAMiK6oFXMUeV.argv_exec_pathatexitgit_extract_argv0_path$XBoAMiKzoFXMEMV.external_diff.external_diff_cmd$XBoAMiKfoFXMUXU.dump_quoted_path.bufgit_work_tree_cfg$XAoAMiKfpFXMEnW.progress_updatestart_progresspthread_join$XBoAMiKMoFXM07T.start_packfile.tmpfilegetpid$XAoAMiK6pFXMEjX.removallocaltimepop_commitdiff_free_filepair$XBoAMiKhoFXM0bU.git_parse_file.utf8_bom$XAoAMiKapFXM0YW.bad_pathgetenvfree_refspec$XAoAMiKhoFXM0bU.config_linenr$XBoAMiKRoFXMECU.alloc_commit_node.nrgit_config_set_multivar$XAoAMiKMoFXM07T.object_countstrtoumaxread_gitfile_gentlyodb_mkstempinit_patch_idsdiffcore_order$XBoAMiKFpFXMEzV.ll_merge.check$XBoAMiKooFXMUnU.tm_to_time_t.mdaysdeflate$XAoAMiKwoFXMUBV.Tfinish_command$XAoAMiKwoFXMUBV.U$XAoAMiKzoFXMEMV.remove_tempfile_installedgit_attradd_excludelog_write_email_headersfor_each_reflog_ent$XBoAMiKMoFXM07T.parse_new_tag.msgset_try_to_free_routine$XAoAMiKopFXM04W.replace_object$XAoAMiKWpFXMUKW.pager_argvindex_name_posfor_each_string_list$XAoAMiKMoFXM07T.atom_tablesetup_traverse_infois_inside_work_treefill_stat_cache_infomkdirread_index_unmergedfsyncxdl_emit_diffrecadd_notecheck_repository_format$XAoAMiKUqFXM0cY.re_syntax_tablefree_commit_listparse_commit$XAoAMiKUqFXM0cY.re_error_msg$XBoAMiKOoFXME9T.make_absolute_path.next_bufdir_add_ignored$XAoAMiK0pFXMEaX.is_shallowwarn_dangling_symreftraverse_trees__assert$XAoAMiKWoFXMULU.de85safe_write$XAoAMiKvpFXMUIX.inside_work_tree$XAoAMiKMoFXM07T.rc_freegit_config_intconfig_exclusive_filenamediff_tree_setup_pathsunregister_shallowtrace_printfsigactionauto_crlfget_size_from_deltaget_index_filexdl_mmfile_firstsprintfobject_creation_mode$XBoAMiKdoFXM0UU.parse_attr.attr_valuesbuffer_is_binaryfake_reflog_parentprefix_path$XAoAMiKMoFXM07T.cmd_savestrbuf_releaseschedule_dir_for_removalsort_string_listrefname_matchsetup_work_treepack_idx_off32_limit$XBoAMiKxpFXM0MX.write_loose_object.tmpfile$XAoAMiKMoFXM07T.branch_load_countxstrndup$XBoAMiKopFXM04W.prepare_replace_object.replace_object_preparedverify_filenameinit_reflog_walkxdl_mmfile_nextcrc32_beginxdl_hashbitsuserformat_find_requirements$XAoAMiKgoFXMkZU.commit_graft_nrstrbuf_cmppacket_buf_writeshow_logsigemptysetrun_command_v_opt_cd_envxdl_mmfile_sizeread_mailmap$XAoAMiKooFXMUnU.typelengraph_show_paddingxdiff_clear_find_funcinstall_packed_git$XAoAMiKlpFXMUzW.packlockderef_tagclear_extra_refsxdl_change_compact$XAoAMiKhoFXM0bU.storesetitimerstrbuf_growxmalloczfor_each_ref_inhas_sha1_packgit_commit_encodinglseekfileno$XBoAMiKhoFXM0bU.git_etc_gitconfig.system_widexdl_atol$XAoAMiKMoFXM07T.total_allocdstrcatsha1_object_infocommit_refindex_pack_lockfilecommit_typefor_each_replace_refdiff_result_codediffcore_fix_diff_index$XAoAMiKRoFXMECU.commit_allocs$XAoAMiKMoFXM07T.relative_marks_pathsdiff_words_stylesprint_string_list$XAoAMiKxpFXM0MX.alt_odb_tail$XAoAMiKzoFXMEMV.diff_colorsgit_config_setforeach_alt_odb$XAoAMiKlpFXMUzW.current_refpack_idx_default_versionxdl_cha_freexdl_cha_init$XAoAMiKMoFXM07T.duplicate_count_by_typewhitespace_rule_cfg$XBoAMiKepFXM0hW.mailmap_name.mail_mapdwim_refrun_hookstring_list_add_refs_by_globlookup_commit_referencediff_tree_combined_mergeadvice_resolve_conflictxwritecopy_note$XAoAMiKspFXMUFX.main_threadstrtoul$XBoAMiKMoFXM07T.parse_new_commit.msgref_newer$XAoAMiKMoFXM07T.atom_cntie_modifiedresolve_undo_readread_object_with_referenceparse_pack_indexdelete_refconfig_error_nonboolclose_ref$XAoAMiKepFXM0hW.commit_formatslookup_blobtypename$XAoAMiKzoFXMEMV.diff_mnemonic_prefixmemsetre_comp$XBoAMiKapFXM0YW.get_pathname.indexgit_author_info$XBoAMiKkoFXMUgU.setup_convert_check.attr_crlfalloc_report$XBoAMiKdoFXM0UU.parse_color.color_names$XAoAMiKMoFXM07T.old_treeinsert_hashselectdiff_tree_sha1strbuf_getline$XBoAMiKRoFXMECU.alloc_object_node.block__dtoulgit_inflate_init$XBoAMiKBpFXMkqV.sha1_to_hex.hexbuffer$XAoAMiKgpFXMUoW.sq_lookupstrbuf_list_freeread_refis_directorypath_nameadd_commit_patch_idrecord_resolve_undostat_tracking_info$XBoAMiKHqFXMEFY.userdiff_find_by_path.attrget_patch_filenamediff_qencode_85unlock_refenter_repogit_diff_ui_config$XBoAMiKIqFXMUGY.git_wcwidth.combiningmemchrxdl_emit_hunk_hdrpp_user_info$XAoAMiKhoFXM0bU.config_file_eoflookup_objectgetppid$XAoAMiKlpFXMUzW.extra_refs$XBoAMiKxpFXM0MX.sha1_file_name.bufalloc_tag_node$XAoAMiKmpFXMk1W.branchesresolve_undo_clear_indexclose_pack_windows$XAoAMiKxpFXM0MX.peak_pack_open_windows$XAoAMiKUpFXM0HW.pack_revindex_hashszinflateInit_strchrpthread_setspecificfseek$XAoAMiKxpFXM0MX.cached_object_alloc$XAoAMiKMoFXM07T.last_taghold_lock_file_for_updatesq_quote_printinsert_by_date$XBoAMiKOoFXME9T.make_absolute_path.buf$XBoAMiKMqFXMkQY.setup_whitespace_attr_check.attr_whitespacestring_list_insert_at_indexdiff_unique_abbrevsigchain_pushdiff_queuexdl_emit_diff$XBoAMiKapFXM0YW.enter_repo.validated_pathgrep_opt_dup$XBoAMiKRoFXMECU.alloc_tag_node.blockstop_progressread_index_from$XBoAMiKzoFXMEMV.emit_rewrite_diff.a_namegmtime_r$XAoAMiKepFXM0hW.user_format$XAoAMiKPoFXMU_T.advice_configclose_pack_indexresolve_remote_symrefxdl_guess_linesdeflateEnduserdiff_config$XAoAMiKzoFXMEMV.diff_no_prefixinit_revisions$XBoAMiKRoFXMECU.alloc_commit_node.blockgit_mkstemps_modestrbuf_addfwrite_shallow_commits_lib_versionobject_list_insertgit_default_namegit_config_rename_section$XAoAMiKMoFXM07T.branch_table_sz___Argv$XBoAMiKzoFXMEMV.emit_hunk_header.atat$XAoAMiKbpFXMkbW.buffer$XBoAMiKzoFXMEMV.external_diff.done_preparingexcluded_from_liststop_progress_msggit_color_default_config$XBoAMiKMoFXM07T.parse_and_store_blob.bufxdl_free_scriptblob_typeadd_file_to_index$XAoAMiKMoFXM07T.avail_tree_table_szget_noteprepare_git_cmdalloc_blob_nodestrbuf_attachmemcmpread_mmblobexpand_user_path$XAoAMiKHqFXMEFY.driver_falselibz.so.1SUNW_1.2libnsl.so.1SUNW_0.7SUNWprivate_1.1libthread.so.1SUNW_1.4libc.so.1SUNW_1.22libiconv.so.2libsocket.so.1libresolv.so.2libcrypto.so.0.9.8/usr/local/libh ='hh0 =(h Nqhh ='hhyi Nqh$-C$0$1$MI $8$8$8$8B$9$9 7$9@>$9`R$9$9=$9X$9$:r$: $:@$:`$:!$:)$:$:$;.$; $;@$;`H$;c$;$;$;$<$< $<@g$<`g$<$<0$<t$<$=5$= $=@$=`$=$=$=$=$>$> $>@ $>`.$>$>$>$>$?g$? $?@$?`H$?($?$?A$?$@-$@ $@@$@`$@$@$@$@$A$A #$A@q$A`j$A$A$A$A$B$B $B@Z$B`$B'$B$Bz$BV$C$C $C@$C`\$C $C$Cb$C$D$D $D@M$D`$De$D*$D$Dw$E$E _$E@G$E`$E$E$E$EI$F$F $F@$F`$F$F$F$F$G $G $G@q$G`$G$Gn$G0$GK$HI$H [$H@$H`$H$H,$Hv$HJ$I$I $I@W$I`|$I$Io$I_$I$J$J $J@$J`u$Ji$J$J$J$K$K $K@8 [`+p  ^@ "a`+p  ^@ ra`+p  @ ra, @`+p '  \h th+``+p )  -`-! `?-! =--%! !#0@:`+p Ő @3#(`*p   Z! `*p   Z!@@|@2@@%@'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@Đw_@Čw__׀h__ߢ@_ע$@_@@ĔH H'Ӡ 'G'G< 0A@cw''G,084084a] @ HG ?G@'G'G<  w'w@Z" _G@@Zw@OB H_L H __h_ס<`4h?H'__$` 'G<  w'w@"" _G@@*w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@cԮwww_\ h_@__\  h`]`]@@ë IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@1@@@6@ww__\ @@W__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@¬/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@c4ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@`ww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @v@w_  @@w_L /HH H' 'G'G< 0www___@@Yw_߰@A@aw/`H]L@'4 0www___@ @t'G< 0www___@ @t'G< 0'wG_@ǐ@@@ǚw_@w_\ 0h _\ 0\ h H' 'G'G< wwwwww__@@|Ēw_h_____ϐ@@@~w_@~d_Hww_ǰww_\ wwϠ '_ w_ߠ w__ע Gˡ,PwGˠ '_נ h__ o__t`_w_0A@b\ww_`]@@pw_0A@awww_` ]_\ @@Z_`(]@@S_ h4  H `0]_@@N_\ h `8]_@@B_ @BH `@]_@@4_ @8_`H]_ k@@@_`P]_ *@ߐ@@_`X]_ @@Ґ@@_``]_\ X@@`h]_@@_ h4 ,00400< #?cH_`p]_ h4 ,00400< @@ה_ @_ @ߒA@c\ww`x]@: @@vw_`]@@Βw_瀤h`]@t_B @ɐ@@Hcp]!`]_@@_`]@@_`]@: @@|_`]@: @@q_`]@ @n  @@c_ @o`]_@@`__@@[_ @__ @[`]_@@L`]_@@Ea]\ w_a]h._\ a]h `]_@@*H `]_@@!_\ _@@_ @_\ w_a]o_ @`]_@@`]_@@_`]b]\ ah]\ @@_ @`]_@@ߒa]_@@ؒc0]\ ww_߀h_נ w_a]__\`X\`@@_\ w_߀o_ @a]_@@`]_@@waH]\ h%`]\ _ס,0\@w_߀h__ߐ@_\ w_߀o_נ w_aH]\  o`]\ h9_ @va]_@@ga ]_@@``]\ w_ǀh_Ǡ @_@P_ @T_\ _@@F_ @J_\ w_ǀo_ @?a(]_@@0a0]_@@)c`]\ h_a8]c`]\ @@H _b]\ @@y_ @a@]_@@aH]_@@_@.0A@cܮwww__@(@`]cp]!@@Ԓcp]!@@̒cp]! @͒ap]D H ap]$`@@@v@F 0A@bw',p,p H@ސwcp]\ _t`_`t`_,p,p t_cp]t``]\ @`]t` A@aܮwwcp]\ \ cp]\ \ h`] cp]\  w_\ w_נ 0_t`_w__@z_w_ A@`ww_ `,  `',0c]\@w_߀h__ߐ@;H_H w_\ w_߀ow_ A@`ww_ `,  `',0c]\@ww_߀h__ߐ@H_H%w_w_\ w_߀o_bw_t _t _׀h__t` H _,0c]t@_w_ww '_w_$ w_w_׀ h,$_L 4 @'_ w_w_$ w_w_ǀ o'4  A@bww_ h_ `  @wb ]\ w_瀤h_\`\`$_ hH _\ w_瀤o_瀤hF_c]\ 40 hc]\ _@c]t`_@yH9wc]\ c]\  @c]t`c]\  @bwb ]\ _t`_`t`_\c]\ @t_b ]t`_\ w_ \_@t_w_ ww__L@w_pw__ߐ@ @ǔ_w_0w_@u Tw__@@ _w_ A@cwwwb]\ w__!@< 4$ h%') `wb]\  _$@b]\ _t`_b]t`__< 4P$o_!@H5__!@< 4w___!@< ,%w__ߡ,0\@h `o__ߡ,0t@_'@$ _ߡ,0\@$@__ߡ,0\@w_!@O__,0\@h `]\  `]t`___,0t@A@`Įww_wb]\ ww__!@< 4$h4_߀h'_!@H!__!@< 4w___!@< ,%w__ϡ,0\@w_߀h_!@O_߀h__,0\@w_׀h aX]_@@/_w_ A@c<ww7_@`] rL"'c]\ ,0\@w_׀h_ H__פ @@CH_H:w_\ w_׀o  w_4`_ע _@@Ɣ _@,` c]\ ,0\@_t`_c]\ ,0t@`H]  `H]$`_w_ A@aLww_@@@2aH]\ l@L$@'`]\ ,0\@w_߀h__\ @@yH_H w_\ w_߀ow_ A@`Dww_@@@P2aH]\ l@L$@'_w_߀h a`]_@@>HHHah]_@@1H_@ ?O OO  w__t `]\ ,0\@_t`!_4`(!_4`>_t `_ߤ h!<,@ $_ߢ h%`,$`_`]\ ,0t@`]\  `]t`_w_0A@b$w' 4 @?,@ @''a] HH'a] $ ''4 A@a\w'wŐ'b]\ ӡ,0\@w_瀤h_  HH _w_\ w_瀤o_瀤h_߀h_\ _t`H _\ b]\ ӧ,0t@H 4 , @?,@ @'',0 w_$`_w_$ _$ _w_0A@cww_w_ W'b]\ ,0\@_t`_b]\ ,0t@@w '_ H_,0@\ @ '_  O_0w'_ G4 @>w_ _$`_ _$`_ߦ _``,0@/__w_ A@aĮwb]\ h:`] 'c]\ ,0@c]t`,0@w_b]t`'$ ''〤 H_`@t`_ @w'$ ''ۀ O_t b]\ w_\ b]t`_w_@A@`Tww_\ h_\ Db]\ _t`_b]t` w_hHNw_ w '_ H7_ӡ,0@\ wew__ߐ@ @@{_\ h_נ (H _\ ͐_t H_t __ӡ,0@t`Ӡ '_  O_ _$`_ _$`_w_0A@bLwb]ap]@$@'b]@ j @w_ ab]@@G_$`HG_ a@@Znb]t !P#K' ' 'b]\ @@Y _a]t` b]t`b]t `]\ aX]  4,0@@`]t _`]\ aX] ,0t@A@`|wb]\ ,0@Ow_wcp]\ w_ǀh:_\ w_w_Ϡ$ 0w_w__Ǣ h#aX] _ (4 ,00400< 4 H__t`_ߠ w_w_Ϡ$ 0w_w__Ǣ o_\ w_ǀo_b]\ ,0@w__׀hax]@_b]\ < a]\  M@@_w_@_w_0A@blwwb]a]\  M@$@'G뀤 Ha]@Ga]\ a]\ @@@ĻG@ʐHa]@ؐb]a]@}Ta]\  M@@$@a]\  ab]@@NHa]@b]a]@}*a]\  M@@$@_b]@@N̒Ha]@f_@gb]w_0A@`w ' 4 aX] H/`]\ G,0\@wb]a]@|_ M@@$@;b]@G 'G4 aX]  OA@bwa]\ w@FGb]\ ha]\ @8>b]\ ې@@Wϔ a]\ D Ha]\  Ma]\  ab]\ < b]\ @@@Ϛa]\ D H@АRw_@@: @@;˔ w_h a]_ϐ@@_`]\ aX] ,0t@_@`]\ Gˡ,0\@w_h'_ h4 ,00400< 4 aX] HbH]\ a]_ k@Ð@@_\ w_oGˠ 'GaH]\  o`]\ w_h"_ aX] HbH]\ a]_ @@@_\ w_obH]\  @bH]\ @*aX]  aX]$`H _D H@ _ a@_@`H]@z`H]t  `H]$` P㽀A@bw7'wwwwעWa]G4 @y_\ @@ݖ < w7@ߐϦW_7@@_\`\`@ؔCϐ@@ے_߀h_ߠC@tC;w_׀h__@w_\ hG,0`]@wW_W\` t` H5'ӢCc(]\ @@IXh&_ (G4 , !<,@$_ (%`,$` _t`G,0`]@wW_W\` t` H'_瀤h'_\ h"_ c]\ h_\  h_\`\`_\`\`/$`@@ٚwHw_  p@_`]D a]@@N p_h_w__/'gH_\ w__\ 'g_g@@A'@ېw_ww_@>  H _@7  O_@9`h]\ hb]\  <_@`h]\ hb]\  <_@b]\ hS_ (aX]  ,00400, !,@$_h>_@w_  p@'_`]D a]@@Җ p_\ w__\ 'g_g@@ϒ'_@@w_ww_@ʒ  H _@Ò  O_@Ő_ (G4 , !<,@$_ (aX] ,00400, !,@$b]\ _t`b]\  b]t`G,0b]@wW_W\` t`b]\ @V_hy_\ _\ $@wO _'KG,0`]@w?_?\` t`_ (_  %,@ % _/W @@H: w7b]\ W_7< @@U3b]\ _7@b]t`_O WK,@_O<0wO_OhWK$ 'K_O$ wO_O  K,@_O<0wO_Oob]\ WK@  `< @@Tb]\ K  `@b]t`H+_ ( %,@ % G_\ W@@: w7b]\ W_7< @@Tϔb]\ _7@b]t`b]\ __< @@Tb]\ _@b]t`b]\ @Ut_$ _@o_@l_瀤h!_ $4  H __ `\@OtH__@Β_\ _t`_ (!<,@_$`  'Gӡ< 0A@cwwwa]\ D H_@@Ha]\ D H_@@ _ha]@3_b]t`_b]\ t`hb]\ $ _b]\   ``@O$㽐A@blw?www!@ww_@ɐw_@ŐwǠ 'c`h]\ hb]\  <_@`h]\ hb]\  <_@b]\ hb]\ wb]\ @Sjb]\  ׂ ``@O$__a]_@@ < w__ha]@7@7__@@b]\ @Teg  p@g`]D a]@@ p__ǐ @@: w__ha]@P__@w__$@'Gc H_ h?oH;__h_HwO_wO_OwW__Wcp]@ @wG_Ghcp]@H a]_@@7__G@@?_wg_G'o__G$@w_h H'_ '_gG_@@I'cHGc H__Ǡ$@wWb]\ __W< @@Sb]\ _W@b]t`_w_'H Hb]Gc@@ĒHGc H?OOO OOGc Olg@7@@_h_@{Bw_瀤h__@~_\ h`]\  `]t`_א@GHnc(]\ @@E]h%_ (%!<,@$_ (%`,$` _t``]\  `]t`_א@H@_ ( %,@ % _ (%!<,@$_ (aX] ,00400, !,@$__t`b]\ @S'_$ b]\  b]t`b]\  b]t`_@_@  A@bwww`]\ _ (4 ,004004,0\@w_a]\ h_\ b]\  h_@2 b]\ @Qb]\  _t`__\ _@@Aw_ ww 7_L /'_ wG'Gۀ H 怤 0H怤 7HHw, $ 0@7_L /'_ wG'GӀ O_4`_w_A@`ww_ (w w__t`_ HH_ːw_瀤h<_ (4 ,00400< #?cH1_ (4  < H b]_@@@7_ (!<,@_$`_א@-w_πh b]_@@@H_àא@/w_πhGÀ H b]_@l@@_w___ע@hf3w_``H _`< @w__t`_ߠ w_ ' _$`__,0@t`_t __ &@w_ǀh b]_@)@@_`&4`_@@: @_t __\   < @w_ _ǐ@쁒_ (_ǐ@{_Ǡ w___ע@o_@ ww_\ w_\ w_\` `_\` `< @@@1'G<  ww_\ w_\ w_\` `&_\` `< &@@@ 'G< A@cww'wwGH_``4b ]@ @H_``4b(]@ @ '_ H'_ۡ,0@\ G,p,p,p H__ۡ,0@\ \   "< w۠ '_  O_陒__ߐ@@sb '_ HC_ۡ,0@\ w_G,p,p,p HH&_b0]_G,p,p,p \ @@@vE __G,p,p,p  @u۠ '_  OA@`Ԯww_\ wb8] (`\@Ot_ (HH '_ H_,0@\ \ h _,0@\ ΐ '_  O_ ﹐w_ !< @#H+_h'_ (4 ,00400< 4 aX] H_`]@ `] `\@Ot_\ w_ '_b8]@ הb8]_ ( @/_$` ' ' '_ H0_,0@\ w_ &H_`&4`_``(꼒__,0@t` 'H_j ' '_  O_ $@$A@awwww7w_\ w_@0 /w_h__$@'H_@'Hb@]@_h!< @#H _׀hbH]@x '_ H_,0@\ w_\  Hw__\  @@Hk_h7!< @#H_ &値H_ (_@H H'_4`&_ (_@_\ h_\ __t`_ ( H'_ &!< @#H l _t !_4`&_\ h___ __א@@GH _ ( Hk'Ӡ Hh' '_  Ow_``H _`< @w__t`w_< @_t  _4`_ 鳐_Ǡ w_ ' _$`__,0@t`_h _t !_4`&__ __א@@H__t`_4`&_ (_@l_ (z 'Gӡ< www_\ w_@ /w_πh__$@'H_@'Ǡ '_ Ho_ˡ,0@\ w_\  ǀHY__\  ǐ@@HM_πh _ &!< @#HHK_\ h___Ϧ _@H) '_\  H_\ ǡ,0@\  &H _ ( HC'Ǡ '_\   OwH H3'ˠ '_  O H''_瀤h __@ @@;H _\ h_\ _t  _4`&_ (軐_ (跐 'G< www_\ w_@; /w_πh__$@'H_@'Ǡ '_ He_ˡ,0@\ w_\  ǀHO__\  ǐ@@֔HC_πh"__@ @@_\ h_ (H _\ "_t H_t  H*'_ &!< @#H H'_\ h_֐__Ϧ _@H 'ˠ '_  O 'G< A@cTww_ kŐH H'_\ Ӑ@@H_\ Ӑ@@ w_瀤h bP]_\ @@Hj'a]D HI瓐HCӐ@ w_ k@ w_ǀh_h_@~bX]_\ @@fH='_Ǡ@@* H_@hb`]_\``k@|@|@@l?H'__ k`]\ @@ H bh]_\ @@-H' 'G< 0A@`w 'aH]\ h,`]\ ,0\@w_ha]D _9@a]$`_\ w_o 'aH]\  o0A@cw`]\ w_h:bp]_\ @@K@ْw_h__ c]\ @@ Ha]D bh]@@a]$`_\ w_o A@btwwww_!@H"w__ߡ,0\@h___ߦ@_!@< ,-0\@@ܔ_ߠ w_߀$ oH#w__ߡ,0\@h_bx]__߬@_,\@@{@@C_ߠ w_߀$ o0A@awc`]\ hHb]c`]\ @@d 'G Ha]D b]c`]@\ B @.@@a]$`HG`]@@w_h%@B 'b]@a]D b]c`]\ G@@@Ҕa]$`Hj?b]$`_b]\ @3_@H_@H%@B 'b]@La]D b]c`]\ G@@@a]$`H-b]@H%@TB 'b]@!a]D b]c`]\ G@@@ja]$`HA@aw`]\ b]@@jw_h b]`]\ @@_@"@=h@ wO :H_瀤h b]@@ _,`@@% w_h_h_L H _ Ӑ@@XH b]@@Zɐw_ǀh/Ӑ@<ʒ'GÀ H b]@y@@?w_Ǧ (Gá4 , !<,@$_Ǣ (%`,$` _t`__ǐ@_@"@o_@0A@bw``]D H  c@]$`?H'c@]D H  c@]$`H`]@l!`]cp]@@q ``]$ ``]D H?Hq'a]D H`]\ b]@@k`H`]\ b]@@kSH@cP]\ w_h _\ cP]t`Ha]\ w_\ a]t`a]a]\ t`_\ @`]\ _t`c]\ _t`a]\ _t`_\ t`_c]t``]\ L #Oi 'G< @A@cwcp]@'G H G?H Gcp]@@@A@cdw`]\ b]@@j’H`]\  @ a]t Ha]t A@bwwww_ᩒ`]\ b]@@jH b]`]\ @@ؒ`]\  b]@@jsHQ`]\  @yw`]\ $ w`]@j`]cp]@@pĔ ?H b]_א@@_`]\ h_`]\ @@HH_`]@ᰒ_t O_@HW`]\  @ ww_w_h __׀h__t` H?'__׀hb]@U__ǀh,___Ϣ$@cp]@@ow_hcp]@H a]__Ϡ$@@/__@w__ǀ o 'G<  ww'_w@z" _א@@{ w@pB H __h_L H?H@'_נ w_L -H _L +H?H0'_ א@@P w@EB H__ h_L H __ߤ$@G뀤h_π%xh?H '__ߐ@@ 'G<  A@a\ww_@c >w_瀤h b]_@@_ w_L H b]_@@_ w__$@w_ߠ @cw___ߐ@@eHG___ߠ@S  H c]__@@[HD___ߠ@@X  H c]__@@EH.c]_@@8H c]_@@2__ߠ@@Y H`]D $  H , < #bl_ϠD_w_@ A@bԮwwwwap]c]\ ߐ@Hap]___ @H_h _@i_t _$ ___@@A@a̮w`H]a]\ @ A@aTwb]\ hvb]\ ah]\  hk?wwwc0]\ w_h_\ X_h_w_\ Xw_w_\ w_o_߀h _\ w_\ _t`H c0]\ w_\ c0]t`_ h!<,@ $_t _\ h_\ 0_t b]\ $ b]t`b]\ h b]\ ah]\ o@A@c,ww_ _ h4  H'_ h%`,$`c0]\ _t`_c0]t`b]\  b]t`c]\  c]t`@w /_40w_h  /_40w_o/4  A@aww/w ' ' H c ]@@H( /_L@_,@ߠ ' '_L@_,@ߠ ' '_,@ '$ /O_@_ߤ  (4@ɔ _ߠ @ (4,PA@`$wwww'w'/_\ ww_ 's_hs_ H_s,0@\ w_\  @'o'k_ &Ho (H _\  <` @ ?$@HH_@_\ ` `@kkH  /_k,@k 'k_k@_\ ` `@Uk_\  @'k _k,@o (H<_K@@tH4_Ҡ@"_נ@@ӒH__ w_HF__נw@NH c(]_א@@__w@@H%_ &!< @#H_\ h_/_____o_kҐ@@< 6sw__\ ws 's_hs_  OB__w_ǰw/_@@  < sw_A@` ww`]\  w_wߠ 7_ɐ@w_h c0]`]\ @@1H! @7H;c0]`]\ @@H/ɡ< # a퀤H!H!OOO# aOO#(H # a퀤OO#(O#8OO_L :H_ @@ `wߢ_ߐ@޺_wH,_c8]@@ewH  7_ wH_ː@@rH c@]`]\ @@$w_ (w_L '_ wG'G H cH]`]\ @@`p]L`p]_@@ɔH_L H cP]`]\ @@w`p]\ w!< @#8H2ǀHcX]`]\ @@]H _߀h_ (4  < Hc`]_ (4  @`]\ @@=HmǀH"_`p]\ h`p]_@>`p]\ w`H]ː@HI_߀h_ (4  < Hch]_ (4  @`]\ @@H)ː@5o'GÀ H cp]`]\ @@GÀ Hch]Gá4 @`]\ @@Д_ _ɐ@0A@aww`]\  wa]ca]_@@H_L H cP]`]\ @@a]\ w_ _@A@`ww'`]\  wa] a]_א@@H_L H cx]`]\ @@KH_@ђ w_׀h cx]`]\ @@6a]__נ$@@ga]\ w_נ w_L H c]`]\ @@_wch]ǒch]_ߠא@@DH_L H c]`]\ @@ch]\ wߠ  @@sGH _ _@aH _ _@H c]_@@ƒ_ _ߨ_@@㾠A@a|wWw/`]\  w_wߠ 7q_L :H_ g@@@ wߢ_ߐ@j_gwH,_c8]@@c'H  7q_ wH_Ð@@pH c@]`]\ @@eԐw_ (w_L 'k_ wGk'gGg H cH]`]\ @@H_㡐w_׀h _נ k@HW_L :H#_ @ٔ wg_g⧐w___ (4  < H c]_g@@__@H1_@@OH"c]\ g@@5@w___h_g .h c]_@@__@H c]_@@qH"_a]\ ha]_@a]\ w`H]Ð@HO_߀h_ (4  < Hch]_ (4  @b`]\ @@H/DH)Ð@3'kGk H cp]`]\ @@Gk Hch]Gk4 @3`]\ @@r@oאs@_ s@H_ `\`$ t`HH_ `\` t`_\ `n/p@ops@_ sÐ@ @w_\ _ *_ @_ _t `0A@bwwww_h_瀤 .h c]_ k@ob@@c]_@ @QH _ _ @@@oH c]_ k@oA@@Ւ_`*`@ڭ0A@a̮ww_ kcH _ *ڤ_ @ڠH_ kc]\ @@4B@w___@_@A@`ww`]\ c]@@a#H H'_\ h_\ Z_t `]\ @  w_ἐw__߀h c]_\ @@RHz_߀h_ߠ @w_ k_ߠ k@ _ *_א@_ @_א@H^_L :HD_ @Ք w_࣐w_ (4  < H c]_א@@_ k_ϐ@_ (4 ,00400< #?cH_Ϡǐ@w___ǐ@_@H_+H__ k@@MmH_H c]_@@ؒ 'G< A@aԮwww_w_$ `]\ c]@@`XH`]\ @4  wϐ @w_w_ǀh _ߢ _Ǡ k@qHZ_L :H$_Ϡ @2 w_w_ (4  < H c]_@@r_ߢ _@JH3__ߠ @@LH#_ߢ c]\ @@2@w_h_ .h c]_ϐ@@E_@FH c]_ϐ@@9_t _瀤h__t`H_w_w_` $``]\ c]@@_Oj_w_A@bܮwwww`]\ @  w_Qw_h_w`]\ c]@@_H `]\  ,w`]\ c]@@_mH `]\  w_߀hc]@`@]_ܐw_\ h ah]\ h_t_\ `/`]\  h`]\ c]@@_ H_ Hg`]\ c]@@_H_HU`]\ `]@@^H_ג HB`]\ `]@@^H_Ē H/`]\ `]@@^֒H _Ґ@xH`]`]\ @@H_H  c@]$`H?HH `]\  o}_\ `/ҀH _ ѐ@_t `_ c_`*`@`]՗`]` ]_ @@l;@@bj_ krH`]`(]_ k@l%@@bT_׀h_\ w`]`(]_נ @l@@b>_@_w_׀o_瀤h_Hw_w`]`0]__ߐ@@b`]`@]@դ_@{_@x`]_ ka]\  @䓘H_ haX] ,00400, !,@$b]\ _t`XA@c̮ww`]\ @Ē  w (aw_t __t `]\ h _`]\ t`H_`]t`_`]t``]\ c]@@]H `8]`]\ @@`]\ @~  w_Iw_߀h _ߠ k@ 'HI_L :H_ @ w_Mw_ (4  '_@֢H-_@@JCH@ϔw_h_ .h c]_@@_@H c]_@@`]\ `@]@@]1H`]\  ِwtHwc]z`](`]`H]@j͐G4 @_\ @@@`_瀤h `]`P]_@@``]4 `]c]@c_@:`]_נ  @YH!?#_$`HaX] _$`0A@cw`]\ @  w_^w_h_ k_ *_ @_\ h_\ Ԑ_t H_w_O`]\  h c@]$`@A@a̮wb]\ h 5ua@A@aLw`]\ `]\ cp] @ @[cp]  @4cp] @2 A@`ww`X]ׂ `\@Otc]D H_^H_@Hw``]@] _@@_א@\w_0A@cww'`]\ hGH`h]@"b(]D H]_`]t `]\ @4Gb(]$`@A@bww_`p]@@H  `]$`H+_`x]@@ےH  `]$`H_c]@@ʒH  `]$`H `]_@@@A@aww_@ c]t c]\ #ch `]c@@A@`ܮww_@ ah]t @A@`|ww_c`]t c`]\ @@A@` wwbH]\ hbH]\ @_`]@@bH]t bH]\ h `]_@@K0A@c4ww_`]@@ZH2_ @@ H H'_#h`]_@@_,0wH_#h `]@!w_`h]t`Ht_`]@@Z~H_ @@ H H`'_c]t`HW_`]@@ZaH_ HG_`]@@ZQH_ H7_`]@@ZAH_ @H'_`]@@Z1H  b]$`H_`]@@Z H  b]$`H H' 'G< 0A@`4ww'_`]@@YH_ NHZ_`]@@YH _ G@HH_`]@@YՒH_ H8_a]@@YŒH  c]$`H'_a]@@YH  c]$`H_a]@@YH  a]$`H H' 'G< @A@b<w`]\  wa]D H a]_@@Ȓ_f HH a ]_@@@A@atw`]\  wa]D H a(]_@@_uHH a0]_@@A@`wwww_a8]@@jH__@@: c]t`c]\ #ch!#c]t` H'_a@]@@DH,__@@x'G߀?H?'HG߀ HG߀ H aH]Gߐ@@(G`]$` b@]$` Hf'_aP]@@H __@@D2 `]$``] H aX]`] @@ H>'_a`]@@H__@@8`h]t  H('_ah]@@ҒH__@@: w_׀ h_Hww_c]t`___@@%'G< 0A@~adw '`]D 4 HAa8]\ ,0\@w_L -H _ax]@@HH(_ `HH_  HH a]_@@h '`]D 4  O`]D 4 H`P]@8 a]$``]\ h A@}cw'w_\ @^G H_\ a]@@H`P]@@Hap]@&b@]D Ha]D H `]D `]$``] `]@W`]  @c]t aH]\  @`]t a]  @b]t  `{b]t G`]$`_a8]t`cx] ,p,pܐcP]t  'cx] $ H"cP]\  4,p,pcP]\ -0-0t` 'cx] $  OcP]\ cx] $ 4,p,pt @ڈaP]@[?Ha]`]\ @@THH`]\ a]@@VH:H`]\ a]@@VܒHHp`]\ a]@@V˒HH_a]`]\ @@HKHN`]\ a]@@VHZH=`]\ b]@@VH H,`]\ a]@@VH.H`]\ b]@@VvHH a]`]\ @@?O\a]D H ۥ/oYbH]\ hbH]\ @xb] H`wwנ '瀤 h_,0b]\@w '瀤  o '瀤 h_,0`]\@w '瀤  ocp]!a]_\ @@cp]!a]@@cp]!a]`]\ @@ݔcp]!a]__א@@ϖcp]!a]b]\ `]\ `]\ @@@cp]!a]b]\ `]\ `]\ @@@cp]!b]b]\ `]\ `]\ @@@cp]!b]b]\ `]\ `]\ @@@kcp]!b]`]\ c]\ @@Wcp]!b] b]\ !@< ,,0 `]\ @@@=cp]!b ]`H] @@.cp]!b(]c]\ `]\ ,p,p40 @@cp]!b0]c]\ 40 @@cp]!b8]`]\ ,p,p40 @cp]!a]@@@cp]!a]@@ݒcp]!b@]@@Ӓa]D @ 'G'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@Hw___ߐ@@H__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@ _w_߰@w_w_ ww_@ؐw_@Ԑw__׀h__ߢ@_ע$@_@@ܔH H'Ӡ 'G'G< 0A@z`<w''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@j" _G@@rw@_B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@y`www_\ h_@T__\  hbH]bP]@@ IH H' '__t` _\ _,@0w'_ @S_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@y@@@V~@ww__\ @@S__\`\`@Ve@w_$ _$ _t 0ww_ (! @'__@ @@C_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@x`Tww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@wa<ww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@#fw_߰@A@vc w/`H]L@'4 0www___@ @>ߘ'G< 0www___@ @>'G< 0'wG_@@@@w_@w_\ 0h _\ 0\ h H' 'G'G<  www_\ _t`_ !< @# H_  @@?? !'G! @H'_ !< @#(H!(H'_ !< @!$@#8@?!'< @'G'G'Gۡ4 _$`_\ w_߰0ww__$@$ 'G< 0ww_D _\`\`'G<  A@ucwww'_ 4  < HG뀤HbX]_@]g_ 4  @{@@Hw_w_߰0w'_@~V@c w_߀hH w__G@w_@w_ڒ w_0w_@|+w_瀤h @_@ @|єHw_ 4  H _%!,@$__@q w_A@u`www_ _ hHw_b`]@ @HHw__h_L '_ wG'G׀ H__h_L '_ wG'Gπ O_ _ hHjw_bh]@ @HH\w__h_L '_ wG'G׀ >H__h_L '_ wG'Gπ >O__ hH3w_w__h_L '_ wG'G׀ H__h_L '_ wG'Gπ O__ hHw_ߐ@C w_ A@taĮww 'c]D 'GG뀤H/GG@<` @< 'c]\ G,0\@w__ߐ@'GۀHGH'Gۀ HG'HG 'GG뀤OG $ 'G< 0A@t`ww'_'G뀤 HGH_@aHc]\ G,0\@@V_c]\ G,0t@ Hc'G $ 'c]D  'Gc]$`c8]D G瀤H!c8]D  ,`@<` @< c8]$`c]\ c8]D ,0@@c]t Gc]D Hc]\ G-p c]\ c]D $$ < ,0@@Ĕ_c]\ G,0t@ 'G<  A@sbww'wGH,_G$ < L@,084084a] @ HG$ ' _G,@GH_G$ < L@,084084a] @  O_L #H_L HHswG <``).! L )$@Hbp]_@@_@HXwG <  ).! $ 'G,`,`<  ,@RwG_$`__א@@ZHO (' (GH-_GL@ HO_G _׃<``).! < ,p,p @ZgHOG )'GGO_w_ A@rc$ww_bx]@@ʒw_瀤h?H7'_@$@h)@F'G@w_׀hH_אw H b]@@{_@$@}o_@ 'G< @A@ra̮wa]D HH@Nw_@A a]$`0A@ra<ww_Ԑ'G뀤 HH wc]\ G,0\@w_ A@r`ww' ' 'c]D H=c]\ G,0\@D  H'c]\ G,0\@@YwG 'GH _b]_ߐ@@H __ߐ@ _ G 'Gc]D OG'G< 0A@qc4ww_T'G H?H/'G c]D Hc]\ G-pc]\  c]D $$ < ,0@@c]D $ c]$` 'G< A@qb wwww_w_w_ 4  H H'_%`,$`__@w__Ϣ .h_b]@ @ H_L - Hb]_ @X@@~/H'_Ϣ @@X Hb]_ @X@@~H'@o_t 8_Ϡ .w_ 0w_ ̐w_Ϡ 0_׀he_b]@ @HY__Ϣ 0h_Ϣ @@XH_L / Hb]_ @X@@}֒Hl'_Ϡ 0w_h_D  H bx]D HHrw_h __@@Ē w_Ϡ 0_׀h_b]@ @iO_h) '_D H"_G,p,p @w_hH __@@ wG 'G_D O__א@V_t ( 'G<  A@paww_h?HW'_ 4  H HN'_ ߐ@ww_瀤hb]_ @W@@}/H4'G H_@b]_ @W@@}H '___ߐ@'a@]D H GۀH__t`@ H'_@eG'G< 0ww @8w__t`_\ _t`__t`_w_@w '_h  '_\ w_o'4 @w_h_w_\ w_@ _o ww_w_\ w_׀h_\ \ (_\ (hH _נ w_\ w_׀o__ߐ@w_@ww_\ h_\ \ Ò_\`\ t`_\ o__t`w'_\ \ w_\ 0w_\ w_\`\ t`_@_׀h0_\ w_ʐH _ !<,@< 4  @H_`%,< 4 @, %`__@o_\ w_׀o_w_0w'_hC_ !<,@< 4 HH5_`%,< 4 < @, %`_\ 0w_瀤hH_\ w_瀤h_\ @ɒ_\ w_瀤o_\ 0\ w_o w_\ w_瀤h_\ Hww_w_瀤h _\ _t`_@_w_w'_\ w_߀hH_t _w_瀤h _\ w_$` _\ w_瀤o_w_瀤h%_\ \ 0w_h_\ w_ H_ ` $`_\ w_o_\ w_瀤owנw_w_瀤h_\ w_ H __ϐ@, w_\ w_瀤oGH_w_t _׀hK_w_\ w_t _\ w_\ 0w_h._\ w_ HH_ $ '_$`  HGH _א@=H_א@ޒ_\ w_o_$ __t`_ w_׀o0w_h_\ w_\ w_ !<,@< 4 !@HH_Hw_ow_w'wwwϠ 'GH__Gˡ,0\@h _ϐ@}HwGˠ 'GGO_HHwߠ 'GH_Gˡ,0\@HHwGˠ 'GGO_`%,< 4 !@@, %`_א@ 'GH$-1<_Gˡ,0\@w_ ,@< 4 , @% _Gˡ,0\@x@Gˠ 'GGO_Uh~_\ w_\ w_@__w_ !<,@< 4 ! @'G4 #H%_ !<,@< 4 ! @H_`%,< 4 !@, %`_ϐ@1G4 !@'_\ 0w_h3_\ w_\ w_ !<,@< G @HH_IHHIw_`%,< G@4 , %`_א@_o_ِo_ѐ_ww_׀h!_\ w_\  !<,@< 4 ! @H_\ ϐ@Β_@ɐ_w_׀o_w_߰wwϠw_w_瀤hK_πh _\ _ǐ@K wH6ww_w_߀h,_\ _\ @@ w_h_wH__t`_w_׀h _w_\ w_׀o_\ w_߀o_w_\ w_瀤o_w_A@kcww'w'_G_@[w 'GH__G,0\@h_H+wG 'GGO_h_\ h*G〤H#_`] @ 'GH_G,0\@`] @G 'GGO_Hwנ '_w_πh _\ wG '_πoG @w_wϠ '_πh_\ _G,0t@G '_\ w_πo__`] @g 'GH_G,0\@`] @VG 'GGO 'G$ HtG 'GGHc_G,0\@h _G,0\@hHK_G,0\@G,0@ w_G,0\@`] @_G,0\@`] @ _w_πh#_G,0\@_\ h_G,0t@_G,0\@_\ h_G,0t@_\ w_πoG 'GGOG 'GG$ Ow 'GH_G,0\@h _G,0\@@G 'GGO_@ _w_װ0ww'_G@ w_߰0ww_h H'_h_\ w_\ w_ߠ@@ H H'_o 'G< A@ja|www' 'G뀤 H __\ @ wHb]@v_w_׀h_ _\  @H 'H _\ w_׀o_aG'G< wwߠw_hHw_ww_瀤h _Ǡ w_\ w_瀤o_ǐ @.w_w_瀤hm_w_h_\ _\ hH _\ w_o_hHJw_w_h_\ _\ hH _\ __,0t@_ w_\ w_o_h_\ _< _ϐ@ wHw_h _\ _\ h _\ _א@ w_ɐ_\ w_瀤o_@~א_w_A@ia̮wwwwww_@ c]\ @}#'ˠ@Cb]_@Q @@G;_瀤h_\ wb]_\  @P@@G&_@~_w_瀤o_׀h@Y wעb]_א@@G b]@Y3 @@GGˀHb]c]\ @@F _@GˀH_@zH cp]!b]@@}Ւ__c]\ _ߐ@@G'Ϡ@COG'Gӡ< @'!?< @4 !? @4 !?# @,  `, '4  ww_@}Bw___ߐ@@}B__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@}_w_߰@w_w_ ww_@|Ґw_@|ΐw__׀h__ߢ@_ע$@_@@|֔H H'Ӡ 'G'G< 0A@h`$w''G,084084a] @ HG ?G@'G'G<  w'w@|" _G@@|w@|B H_L H __h_ס<`4h?H'__$` 'G<  w'w@|d" _G@@|lw@|YB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@g`ܮwww_\ h_@B__\  hb]c]@@{ IH H' '__t` _\ _,@0w'_ @A_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@{s@@@Dx@ww__\ @@A__\`\`@D_@w_$ _$ _t 0ww_ (! @'__@ @@{=_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@z/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@f`<ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@ea$ww_b@]@ @y`?'G'G< 0ww__@ @y'G< @ww__@ @y@w_  @y@w_L /HH H' 'G'G< 0www___@@`w_߰@A@dbw/`H]L@'4 0www___@ @,٘'G< 0www___@ @,'G< 0'wG_@y @@@ܔw_@w_L H_L ,08408< C _, _ w_L OA@d`ܮwwc]ς `\@Ot_@Iϐ@@O =w_\ \ h_\ \`\`$ L =H _\ \`$ @Ғ_\ @?0_\ \ h_@@?H2'  @~,w_\ @>~_t _\ h _\ @?_\ @>n_t _@@_\ }_c@]\ t`_c@]t` 'G< 0A@cbw '`0]\ w_h1_@y'G H_@y'G H G_@@y 'G H b]D  b]$`G?H  `]$` 'G'G<  A@cawנ ' ' ' ''G뀤# hHwG〤 HGHHw Gc],@c]HwGHOG,084084a] @ HGH G뀤HG 'OGHG〤 ;HG〤 #H 'OG瀤H Gc],@G 'G$ 'G瀤OG〤 \HF@'HO 'H0 'H, 'H(H&HDwG〤 bH "H OOO \OO nH bOOO tOOGGc],@G 'OZG〤 "HG  'OPGGc],@G 'OE_0A@bb\w'G,084084a] @  HG -H H' 'G'G< A@bawwww''`]D HH GHHGۡ,08408< A _,@ '〤! H?H;'O _,@Gۀ HGۀ H r'Gۀ OGۀ OwGۀ HGۀ =H?H'w_πh?H '____@'Gߡ< 0A@acܮww''G H?HW','G,084084a] @  OG "H?HA' ._G,@G ''G瀤 H?H0'G瀤 "HH G瀤 \H 'G瀤 H?H'G_G,@G 'G H?H'O ]H?H'G'G< 0A@abww ''`]D H?H@'G뀤 ]HGH9'G,084084a] @ H _GG@cH%'GH G뀤 .H?H'G H?H'G,08408< 4 _G,@G 'OG< A@a`www ' 'b]\ w['_׀h _ HGӡ,08408< _ H_נ wO_b]\ hHwGӀ H`]D H H' 'OG瀤H Gӡ,084084a] @ HOGӀ #HGӀ ;H 'OGӀ [H`]4'G〤 HHA .G`],@G ' G`],@OGӡ,084084a] @  HH"Gӡ,08408<  G`],@__`]G 4 @ HHO_c]b]D b`]\ @@lG< 0A@`adwww_L H H7'_c]@@vqH _\`,0 t` H&'_c ]@@v`H _\`,0t` H'_c(]@@vOH _\`,0t` H' 'G<  ww_h#_L H_ߐ@@tƔ wנ w_ߠϐ@H H '__ϠL@_t` H' 'G<  ww_h_L H_ߐ@@t w_ߠא@dH H '__t` H' 'G< @A@_bwwb`]\ hc0]_b`]\ @@kڔc8]_@@kӒ0www_ߐ@gH_ɐ_'G< 0ww_ߐ@H__w_0A@_awww_h HN'_L H HF'_c@]@@uRH_cH]@@uHH _cP]@@u>H H%'_cX]@@u1H_c`]@@u'H _ch]@@uH H'?'G< 0www__@'G߀ H _$`GH '_$ __@K'G< 0ww__@Д@ 'G'G< 0www_瀤h_@ǐH '_@w_t  'G<  A@^awwww_瀤h_@H'_@x_t _\ h cp]_@@j 'G< 0A@^a$www_cx]@@sH__@a]$  H['_c]@@ssH__@kb]$  HE'_c]@@s]H__@U`p]$  H/'_c]@@sGH__@?a]$  H'_c]@@s1H__@)`]$  H'_c]@@sH__@a(]$  H'_c]@@sH__@`]$  H'_c]@@rH__@a]$  H'_c]@@rْH__@ђc]$  H'_c]@@rÒH__@b]$  H'_c]@@rH,__@'G瀤?H?'HG瀤 HG瀤 H c]G@@iG`8]$` b]$` Ha'_c]@@ryH8__@'G瀤?H?'HG瀤 HG瀤 H c]G@@i]G`]$` a]$`b]D HG`8]$` H!'_c]@@r9H3@s* '__@i: a]t`a]\ Gl@a]t`a]\ h a]t`a]\ GL@a]t` H'_c]@@qH__@2: a`]t` H'_c]@@qH__@: cP]t` H'_c]@@qВH2_h"_`]@@rHa0]D H `]@iSH'?c`]$` H'__@c`]$  H~'_`]@@qH$_h_`]@@rhH  aP]$` H^'__@xaP]$  HR'_` ]@@qjH^_h_`(]@@r<H  a0]$`H1_h_`0]@@r'H  a0]$`H_h_`8]@@rH  a0]$`H a0]$`a0]D Hc`]D ?H `]@hH' H'_`@]@@qH _@t`]t  H'_`H]@@pH c]__@H'_`P]@@pےH ah]__@H'_`X]@@pƒH a ]__@H'_``]@@pH_h_@ H'_@` ]$  H|'_`h]@@pH__@b]$  Hf'_`p]@@p~H__@vc]$  HP'_`x]@@phH._`]@@p^H  ch]$`H_`]@@pMH  ch]$`H `]_@@g@ H'_`]@@p2H__@*cP]$  H' 'G< 0A@Zcwww_`]@@pH _h_@ HA'a@]_@#@pcH]D  cH]$` H,'_`]@@oߒH _h_@ H'a]_@#@pcH]D  cH]$` H' 'G< 0A@Zawww_`]@@oH c]__@H'_`]@@oH c]__@H' 'G< 0A@Z`www_`]@@oaH$_h_`]@@p3H  c]$` Hs'__@Cc]$  Hg'_`]@@o5H[_h_@ HT'_`]@@o"H  a]$`H>_`]@@oH  a]$`H-_`]@@oH  a]$`H_`]@@nH  a]$`H `]_@@f~H' H' 'G< 0A@Yawww_`]@@nHb_h_@ H['_a]@@nH  bh]$`HE_a]@@nH  bh]$`H4_a]@@nH  bh]$`H#_a]@@nwH  bh]$`Ha ]__@@fa(]@eH' H' 'G< 0A@Xcwww_a0]@@n=H b]__@KH' 'G<  A@XcLwwww_a8]@@2H __@tH'_a@]@@2H __@Hy'_aH]@@2H __@:Hh'_aP]@@2H __@iHW'_aX]@@2xH __@HF'_a`]@@2gH __@kH5'_ah]@@2VH __@@H$'_ap]@@mH _ax]@@mH__@b]$  H' 'G<  A@X`wwww_a]@@m$wנ?'_׀h#_`0]t`_b`]t` b]$` `]$`__@В'_@m b`]t G'G< @A@Wc|wc]\ h a]@8c]t c]\ w_0w'_@mw_瀤h __@H'G'G'G< @A@Wb\wa]Ԓ `?'G'G< @A@Wawa] `?'G'G<  A@Wa|www` ]\ w_߀h__\`\`_@ H?H '_\ w_߀o 'G< A@W`www ' 'wwcX]\ h_cX]\ _@H'qH$ @mMHG__@ϔ'G 'a]@m/wlH'_πh#a]_ϐ@@nT@ow_ǐ@m HG___@'G '_@la]@nw_א@m HG___@'G '_@k` ]\ h G__@6'G 'G〤 H?H'G'G<  A@Vawww_c]\ @@kH"c]\ hc]\ _@ @ `?'c]D G㠜@H H' 'G'G< A@V`ܮwwwwH__@HKc]! ,D@ Hc]D H a]_@@c"Hc]! ,D@"H a]_@@b H'`0]\ @lIc]! ,D@,0c] (t@c]%,D@ c]$@H_@k .w__$@w_c]D h_c]\ _Ϡ @@jtH  c]%($@H`0]\ @lc]! ,D@,0c] (t@__@#H(`0]\ @kc]! ,D@,0c] (t@ c]%($@c]%,D@ c]$@H8_@k ._"c]D h+_c]\ c]D @@iH c]%($@`0]\ @kc]! ,D@,0c] (t@Hc]! (D@ H , < #`p_ǠD 'G< \t\(0A@U`Lwwa]_@@a 'G< A@Tcܮw'wa] `\@Ot_c]D @ .@k<w_߀hGa]__$@< @2@__$@ 'Gc]D H$_GL@ "H_GL@ \H \_GL@G۠ 'Gc]D Oa]@Ha]c]D _@@2tG__ǐ@@o_ǀ "h 'נ@.G'G< A@Taw'ww_c]D @ @h'a]wb] `\@Ot_L Hb]wϠ '_L H_GL@ ;H_GL@ #Hb]wGߠ '_GL@OG߀H_Gߠ$ < L@ Hb]wϦb]G_c]D @ _ϐ@@@1 '_L HAH!b]@H-b ]@ H$ߒ \_GL@ؒH_GL@ "H  O OOO \OOGߠ '_GL@Ob(]_ϐ@@1G__@@o_ "h '۠@.G'G< wwww_w_w_$ w_ h&__L@ H H_Hw_H w__L@ =O ]O_$ w_ h__L@ O_ h __@L? \H_wO__πh  _$`_Ǡ wH_ w_w_װ0ww__@@ 'G< PA@S`dwwww'?'w_@i .wcX]\ h cX]\ @lwHa]@jOw_h b0]_@@_Ւ 'H__$@c]$`Gc]$` _@gr @l c]t  'נ '_L Hm_GL@/ .H 'G׀H Gc]D H2HGc]D  Ha] @  Hb8]_@@_}c]\ @gߐ 'Hiy /H Hb@]_@@_ac]\ @gÐ 'HMc]\ G,@G۠ '_GL@O c]\ G,@$ @lw__@@I 'GӀ HbH]@f_B @gP@@_#c]\ @g?'H_@h 'Gπ HBc]\ @gr@fB HbP]@f_B @g$@@^ 'H_h 'H_c]t`G_@H G__@HHH '_瀤hc]t H:_L !H  c]$`_ wH c]$` @@jc]t c]\ _@@b HbX]_@@^c]\ @f 'Hc]t ( c]%($@ c]%,$@a]_@`H&b`]_@@^hc]\ @fʐc]\ hc]\ @Ðc]\ @f 'HAc]\ @fc]\ hc]\ @c]\ @fc]! ,D@ H_hc]! ,D@ H G〤 H 'H GϠ/@@g_Wؐw_Gϐ@  @kw'G@fnc]! ,D@ H  c]%,$@ ' wc]! ,D@HG ,0c] (\@ h_w_G ,0c] (t@H&c]! (D@ H G ,0c] (\@wH_'_G ,0c] (\@$ @-w_ h _'_$ L@ H '__h$G_'_@_$@@l__$@hHGHGbh]@ hHwG ,0c] (\@wG  ' G c]! ,D@O_h!c]! (D@ H G_@iHHKG__@HH?__hG_'_@_$@@k__$@hH(_'_@@f_@H H bp]_@@] 'Hw '_h_@H_@ejGH '_ 'OGߡ<  A@Ocwww ' ' '_GL@ [H H' '_L H_GL@ ]HG߀HK_GL@,084084a] @ H> '_GL@'G 'G'G׀ .HHG '_GL@,084084a] @ HG '_GL@,084084a] @  O_GL@ "HHaHP_GL@ \H G߀HG 'H/_GL@ "H)G߀H%G '_GL@,084084a] @ HG '_GL@,084084a] @  OH#_GL@'G 'G'_GL@G׀HHG '_GL@H_GL@ ]OX_GL@ ]H8_GL@ H2G '_GL@H&_GL@,084084a] @ HG '_GL@H_GL@,084084a] @  OGH' 'G< A@Ncwww ' '$ @iwcX]\ h cX]\ @gАwHa]@fw__א@@E 'Gˀ H bx]_א@@['H_b]@@cw_`0]t`_hH`0]\ @$@dhw 'ǠL H&GL@,084084a] @ HGǠ 'ǢGL@HGL@,084084a] @  OGL@ [HDGǢ@_@U'G H6G '_h 'HI_@bܐc]$`G_@?H _Ϡ 'HS_GGǠ@< w_@b h_$ w _,` 'G〤HH_@b'G_GÐ@@i{GÀh _Ϡ 'H"`0]\ @$@coe`0]\ @c_@Eɐ H bp]_א@@Zɒ'_@c-G'G< 0A@Mc$wwb]_@@Z'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@b(w___ߐ@@b(__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@a_w_߰@w_w_ ww_@aw_@aw__׀h__ߢ@_ע$@_@@aH H'Ӡ 'G'G< 0A@Lcw''G,084084a] @ HG ?G@'G'G<  w'w@a" _G@@aw@awB H_L H __h_ס<`4h?H'__$` 'G<  w'w@aJ" _G@@aRw@a?B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@L`twww_\ h_@&__\  hb]b]@@`Ӕ IH H' '__t` _\ _,@0w'_ @&_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@`Y@@@)^@ww__\ @@&__\`\`@)E@w_$ _$ _t 0ww_ (! @'__@ @@`#_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@_Ԑ/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@JcԮww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@J`ww_b@]@ @^`?'G'G< 0ww__@ @^'G< @ww__@ @^@w_  @^@w_L /HH H' 'G'G< 0www___@@Fw_߰@A@Ibw/`H]L@'4 0www___@ @'G< 0www___@ @ǘ'G< 0'wG_@]@@@d”w_0A@I`www'_D _@@d9'G瀤 H_ h\G@t_\`p\`h@s_G@wG4 $@'뀤HOHG瀤H b]_\ x@@USb]_\ x@@UmO@w_ 'H_``@@^c_`@_$  A@Hc,www'_Ԑ_``@^H_h__ @렌 H%_`@h 렌 H_D`\`x@kʒ_D @]H b]_\ x@@U 'H_D '_@]ِG'G<  ww'뀤Hg_ ' !@'߀HH'''_D H _ _ې@@^˔_$`#h_wH_@ _ې@@\_ w۠$@'۠@'_۠@w۠$@'߀H_ _@@]__@Ԕ '_$`뀤O 'G< 0'wG_@@ w_ 'ww @awG_$`_$ _t h__t`p__t`x_$ _נ @]J_w_߰@w@^8 _$` _$`@w_$ _ '4 @'!?< @4 !? @4 !?# @,  `, '4  ww_@\w___ߐ@@\__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@[Ӕ_w_߰@w_w_ ww_@[w_@[w__׀h__ߢ@_ע$@_@@[H H'Ӡ 'G'G< 0A@Fcdw''G,084084a] @ HG ?G@'G'G<  w'w@[l" _G@@[lw@[aB H_L H __h_ס<`4h?H'__$` 'G<  w'w@[4" _G@@[<w@[)B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@F`www_\ h_@ ђ__\  hb]b]@@Z IH H' '__t` _\ _,@0w'_ @ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@ZC@@@#H@ww__\ @@ i__\`\`@#/@w_$ _$ _t 0ww_ (! @'__@ @@Z _ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@Y/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@Dc|ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@D`dww_b@]@ @X`?'G'G< 0ww__@ @X'G< @ww__@ @X@w_  @X@w_L /HH H' 'G'G< 0www___@@0w_߰@A@Cb4w/`H]L@'4 0www___@ @ 'G< 0www___@ @ 'G< 0'wG_@Wِ@@@^w_@'!?< @4 !? @4 !?# @,  `, '4  ww_@Ww___ߐ@@W__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@Wc_w_߰@w_w_ ww_@W2w_@W.w__׀h__ߢ@_ע$@_@@W6H H'Ӡ 'G'G< 0A@Baw''G,084084a] @ HG ?G@'G'G<  w'w@V" _G@@Vw@VB H_L H __h_ס<`4h?H'__$` 'G<  w'w@V" _G@@V̔w@VB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@Ab\www_\ h_@a__\  hb]b]@@VM IH H' '__t` _\ _,@0w'_ @2_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@UӐ@@@ؔ@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@U_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@UN/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@@aww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@?bww_b@]@ @TP`?'G'G< 0ww__@ @T6'G< @ww__@ @T@w_  @T8@w_L /HH H' 'G'G< 0www___@@w_߰@A@?`tw/`H]L@'4 0www___@ @9'G< 0www___@ @A'G< 0'wG_@Si@@@Z<w_ A@>bww_D $ F'_D '_D 'G뀤 HG뀤 H?HgwG瀤 HG瀤 H?H\wG瀤 H G <` @ ?$@HG$ '_D  H _D  H_D H?H9wG,` $,`$,`$,`$`<` @< G,0b]D@G@,`,`,`,` < ,p,p$_E ,`,` ,` ,` < E ,`,`$< E @w_ w'G HG H'G'G'G<d.# ,`,`$<d.! L d$@'G HG H'G'G'_G,`,`$< w_0w'_G@wߠ@Tbw_w_w@@TU w__瀤h ?'__ߠ$@'H '__$@'G<  <.! 'G<``<.! L <$@<``<.# ,`,`,`'GGL@'G< A@=aww'www_\ _hb]Hw_\ _$@w_ǀ Zh__b]_ǐ@@R_Hw_Ǡ l  'G,0b]\@@H'_G,0b]\@GH G_$` _$`_HwG 'G O_c]@Ԓ H _$` _$`_Hwc]w_\ h._\ @Gې'__\ @G$ H___D _D L@< _$  _$`_Hw_Ǡ w_\ o '_G,0bX]\@'G H1_D $ '_$ _D G$@'G HG 'GG,$@'__G,`,`,` ,`,`<  _$`_H[wG 'G O_c]@E H)__@_D _D $@'_$ G HG '_ D`$ $`G OG_$` _$`_H#w_c]@ H__@__ D_D $@$_$  _$`_Hw_w_ϰA@2aĮwwww_א@@G) wH%_L ,084084a] @ H__M`__@@'GˀH_Gˠ@H*wH_L :H$ - H, < #a_DO_L 0H__$@ h__$`_w_߰DDD0ww_D 'G뀤HI_$ _D H G뀤 HG_$` H9_D  H G뀤 HG$ _$`H*_D  H%G뀤'H G뀤(4HG$'l_$`HG뀤 EH G뀤 dHG_$`H G뀤 &HG d_$`A@1bwwwww '۠ '_\ w@@H) $`@O$?'Ǡ?'à?'_L /HH6_ wa] @ Hې@m_$ ې@w 'Oa] @ H_$ ۠א@NwOې@DG׀H _$`@w_߰ ww '_ߠې@` H_H w__א@mw_ww '_hw_Ϡː@6 H_$ _Hwא@G__@=w_@'!?< @4 !? @4 !?# @,  `, '4  ww_@EXw___ߐ@@EX__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@E_w_߰@w_w_ ww_@Dw_@Dw__׀h__ߢ@_ע$@_@@DH H'Ӡ 'G'G< 0A@0`|w''G,084084a] @ HG ?G@'G'G<  w'w@D" _G@@Dw@DB H_L H __h_ס<`4h?H'__$` 'G<  w'w@Dz" _G@@Dw@DoB H_L H __h_ס<`< h?H'__$` 'G< wwwwww__@@Vw_h_____ϐ@@@w_@_Hww_ǰww_\ wwϠ '_ w_ߠ w__ע Gˡ,PwGˠ '_נ h__ o__t`_w_㾰A@/`wgww_w_h_hHw_$ 40'ס4 'ߠ ' < ,@߀H〤 H ' < ,@߀H〤  O < ,@'ߠ$ 'ߧ,0ץ,p,pw_@Ew_hHZw_w_ߡ,0@w_w_ߡ,0@ @Cߐ @Ew_ǀh_@DH;w?'_ס, 4@$ w__ hZ '{ '〤 H{- _ @5 4,0b]@'{ '〤 O{ӀH _ _tנ$ 'H&{'{۠ @'_ _t`{_$`_,0\@_t`__,0t@_ w_,0@wo_o` $`_$ w__o '߀HF_,0@ @HH5_,0@$ @$'_,0\@w '{G{4 _,0@$ @'{G{ H_wo_\ wG{$ @'{G{ O_\ _ot`_\ w_o '߀ O_@C[ߠ 4,0ס,0@ (w_@Dw_h_@CGHqw_w__t`__t`__t`_$`_ w_w_ߠ 4,0@w_w '߀H(__,0t@_,0\@w_h__\t`\t`_ w_\ w_o '߀ O__ߧ,0t@__$@<0?`8p׀hc]c]@@Bb!H H'{ '{_@Bؐ_w_@w_@Bǐ@w_h_\ HwHw_㾐A@-aDw?wwwww_h_瀤hHwϠ '!'_׀h _׀ h_נ #'@Cw_hHw_\ 'ˀ Hˠ < _,@Ǡ 'ˡ4 'ˀ O˧< _,@Ǡ '_'ˀ Hˠ < _,@Ǡ 'ˡ4 'ˀ O˧< _,@Ǡ '_\ w__\ @w_w__@wǠ 'Ǡ ' 'ˀ H.__h)_ _,@Ǡ ',_ @44,0b]@'ˠ '_w_ wˀ H__ o' ' '__h #H_ ?,0b]@',_ @44,0b]@'_  @'_ˡ,0@\ w__ˠ 4,0@\ h_\ ww_wo__w$@'k_ HHbk__o$@h__o$@'kkHH^k'gk$ 'kg'__H,_o 'c_o woGc'[G[_w H_w wwk'Wk$ 'kW'OOH_o 'S_o woGS'KGK_w O_w_\ $@h_w_\ $@'_\ _$@'# HH_ w__ˠ 4,0@\  o H)GHǠ '_ _,@_ wǠ 'G 'G H G_ǡ4`$$ 4, ' 'HGHEH7_$ 4 @_ ?H- '$ '_$ wǠ$ 'G$ 'GHH Ǡ$ 'G$ 'HH _$ 4 @_ ?OG_ǡ4`$$ 4, '#@H H's!@$@'ss'$@'_Ǡ@wwǠ 'Ǡ ' H < _,@Ǡ 'ˠ '!?#@H4 < _,@Ǡ 'ˠ '!?@H4 < _,@Ǡ 'ˠ '!?<@H4 < _,@Ǡ 'ˠ ' H < _,@Ǡ 'ˠ '!?#@H4 < _,@Ǡ 'ˠ 'ˡ< _w,`_@w@''#H '?'o- _Go @5 4,0b]@'Go 'oGo Oâ$ " H/_wá,`@4 '_׀h _׀ h_נ #'_׀h _׀hH_Ð@@Abw_h_@?H&w__ oGH G_ǡ4`$$ 4,_׀h _׀h_@?ːHw_t`_w_ϰ@'!?< @4 !? @4 !?# @,  `, '4  ww_@>ܐw___ߐ@@>ܔ__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@>_w_߰@w_w_ ww_@>lw_@>hw__׀h__ߢ@_ע$@_@@>pH H'Ӡ 'G'G< 0A@)bw''G,084084a] @ HG ?G@'G'G<  w'w@>6" _G@@>6w@>+B H_L H __h_ס<`4h?H'__$` 'G<  w'w@=" _G@@>w@=B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@(cDwww_\ h_@__\  h`]`]@@= IH H' '__t` _\ _,@0w'_ @l_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@= @@@@ww__\ @@3__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@<_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@</w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@'bww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@&cww_b@]@ @;`?'G'G< 0ww__@ @;p'G< @ww__@ @;R@w_  @;r@w_L /HH H' 'G'G< 0www___@@w_߰@A@&a\w/`H]L@'4 0www___@ @s'G< 0www___@ @{'G< 0'wG_@:@@@Avw_@A@%c̮w`]@bH]t bH]\ `H]`P]\ @@;`P]\ @:3 @>ΐ`]t `]\ `P]`P]\ @@:`0]@;`]t `]\ h`P]\ @: @>`]t `]\ `X]`P]\ @@:ɔ`(]@;a0]t a0]\ h ``]@<a0]t `8]@;{h c ]$`@A@%`wa(]D H @h H' 'G'G< @A@$cw`P]\ h H' 'G'G< @A@$c|w`P]\ h`P]\ w_@A@$cwwa(]D H`h]@1 a]$`aH]\ @9_@ǐ@=aH]t  a(]$`@A@$b4wa]D Hi` ]@:aH]t aH]\ h5a(]D H.b]\ aH]t`aH]\ haH]\ ːH`p]aH]\ @@;@l@=LaH]t HaH]\ haH]\ @W@=7aH]t  a]$`aH]\ h a(]$`aH]\ w_@A@$`$wbH]\ h bH]\ w_@A@#cw`]\ h`]\ w_@A@#c4wa0]\ ha0]\ w_0A@#bww`]_@@:o H `x]_@@0H' 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@7w___ߐ@@7__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@7_w_߰@w_w_ ww_@7w_@7w__׀h__ߢ@_ע$@_@@7H H'Ӡ 'G'G< 0A@"cw''G,084084a] @ HG ?G@'G'G<  w'w@7X" _G@@7Xw@7MB H_L H __h_ס<`4h?H'__$` 'G<  w'w@7 " _G@@7(w@7B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@!c̮www_\ h_@__\  h`]`]@@6 IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@6/@@@4@ww__\ @@U__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@5_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@5/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@ c,ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@ `ww_b@]@ @4`?'G'G< 0ww__@ @4'G< @ww__@ @4t@w_  @4@w_L /HH H' 'G'G< 0www___@@w_߰@A@aw/`H]L@'4 0www___@ @畘'G< 0www___@ @睘'G< 0'wG_@3Ő@@@:w_ A@`Tww`]ׂ `\@Ot_WH_Hw`]`]\ _@@ݖא@{w_w_0A@ctww_h_L HH3w_@3gw__瀤h_L /H_$ w__瀤h_L /O__ h__$@@8ea]t _ Hw_w_@A@b<ww_bx]t``]_@@5J 0A@a̮wbx]\ hbx]\ Hw`]@4w_h _L H_Hw`]6w_@ww_h _L H_H __@H __@ @__ : A@`<w`]@4Kw`]߂ `\@Otߠ@a]\ @_h _@NH `]@E`]_@@4 @H A@cww '_\ h G '_G,0\@oG < ,0@6w`]_t` '_\ h_G,p\_ߠ`< ,0uG '_G,0\@o_G < ,0t@_w_0A@aܮww_w_`]@@ `]_@@45`]@2B @2m@@{_@2?'G< wwwwwA@`www_wߠ 'G瀤 H_ (p  @w_ǢG,0t@_w_Ϡ wG '_׀hHG瀤 OG瀤 H `]_@@)H 'G,0t@'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@1fw___ߐ@@1f__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@1'_w_߰@w_w_ ww_@0w_@0w__׀h__ߢ@_ע$@_@@0H H'Ӡ 'G'G< 0A@`w''G,084084a] @ HG ?G@'G'G<  w'w@0" _G@@0w@0B H_L H __h_ס<`4h?H'__$` 'G<  w'w@0" _G@@0w@0}B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@alwww_\ h_@%__\  h`]`]@@0 IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@/@@@@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@/a_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@//w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`̮ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@aww_b@]@ @.`?'G'G< 0ww__@ @-'G< @ww__@ @-@w_  @-@w_L /HH H' 'G'G< 0www___@@ńw_߰@A@cw/`H]L@'4 0www___@ @'G< 0www___@ @'G< 0'wG_@--@@@4w_0ww '_L * _L @'㠌?H?H'< _,`_ w_ wG 'G瀤 O 'G< A@awwa]D  'Ga]$`G < ,p,p,p`]@w_wנ '_ '_ wϡ4 4aX]L@_,`_נ wϠ 4aX]L@_,`_נ wGӠ 'GӀ O _,`_w_@'!?< @4 !? @4 !?# @,  `, '4  ww_@,hw___ߐ@@,h__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@,)_w_߰@w_w_ ww_@+w_@+w__׀h__ߢ@_ע$@_@@+H H'Ӡ 'G'G< 0A@`w''G,084084a] @ HG ?G@'G'G<  w'w@+" _G@@+”w@+B H_L H __h_ס<`4h?H'__$` 'G<  w'w@+" _G@@+w@+B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@atwww_\ h_@'__\  h`]a]@@+ IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@*@@@@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@*c_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@*/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`Ԯww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@aww_b@]@ @)`?'G'G< 0ww__@ @('G< @ww__@ @(@w_  @(@w_L /HH H' 'G'G< 0www___@@w_߰@A@cw/`H]L@'4 0www___@ @'G< 0www___@ @'G< 0'wG_@(/@@@/w_A@awwww_\ @(ww_w_\ (w__瀤hJ_L 'GÀ &HG_,`_נ wGÀ HGÀ ,HH3_Ϡ wH&__Ǡ@_瀤h_\ L ,08408<   _, _נ w__\  _Ǡ$ @@'ڔ__Ǡ$ @w_ߠ w__瀤 o__瀤h __,@Ha]@a A@`ww_\ @'w_!ha]@Ca]_\ _@@' @_a],@_ w_a]@ #@)_a]@@( .hD_a]@@)wa]@'Zw ._a],@_ w_瀤h_\ @( .w_߀h_a]@_ߤ  #@(ՔH_a]@a] #@(Ŕ0A@awwa@]L H@)\@)bw_ha ]@_a@]@#a]L H:a(]@(w_h_L Ha]_@#@(xcH]D  cH]$`H_h@)@)w_ha ]@o_bh]L Hbh]ܵ 2 ww'w_@&wG_Ϡ@_h_H'_G@__ϐ@@&G_Ϡ@'Gۡ< @/ H& .H" ,H :H ;H <H >H "H  \H 'H H' 'G'G< ww'w_L /ƀ HHH _ߠ w_L /ƀ O_@&w_ǀ h__Ǡ$ L@/HH _Ǡ$ w_ǀ ow_ǀh2_L /_ߠ wHH ơ< <H OHO >OG_ h_H'_G,@G '_Ϡ w__ǀ oG'Gۡ< СA@`|wwww'G 'G 'G '_ha@]w_ha]w_L HPGHGH_a@]hb]\ hb]\ cp]!@@%˒b]t GH a0]__@@@'@'w_ha ]@a@]_\ @#@&a@]wbh]@@%ߒGH _瀤h_@ڋ 2b]_@# 'b]Ga8]@#{'b]G_@#ؖ'GHb]Ga@]@#_'b]G@#'Hb]GaH]@#E'G# haP]@ Gb],@b]w_װ0ww__@ w_0A@`Dw'aX]@&La`]@&Fah]@&@G@Ֆw_0A@cw'ap]@&"h cH]D  cH]$`ax]@&h cH]D  cH]$`ap]@&ax]@%a]@%G@w_@A@blwcH]D 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@#w___ߐ@@#__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@#_w_߰@w_w_ ww_@#w_@#w__׀h__ߢ@_ע$@_@@#H H'Ӡ 'G'G< 0A@c w''G,084084a] @ HG ?G@'G'G<  w'w@#V" _G@@#Vw@#KB H_L H __h_ס<`4h?H'__$` 'G<  w'w@#" _G@@#&w@#B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@ cĮwww_\ h_@軒__\  ha]a]@@" IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@"-@@@2@ww__\ @@S__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@!_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@!/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@ c$ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@ ` ww_b@]@ @ `?'G'G< 0ww__@ @ 'G< @ww__@ @ r@w_  @ @w_L /HH H' 'G'G< 0www___@@w_߰@A@ aܮw/`H]L@'4 0www___@ @ӓ'G< 0www___@ @ӛ'G< 0'wG_@Ð@@@&w_@A@ `Lw@ '`]\ h9`]\ D GH `]\ L H`]\ D  H `]\ D @ B`]\  @(`]\ \ `]t``]\ o@'G@ѐG@!0w_@  w__h_H5w_$ w__h_L /H_$ w__h_L /O__h_L? /H_$ w__h_L? /O_w_A@ awww 'G'G$ 'G'GۀHl_ې@$@!P'G׀ H_H_wG׀$h G,@H a]_@@E_HKwHG_h _ې@@\H a]_@@)_H/wH _]w__$@Gנ@_h _Ϡې@@<H a]_@@ _HwG'G$ 'G'GӀO_w_0A@ cdwww'_@l$ h?Hj'_ _@@G H_ #S_ a]@@ _ %@ !_$ _D  H>`]\ h a]@a]@@_$ _L H`]\ _t`_`]t` _,`_ @% H a]_ @@XH 'H _,`_D 'G< A@ aLww'a]ς `\@OtG Ha]_@q G@J@@Ha]_@q G@7@@ ϐ@㧒w_0A@ `,ww'_G@wa]_@@_@J?'G< 0A@cww'a]_G@@$0ww'__G@'G〤 H G렌 H@r_B @̒G'G<  A@bwww'__G@'G〤 HG렌 H@E_B @GH@'_@ 'G߀ H!@3B HG렌 H a]_@@G@a]_@@JH'HGG@@oHG렌 H@ؐ G@?H'G'G< @w_D '?_$`G@'G< 0w_D  H _H?H"'_ @@F@" w _,@_ @@H?H' _,` 'G< 0w'G@ '_G@ 'G< @A@cww_c]t`0A@bĮwwc]\ h%_D  H _hH?H'_ c]\ @@H?H ' _,` H'_bH'G< @w_L H_D  H_D @_ @%> _,`@'!?< @4 !? @4 !?# @,  `, '4  ww_@Ȑw___ߐ@@Ȕ__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@Xw_@Tw__׀h__ߢ@_ע$@_@@\H H'Ӡ 'G'G< 0A@b<w''G,084084a] @ HG ?G@'G'G<  w'w@"" _G@@"w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@bwww_\ h_@__\  ha]a]@@s IH H' '__t` _\ _,@0w'_ @X_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@@@@@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@t/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@bTww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@c<ww_b@]@ @v`?'G'G< 0ww__@ @\'G< @ww__@ @>@w_  @^@w_L /HH H' 'G'G< 0www___@@w_߰@A@a w/`H]L@'4 0www___@ @_'G< 0www___@ @g'G< 0'wG_@@@@bw_@wwwwww0A@c<www_wa]__\`\`@ܖ_\ @ _\ @0A@bwww_wb]__ @b]_\`\`_\ @ސ_\ @ڐ_ %`,$`_ b]@@A@awwwwww_׀h_w_L H_L ,08408<  _, _ w_L O_瀤h_w_L H_L ,08408<  _, _ w_L O_׀h_ww__א@@贔 'Gˀ HGˠ $ 'H_G_א@@fw (@@_t _\   (@_\  %`,$`_\ Gˡ,0@\ w_߀h'b ]_Gː@_\ @0_\ @,_h_@א_t _瀤h_@ΐ_t H) @wb(]_Gː@ה_h_@_t _瀤h_@_t __Ϣ _ߐ@@r b0]____@@A@bwwww'w_t __t`_@P w_ǀhHwG〤H _Ϡ _ǀhHsw_w_L ,084084a] @ H__πh_ w_L ,084084a] @ H__π o_Ϡ$ w_L ,084084a] @ H__h_$ w_L ,084084a] @ H__o__h_Hww__t`_Ϡ _t` _,` _,`_Ǡ w_L _Ǡ@?,@w_w_װA@c\wwww_hH w_b8]@@Вw_w_׀h H'_א@$@hwwwwO〤 #Hb '@@'_瀤hHzGHG$ < L@ H G$ ' G,@b@]G@@%H5_瀤h_\ @ʐG@_t G@w_L ,084084a] @ H_ w_L ,084084a] @  O_\ _@@H&Ǡ@ w_h _@ _h _____@@_א@$@om_@E 'G< 0A@`$www_ %`,$`_bH]_@_b]\ _@  4H 'G< @A@cLwwbP]_ @_ %`,$`_b]@@,bX]ӐA@bwww'w'_@w >w_πh_G@ @w_πh H'__$@ $hwH __$@ @w '__$@h_GL@,08408< Ւ _G,@G 'G__$@o _G,@b`]__@k__@@w_ǀh_\ w_ H_ _ߐ@@w_h_w_h_@x_ǀhV_\ w_\ h_\ hG뀤 H bh]* HD'G뀤H_\ h __\ G@@2GۀH_\ h __\ Gې@@!_\ h_\ Hwbx]wbp]__@ H 'b] 'Gס< @'!?< @4 !? @4 !?# @,  `, '4  ww_@0w___ߐ@@0__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@ĔH H'Ӡ 'G'G< 0A@cܮw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@R" _G@@Zw@GB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@`www_\ h_@__\  hb]b]@@۔ IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@a@@@f@ww__\ @@ׇ__\`\`@M@w_$ _$ _t 0ww_ (! @'__@ @@+_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@ܐ/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@cww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@`ܮww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@Nw_߰@A@bw/`H]L@'4 0www___@ @ǘ'G< 0www___@ @Ϙ'G< 0'wG_@@@@ʔw_@w_\ 0h _\ 0\ h H' 'G'G< @A@`w`]D '4 0A@`Dw'ax]\ ,0\@w_0A@cܮw'  hH w,0bx]\@w_0A@cdww '_G,0bx]\@@HGH'G 'G  ob]_@@G< 0w'_ @ @Kt@L$@'4 0ww'_@ْ'_,0\@h '뀤 H '_,0\@o__,0t@0A@a\ww_@ @ `]D 4 t@L$@'4  A@`wwax]\ hH9w_ǐ'ax]\ ,0\@w_߀h'__ߠ @HH '`]D 4 H 'ax]\ ,0\@w_߀o_w_ A@ctw`]D H H'`]D , 'G'G @w '`]D H#ax]\ G,0\@w_߀hH __G4 @G 'G`]D Oax]\ @ _ax]t`G`]$` A@aww'w_w_!<,@ $_!,@ $_G4 , !,@$_ע %,@ % _ע _@M`]D $ a(]D , Hb_ax]\ `]D 4 @a(]D  a(]$`_w_߰0w_w_瀤h @j_@ w_w_A@cww'wwwנ 'wG H_@~w_h___@@~GHHw_wHvG H#_@3w_h_w_ 4  H___@@lHHpwϠ 'HQG H$_Jw_h___@HHTw_\ @h__t`@ '_wH+G H_@Pw_h___@@HH.w_wHb]_G@@sw_ǀh_ 4  < H _G4 , !,@$G_$`_w_ϰA@`ܮww_א@@w_πh7___G4 &@@ H_@ fb]_Đ@@Hw_G__Ϡߐ@@wG߀H_@ G_Hww_0ww @w__t`_\ _t`__t`_w_0ww_h_\ _h H '_\ w_o 'G< 0www___@ @  www'_ '_ '_\ wۀ H۠ , '_ۥ,p,p@Qw_$`__t`__ߥ,p,pu__ߥ,p,pt`_ߥ,p,p$`ߠ '_$`0w_\ wߠ ' _ HP ''_ H8_,p,p\ ,p,p\ @ cHH뀤H_,p,p,p,p `]Ot ' '_  O_$` ' _  O@'!?< @4 !? @4 !?# @,  `, '4  ww_@ Rw___ߐ@@ R__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@ _w_߰@w_w_ ww_@ w_@ ސw__׀h__ߢ@_ע$@_@@ H H'Ӡ 'G'G< 0A@`dw''G,084084a] @ HG ?G@'G'G<  w'w@ " _G@@ w@ B H_L H __h_ס<`4h?H'__$` 'G<  w'w@ t" _G@@ |w@ iB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@awww_\ h_@__\  hb]b]@@ IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@@@@ш@ww__\ @@Ω__\`\`@o@w_$ _$ _t 0ww_ (! @'__@ @@M_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`|ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@adww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@pw_߰@A@c4w/`H]L@'4 0www___@ @'G< 0www___@ @'G< 0'wG_@@@@ w_ ww_\ w_\ w__א@$'G< PA@a<www'wwG뀤H4_w_w_G,0@w 'G뀠H_G,0\@\ _h_G,0\@\ wG 'GG뀤O_Gb]@ @Hwww_hb]b]@$@&'b]@ wH _@_%@S!'G H b]_@@UG_@w_<0h H'?`] '??'CC  H !"?c'7C';_Ϡ7@I  '_w7_7_h_7\ w/_/ GHH _7 w7_7_ o_7_Ǡ$@<0?`8pG,0$@_7wG 'G!O_Ϡ@$G@_w 'G뀠H*_\ w7_ wC H _7\ '3_Ϡ3@ __7@ G_7@ @G 'GG뀤OC  H ';_w 'G뀠H+_\ w/_ w_/ '+_ϐÔ G 'GG뀤O_w 'G뀠H4_\ w/_ w_/\ c] h_/\ Hw;!<@4w; ';_w_w_'++'+_Ϡ+@ G 'GG뀤O_w;H(_\ w/_ w_/\ w'_'c] h_'40 '_'?4 @'#_Ϡ@\ ;$ ';;O__ߐ@O _ϐ _ߠG@@G_w_װA@bw'ww'ww!'Ǡg@@G@Q h b]_@@G@ @  h b]_@@G@1 h b]_@@ݒg@ @'@ @G@ @_Ϡ$ wG@wGǠ$ '_׀h _Gˀh_HwGw_wG__@@ w_߀hHW_߀ h b]_@@__ߐ@@G_ߡ< $@'GˀHG'_׀hOg__ߐ@@__ߠ$@w_π h#àg@@_א@ H b]_@@6g@__Ϡ<w_!<,0 @$@wO_@(_׀h_נg@@W_@@RG_@ @ G_@@A@bw'G@ .@  .h'O H#c]@ @H H /c]Ɛ@$@@Hww_ A@aw'ww 'G HG H c]G@@G, < _ @/_40w_hޠ _,`_ w_ /_40wGߠ '_o_,`G'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@[_w_߰@w_w_ ww_@*w_@&w__׀h__ߢ@_ע$@_@@.H H'Ӡ 'G'G< 0A@aw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@Ĕw@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@b<www_\ h_@Y__\  hc]c ]@@E IH H' '__t` _\ _,@0w'_ @*_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@ː@@@Д@ww__\ @@__\`\`@ɷ@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@F/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@aww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@bww_b@]@ @H`?'G'G< 0ww__@ @.'G< @ww__@ @@w_  @0@w_L /HH H' 'G'G< 0www___@@w_߰@A@`Tw/`H]L@'4 0www___@ @1'G< 0www___@ @9'G< 0'wG_@a@@@4w_0A@bĮw`h]D  'G`h]$`G < ,0 b]@w_0A@b4ww_c(]@ @4H_ w_L /H _ w_L /O_w_wwwA@ahwwwww____ϐ@@}'_ h_b]_@@v_Hw_w_װA@`wwwww<w_@w__Ǣ hH6___ǐ@@_ǀh__Ǡ$ L@ /H  /__,@_Ǡ w___$__ߐ@@: @w__ hH_OH w_b]_@@_w_װ wwwwwww____ϐ@_w_װ0wwwwwwwߢ__ߐ@$q@w_wwwwwA@bwwwϠw___ߐ@$@'ۀ$ Hb]Hw_Ԑw_wwwwwA@a8wwhww_@ݐ'ˀ#Hb]H;w__ː@@ܔˀH_ˠ$ 4L@ /H  /_,@ˠ 'ˠw_צ $4__ϐ@@B2 @'ˀ$ Hb]Hw_qw_A@cĮwwwwc0]@*w_׀hc8]w__c@]__@@@4: w__πh@ N" ?H'_@'G< A@bĮwwww'c0]@w_׀hc8]w__c@]__@@@: w__πh@B N" ?H'_G@@'Gߡ< A@aww''_@w_G < h@ " ?H'__$ G$@cH]@ @H@ " ?H'ϐ@ϒ_ף,0_@:@: @w__$ G$@wǠ '_w_b]D l@L$@a]L@_,`_b]D l@w_b]D l@L$@a]L@_,`_b]D l@w_b]D l@L$@a]L@_,`_b]D l@w_b]D l@L$@a]L@_,`_b]D l@w_b]D l@L$@a]L@_,`_b]D l@w_b]D l@L$@a]L@_,`_b]D l@w_G@%@'G HGH'@VB HH_!"a@wG 'G#O_ _,`?'G< @w'_G@ 'G< @w'_G@!'G< 㽀A@aw7w_o@@j H?H'!< @#(H _g@ @.wG_G hcP]g@ @H H}'?Hz'_@s 'OGO H?Hn'GOg@ @4wGGO@X_G h?H\'cX]g@ @HCkwg_G$ wG_Gh'_gL ,084084a] @ H_g wg_Gw?_G$ wG_Gh_gL ,084084a] @  O_G hcP]_g@ @bH H'gS@%H H'?'G<  ww_ @Ðw___@@- __,@_@w_@ʐ_w_A@awwc`]ׂ `\@Ot_v /w_w_hHP_L ~H<_ w__$@w_ hch]@|w_hH4_@ɐ@@@ΔH__@w_hH_\ 0@@@@¶_wǦ_@@@@«א@Hw@rw_ A@cww'_hHwGH_@w'G〤 H_G$ < L@ /H _G$ < ,@G$ 'G〤 H _G$ < L@ /OG〤$HHw_L ~H,_Cw_׀h_@?#h_@Hwa]_א@@В_@a]_@@ƒa]wHG〤#HHwa]_@@wa]_@@_@' 'cp]\ h/_G@Gߣ,0cp]\@@_@ Ha]Gߣ,0cp]\@@OHGߠ 'Gߣ,0cp]\@oGߣ,0cp]\@h_@eHH5wa]wH _@WHH'wcx]@\  Hc]@S  Hc] H c]G@ng_Hww_㾠A@cwWw'`]D HGH _G!<,@4 @H' H'GH_o@@ H?Hw''G'cH 'c`]D H `]D 'gH`]D 'gGg'kG HGk ?m'kG @HGk a$< @'k`]D H G >Gk@'HGGk@'G!< @#H GGg !$< @'G$'`]D HGc %H'_GcG @'_G_GH_G!<,@4 @ H?H' 'G<  A@`̮www ' '_h_L H_Hw_GL@HI_GL@ /H+_GL@ /H_H~w_GL@ /H G '_GL@ /O_GL@ /H Gߠ '_GL@ /OH_GL@_GL@H_HRwG 'Gߠ '_GL@O_GL@H_G$ < L@ /H _GL@ /H_H/w_GL@ /H Gߠ '_GL@ /O_GL@H c]c]@@ؒH c]_Gߠ@@͒c]w_wwH_L _,`_ w_ w_L _,`_ w_ w_w_L /H /_,`_ w_L /H _ w_L /O_L /Oހ .HI_L H_ wH?_L  /H_ w_L /H _ w_L /OO_L  .H#_L H_ wHh_L  /H_ w_L /H _ w_L /OHQ_L /O'_ wG'GӀ HOހ /HO_,`_ w_L /O'_ wG'Gˀ HOހ /OOހ /H /_,`_ wOހ /H _L /_ wOހ /O_$ wHOހHH(Oe_$ w__߀h?H '__h_L? /H_$ w__h_L? /OOA _,` 'G< A@awww?'_h _c]@@H?H'_w_w_L Hw_w_L H_L :H_נ w_L H_L :O__ߠ$@'GӀ H GӀ$H_HHB_GӠ < @@` HH0@'GӀ HGӠ$ < L@ /H GӠ$ 'Ӧ G,@_GӐ@@H_GL@ /H GGπHG'_נ w_L OG'G< @w'GH_G$ < L@ /HG$ 'GH _G$ < L@ /OG'G< 0ww_@B'_@>'G߀H=G〤HHOw_G$ < L@ /H_Gߠ$ < L@ /HH=w_G@'_Gߐ@'HG$ 'Gߠ$ '_GL@_GL@HHwG߀OG〤H _G$ < L@ /HH w_G@y: @@ w_0w_h _L /H _L ~H?HT' ' '_ w_L /_ wGH2Oꀤ .HG 'H'Oꀤ /H G뀤 H?H/' 'HOꀤ HG뀤 H G뀤 H?H' H' ' 'HOꀤ H H 'Oꀤ /H ' 'OG< @wH _L  / "H  H '_L / "H 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@&w___ߐ@@&__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@ނcw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@uB H_L H __h_ס<`4h?H'__$` 'G<  w'w@H" _G@@Pw@=B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@ނ`lwww_\ h_@__\  hc]c]@@є IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@W@@@\@ww__\ @@}__\`\`@C@w_$ _$ _t 0ww_ (! @'__@ @@!_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@Ґ/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@܂c̮ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@܂`ww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@Dw_߰@A@ۂbw/`H]L@'4 0www___@ @'G< 0www___@ @Ř'G< 0'wG_@@@@w_A@ۂ`w'ww_w_瀤h*G__@@2'GӀ H _GӠ@w_GӠ$@wHGӀHc]@bc]@_瀤o_w_߰@A@ڂcw'Gc]@ @A@ڂcww_c]@ @z0A@ڂc$wwwaX] __@#@'瀤# hc]@ '4 4`]L@aX],`4  4`]L@aX],`4  4`]L@aX],` 4`]L@aX],`'4  wwwwA@ڂaw'ww__ߐ@'GaX]ې@ wwwwA@ڂa wwww__ߐ@m'_aX]ې@@Ӕ A@ڂ`w'w'G_@@Hw_߀ h c]@|H _뀤hc]@N0w ' '_GL@/G, 'ꀤ 0H ꀤ 9H G$ 0@'H#ꀤ aH ꀤ fH G$ W@'Hꀤ AH ꀤ FH G$ 7@'H?H 'G 'G OG'G<  A@قbw'w'Gߐ@s 'G〤 H c]ߐ@@֒G〤H H 'G$ 'G4 뀤 H c]G@@忒G_G4 @E _G,@G'G< 0www_\ h?HE'_\ R'G߀ H?H9'G߀H _\` t`_\`$ t` H*'_\ G߀h?H!'_\` t`_\`$ t`Gߠ$ '__\ Gߐ@@_\Gߠ@t_\Gߠ$@tG'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@rw___ߐ@@r__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@3_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@؂`w''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@̔w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@윔w@B H_L H __h_ס<`< h?H'__$` 'G< @A@ׂbw' b]$`A@ׂb<w b]$`ߐ  @]c]w@Ɛ 'ߠߐ @ǔ wwǦ\t`\t` @ A@ׂadwc]߂ `\@Otߐ @  @ b]$`A@ׂ`̮ww'w_ H;b]D H _ $ '_$`ˀH H'_ H!,`,`,`_ t@'_ H ?4 _$`_$  H'< _$`_\ h_\  lHw`]w_w_瀤h_Hw`]w_w_ HG,`,`,`_ t@'_ H b]D H*_$`cp]!`]_\`` __א@@s@scp]!@ b]$` H.'H*b]D H#cp]!`]_\ __א@@@Vcp]!@А b]$` H' 'G<  A@ւa,www' '_#h#G_ l` ]_ 'G〤 HG__ H@  @rH _ ,@V'G'G< A@bwww?'`] `\@Ot_ H_@@KH?H$'_ ,Ӡא@sw_߀h__h ___א@@,'_@Ȑ@G'G<  A@aww_ Ha]ߐ@@_ H H '_ ,@ݒ'G< 0wwH6__@NH HF'HB__\ (@oH H5'H1_ !< @#8H _ @?,@'GH ' @H'_ !< @<# H#OO# O#(OO 'G< 0A@c\ww_`@]@b`?'G'G<  A@bwww '_ (!@H hH'He_ !< @! $@ @@?,@'G4 @'a]D H _ _ @ @H 'HZ_ !< @#(Ha]D H _ !< @# H @'H>_ !< @#H @'H _H 'H|'a]_ @@˖H_ !< @<#(H# OO#(O#8OO_ _\ @< H 'b]D H _ _\ P< H '_ _D 4 H _ _D 4 H '_ _\ < H '_ $_\ (< H '_ $H _ ,H ''G< 0ww_ !< @#8H_ (H _ (_ H H' 'G'G<  www' ' ' 'GӀH _ (!@H H>'G׀H _ (! @H H1'_ (!@H hH('__@'ۀH__@HGπH۠ 'H __@2 ''Gߡ<  www'___@'GۀH H.'G۠ HHGH''G۠ H_ !< @#8H_ $ HGH'__@Β'G׀HGGנ@H'ߠ 'Gߡ< w''w''GG〤HGH'G'G'__GӐ@@҈'GˀHGH:'_GL@/_GL@/ڀH G!< @#H //ـH G!< @#H //ـH?H'ـH H'à 'G'G'Gۡ< w''w''GG〤HGH'G'G'__Gא@@"'GπHGHF'GG〤H H>'_GL@/΀H G!< @#H //_GL@/̀H G!< @#H //΀ /H ̀H H'̀ /H ΀H H'͠$@'Gۡ< w'w'G /'G /'GG׀HGH'G'G'__GӐ@@Ѭ'GˀHGH,'GG׀H?H$'GG׀H H'G! @'G! @'GG〤H?H 'GG〤H H'ߠ 'Gߡ< ww' '_ 'GG〤H0GG@< '_\ Gۡ,0\@w_G_Ϥ`H`(< 'GˀHGH'Gˀ HG'HG۠ 'GG〤OG $ 'G< 0w'_\ G,0\@w__@@7_ _$`_ `$ $`G4 _  H H'_] G, 4$@4,0@@` 'G< 0w_\ w ''_ H&_,0\@ (!@H _,0\@PH _,0\@,0t@ ' '_  O _$`_$`0ww__@Ё@: @('G瀤 HG $ '_\ _@@HG4 _ H(_\ G,0\@ H_@@ H_G@LG4 _ H_\ G,0\@ H_@@O 'G< 0ww'G_@h __ HG@@7H H' 'G'G<  ww'__G@'G〤 HGHO'G $ 'G4 _ H_\ G,0\@w__G@H?H1'_ (! @4 H'G 4 _ H_\ G < ,0\@w_ (! @4 H__G@HG 'G'G< 0ww_X'_TG瀢h_ H_ HG@@ϟH H' 'G'G<  A@cLwww_ (!@H a]_ H_ H@@ _'G P ? @w_ע H_ HG@@U__@ƒ_@_w_ A@bDwwa]ax]\ @@jHa ]@Ȑ_ ,@СA@awwww'_ 'Ϡ 'G 'G 'G 'G @?,@'G '!< @# H!< @#(H!< @#H a(]_@@HM'_@Đ'!< @#HG׀H_Gנ$ < L@ /HGנ$ 'G׀H _Gנ$ < L@ /OGנ P ?'Gې @3w_Ǣ H_Gא@@ΠGס4 _$`(GH __@H_Ǥ (!@$a]D Ha]D H J_$ H__Gא@W'G H _\ G,0\@Hww_w_ϐ@__$ __Ǥ`H@: `]D @@0w_h0_ (! @4 H(___@H_@֐_ !< @#8H_ (!@$_ (!@$ H'GH_Ǣ ,__@@kޖ H a0]_@@@Ht'H_`]D H_h__@=H__@aw_Ǥ (!@$_h_ (! @4 H_ ,_Ǡ ,@H _ _ H H' 'G'GHH__G@@ H a8]_@@ߒH'GH GӀH a@]_@@V 'Gߡ< 㾰A@cDw_ww'_g@@H aH]_@@.__gG@K'G< A@bw'ww''_@8H aP]_@@zH6w_@%'GϠ P ?'GӐ @Ҷw_Ǣ ,_@%_Ǣ H_Gϐ@@GG4 @_$ (_$ G߀H_ǐ@ Hw_w_װ0ww_'_G瀢h_ H_ HG@@H H' 'G'G< ww_h Hc'_q'_ Hw_\ w_w_ w_w_hL_@̜'GGӀHH3__G@@̥HH'GH _G$ < L@ /H H('_GL@ /H_GL@H H'GH H'_\ w_w_ w_w_o 'G< @wH% H:'_L  iHH1_L  tHH*_ w_L  H_L  /H H'H_L gH .H OH O /OHO 'G< @wH OH H7'O /H+_L /_ wHOH_HOHO .H OOO /OO H '_L /_ wOG< ww'' '_h'_ (! @4 '_ HwG4 _ HU_\ G,0\@wG 'G_J@ hHC__Ǥ HGې@@˕HH6_ (! @4 Gף4 HH#_G۠@L H /HH_ (!@HH?'G瀤HHG$ '_G@CG4 _  OG'G< ww'' '_ (! @4 '_ Hw__@w_Ǡ$ w_L /HH __ HhGH'O__Ϡ$@'__GGۡ4 @: 'G뀤 H_\ G,0\@ (! @H?'G瀤HHW_G@ՒOHG $ 'G4 _ HA_\ G,0\@w_GÀh_Gà@L H /H _ H_GÐ@@ʔHH _ (! @4 Gۣ4 H _ (! @HGH'G 'G4 _  OOoG'G< 0ww''_ (!@H H'__GG@'G__GG@*'G< A@bĮwww'G 'G 'G 'G '_\ _ H@@Bf__`H`(< ה'G〤 HGӀH _G_@ה H'G $ 'G4 _ H._ (! @4 H&_\ G,0\@_@H '_G@HH_\ G,0\@_@OG߀H?HB'_ HڐH aP]_ H@@H1'G׀H*__GGې@>HGۀH aX]_ H@@H'__`H`(< N'G $ 'G 'G< 0ww'G렌 H_ 'H__G@,'G߀ HGHE'Gߠ$ '_`` H_  ,`@4 $_\`` ,0@΢_t _ ` $`_ G 4 H_] G,` 4$@$ 4,0@@Ô_G_@ؔ _$` 'G< 㾀A@bw7ww'w 'C '?_ (!@H_HwG?H_ (!@H _ (!@$_HwGCH_ (! @H _ (!@$_Hw_ HW@@o H _߀h@B _$`Hzw__W@'KGKH'GCH`]D H _ (! @HH_ !< @#8H_ (!@$_HGw__W@H _߀h _$`H2w_F P ?'GGG@wO_O_GG@@Ȉ_OW@GCH`]D H_ (! @H_O (! ,@$_Ow_װ A@bwww'wwG뀤H_D H_׀h a`]_א@@_$ __@@㾰A@bwgw'wwwנ '   2H '   2H '   2H '   2H '   2H ' ' 'Gǀ@ 'G'GHah]Hwap]w_wGHax]Hwa]w_w 'Ϡ 4 _ H 's_\ Gϡ,0\@wGH _ !< @#8HH_ (! @4 HHGϧ4 _ H(_\ Gϡ,0\@ H_ H@@>HGϠ 'Gϧ4 _ H_\ Gϡ,0\@ H_ H@@&OGϠ$ 'GÀHHu__ HG_א@@ 'He_瀤h__ H@Q@: _ߐ@ @aHHO__s@ww_w_hH>_wh3GHGs HH0GǀHGs H _ (! ,@$ _$`GHH__ HG_א@@ 'H _G_w@GϠ 'Gϧ4 _  O1G'Gӡ< 0A@aww'ap]_G4 @w_A@`woww_ #TbCH a]@H7'_  H_  H a]@H&'@x__$ @@xw@@{w__@$ }H a]@ːH' 'G<  A@c4wwwwwH5__ߐ@@CŒ_t HC__ߐ@@-ޒ_t H:_L AH_L ZH a]_@@H)'cp]!a]_@@H_L`, L`, L`, L`#UaCO#aEOO 'Gۡ< @w|_@@'G< A@aTwww_ _$`_ _$`_ _$`_ _$` _ _$`_ _$`_ _$`_ _$`_ _$` _ $_$`$_ <_$`(_ ,_ (@ɒ_ ( /4w_ (!@H _w_ >, 'G!@H a]GӐ@@P_ (Gӡ4 @$_נ @wH_ >w_瀤/h_@ņw_ H__ @@Ō0w' w_ߠ w__ߠL@w_`A@bwww@r " _ 84  H_ H'@e " _$ (_$ ,_@ 'G瀤 H@UB H H'a]@G_@@Ha]@@: " _w7_7 ha]@_7G@  @w?G@ſ_??ha]@̐_?wG_G_7@/ HH_G _$`_ ,`@4 $ _  @s_t _7_ @_@m_t 0_ 8%`,$` wWwO ' 4 _ H@_?_W@w/_\ 0_O@w'_/_'@_G_'@I_' (!@H _'\ H ?Hw_'U F ?w_W_@wW_O_'J P ?@wOG 'G4 _  O__$`(__$`,_W_7$ h+3_?_W@  @v3'3__?_W@`3@ HH_W wW_W3@wW_W_7$ o_?_7@@_ H'_?_7@@@W " a]@G< @w_ H_\ 0h _ (H H' 'G'G< @w_@,E_$ _$ _$ (_$ ,_ 8!<,@ $_ @@q_ @:ɐ_\ 0@ę_t 0_ 8!,@ $ 'G< @w ' 4 _ H_\ G,0\@ (! @4 H H'G 'G4 _  O 'G< 0A@cww'`]\ '뀤H_b`]@@fGb`]@@M뀢h?H'`]t  'G<  A@bww'w'〤HJ`]\ 'ۢ !@'〤H'b`]@_א@@Bנ@'#H`]t`_G@H?H'ߠ '`]t`נ$@'_נ@w〤O 'Gߡ< 0w'''''_G@  H _G@  H?H' 'G'G< 0A@`ww'`]\ '뀤H`]t _b`]@@Ð #HGb`]@@q뀢h?H$' 'b`]@_@@w 'Gb`]@@S뀢h?H' 'G'G< w_ Hw@@ HH_w@HH _w@tH_$ $w'w_ (!@H _ H ?Hw_ F ?w_'Gߐ @Ƿw_ _$`_ _$`_ _$`_ _$` _ _$`_ _$`_ _$`_ _$`_ _$` _ $_$`$_Ϣ (_ ,@_ (< _4`<_ (!@H_w_ (! @4 < _4`>_ @wH_Ϡ >w__`H苐@@֔_G_Gߡ4 @b'G< A@`Dww'_\ wg_ 'c 's 'w 'GcH8_gG,0\@ (!@HGw 'w_gG,0\@ (w_!,@$_gG,0\@ (!@HGs 's_gG,0\@ (w_!@$G 'GGcO!T"C'Gs@  'G'GcGw$@'@1G@ H?H' 'GcH6_gG,0\@w_ (!@HH!_ (! @H __ϐ@’H_G_ϐ@ߔ H?H'G 'GGcO_\ h<a] `\@Ot_\ @@=UG_ǡ< @E HG__ǡ< @ H H' 'G'{@G{H?H\'_\ h<a] `\@Ot_\ @@((G_ǡ< @UC HG__ǡ< @E H H' 'G'{@lG{H?H'G@’H Gߐ@@NH?H '__$`(_'_$`, 'G< A@cww '_R ' 4 _ H]_\ G,0\@w_ (! @4 HHC '_ߠ H@9'GϠ P ?'GӐ @ʒw_ע H_ߤ HGϐ@@6GϐȒ ! _$`(_ _$`__א@ H a]_ߠ H@@YH'__פ HGϐ@'G 'G4 _  OG'G<  ww'G뀤H _G$ < L@ /HG$ '__G@x'G〤 H H*'G $ 'G4 _ H_\ G,0\@w_pG뀢h_ע H_G@@H H' 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@jw___ߐ@@j__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@+_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@`Įw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@Ĕw@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@a|www_\ h_@)__\  ha]b]@@ IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@@@@@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@e_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`ܮww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@aĮww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@Sw_߰@A@cw/`H]L@'4 0www___@ @o'G< 0www___@ @o 'G< 0'wG_@1@@@w_0A@bww_b]@@Zw_@w_L .H_L  H _L  .H _L  H H' 'G'G<  A@`www_@Ӑ" *'G〤 HH:w__@ HH0w_L (,084084a] @  HH!w_ )w_L ,084084a] @ HHw_GL@ HHw _G,@_w_ww'ww_@ 'Gˠ 8@w_ _@_ _ 1_Gː@@vG_,`__t`_׀h__t`_w_ϰA@bww_w ' '_h_HwG' ' '_w_www_׀hG 'GGH_\ h_\ wG 'GGH_\ oG'G뀤 H G瀤 H_πhG瀤 H_πh _w_\ wG$ 'HaG뀤 H _w_\ wG$ 'HS_Ϣ 1_נ 1@@'G߀ H _w_\ wG$ 'H=G߀ H _w_\ wG$ 'H/_Ϣ _נ @ĒH b]_Ϡ 1@@[b]_Ϡ 1@@_w_\ wG$ '_@L_w_\ wG$ '_t _ǀh__t`_h_w_wG뀤 O|G瀤 H_πot_w_׀oCG, 'GGO+G〤 O'_w_@w_h_\ w_@_w_o0A@b\wch]w_L H _\ h_\ А_L H _\ h_\ w_t __t` _,` _,`A@atwwwwwߠ 'ۢ_@$@Bh`b ]@ @Hw_b(]@@hG۠ 'H:ߠǐ@w_h_G_ߐ@@/wH"_߀hO߀ ^H@ *hO Hǐ@H_ߢ ǐ@_@$@o_<_t 0A@ctwww'__G`]\ @`]t @A@bw`]\ `]t 0A@bwch]L H$b0]@b8]@wch]t _h _ch]@!_@ ch],`ch]\ w_pA@awwwb@]_@@w_߀h_@'GӠ!< @w__GӐ@@GӀH_GӠ$ < L@ /H  /_G,@GӠ '_@w_׀ho_L  .HH`_נ @''G' HHU_ע bH]@HHH_GӢ@_פ G' < @@b@]_ǐ@/@ HH-?!< @#H __@zwH_Ǥ+@ @mhG+ '+_ǦG+_@ؘw_@Jw_׀o_@ _@O_w_ww'w_wG H H$'_@ @&w_ǀh __\ @@ϒH H '_\`\`_@@a 'Gۡ< A@awwww_w_w_wbP]ϐ@@@A@awch]L HbX]ђch]t  ch],`ch]\ w_A@`lww'ww_G@b0]@@G_b8]@@w_πh?H3'_Ϡ@_@ _w?'_h_ 1_@@2H '__ @ @H _\ w_o_G'Gۡ< 㾐A@bw?w'ww'_@N'Gۀ HGπ$H?H'_G@_GϠ < @@G_@ 'GӀ H _G__ߐ@mH'GӠO@ @$'G@ؐGπ H?Hu'GπH(OGϠ$ < L@,084084a] @ HGϠ$ 'GπHOGϠ$ < L@,084084a] @  O OG,@O_ߐ@ĒH H<'עOb`]@ @H?H-'נSwG_GL ,084084a] @ H_G wG_GL ,084084a] @  O_G_G_G۠ @@E'Gס< A@c̮wwww_@'G߀H_Gߠ$ < L@ /HGߠ$ 'G߀H _Gߠ$ < L@ /OG߀H?HT'Gߠ$< @w__Gߐ@@f_Gߢ@bh]@ @[Gߠ '_@&ѐw_ǀh_@_@:'Gߠ$< @Ґw__Gߐ@@< /_G,@Gߠ 'ߦ _G,@_G__@ '_@ːG'G< pA@aw'ww'wߠ '_߀h_$ GӠ$ 'GӀ HHc]_@6@Hc]_@9_B @@@`GH'G#H#c@]c]_@ @oHc]@_B @d@@7 'G< @w_\ H?H'?_$`, 'G< @w_\ H?H'?_$`, 'G< @w_\ h_\ _\ @K_\ @G_@DA@cwww_wߠ 'נ _,`_ߠ w_L /O/_ wO/OՀH;G׀HOޡ,084084a] @ HHOޡ,084084a] @   2H 'G׀H /O_,`_ߠ w_L /O/_ wO/OӀO__߀h#_L?,084084a] @ H_ߠ$ w__߀h_L?,084084a] @  O _,`_ߠ w__$@'G<  A@aDwww' '_Gc]_@zc]D H<_b]@@m&H _b]@@mH_c]@@mH _b]@@^H_@# H b]_@@HZ'Gߠ!'_Gߐ@@[!'G〤 HCGߠ !H @B H H>'Gߠ!H@B H_7H c]_@@H%'_Gߐ@@)!'G〤 Hc]@w_B @̐@@H '_ɒ G@ 'G< A@bdwwwwwߠ 'c]D H mc`?'Gc]$`_@'$'GӀHGHy'ۢGː@@В'G׀ H Hl'_߀h_@Hww_'n w_@G d'@w_c]_y_y_@@@'GH_$ _ߐ@D" 4 'ǀH G_Ð@@Hw?w_'_@eG@j HGϡ4 ÀH c]@@H'۠ 'Gۡ< 0A@cww_b]@@,H _b]@@k̒H H' 'G'G< A@bwwww_h?H'_D 0H_ _@SH_ H'_aw_׀hc]_\ _yD@@s_?H'_ 4  < H_\ Hc]_y$_\ @@Q_^?H'_D ,_y@ (@ (h_D ,`P]@ @ h_ Hc]_\  @@!_.?Hy'_\``__@ H_\`\`@fH_\``__@s H_?HN'_\ b]@@H H*b]à@ w_hG H__\ @@,Hb]_ __@7_Hc]_\ @@_?H'_ 'G< A@bwwwwb@]_@w_瀤h _Ð@Hb_@  H b]_א@@kH'a]D H_@__א@@"HHcp]!c]@@Kc]_@#@'G瀤# h c]_@@5H[`]_א@w_א%@!'G뀤 H `]_א@@H@GG@@'G@} HGG瀤H `]_א@@H!__א@@ H `]_א@@H_א H` ]_א@@ݒ_@ΐ_@??H'_瀤h_@גH _è_@9_@$ 'G< 0ww_ Rw__$@ @w_߀h_w__ߠ$@@Pw_A@bdwww'www_w_w 'sc]_@/w_ @ '{G{ H `(]_@@G{@@ؒ_h `0]_@@_w_G{@  @w_wG{@w__@w_w__hGs 's__h _L? H_$ ww__h_L? H_$ w_L >H_w__h_L? O_h `8]_@@X_ @@Ŕ w__hG뀤 H_@ 'w_׀h __@(_t _πh__t`_ǀhGw_$`_hGs$ _$`_h:_Ӑ@vIH `8]_@@ _ )_ߐ@v9H `8]_@@_ߐ@VH`@]__Gw@R+ @H@__h_ )_ߐ@v H `8]_@@ϒH)_ )Ӑ@uH `8]_@@_ߐ@H`H]__Gw@Q @h__@@Œ H'_wG뀤 HG$ '__ o_w__h_L >H_L H_ w__h _L >H_L O__h_L H `8]_@@\_ @@ɔ w_@Ô 'w__ߐ@u{H `8]_@@?_zH_ )_ߐ@ueH `8]_@@)_׀h __@_t __@@=_πh__t`_ǀhGw_$`_hGs_$` 'G< `A@bwwwww`P] `\@Ot 'c]_@6w_b8]@@w_ǀh?H'_瀤h,_@/'@ܒH_O_瀤h__ @@% H _ǐ@@mb H _@~@gŐ?H'ۢ_ǐ@@mP H_ S h__$ L@ H|_@tHt_L ( Ho_ )@tHf_L Q Ha_ R@ >ww_whW_wL  HR_w o@@˔ wg_ghF_ohB_oL H=_oL  +H_oL  -H3_oL ,084084a] @ H'_oL ,084084a] @ H_oL ,084084a] @ H_oL ,084084a] @  HH2 _w,`_o @ 'c_oL  H_o woH_o wo__ R_gGc_o_ߐ@@s@'GHH _ǐ@@l OB_@@fG'Gۡ< 0www___@'G< `A@aTwwwwc]_@@wנ '_׀h_@ؐ'Gà!< @pw__GÐ@@ڔGÀH_Gà$ < L@ /H  /_G,@Gà '_@w_ǀhz_L  .HHk_Ǡ @'3G3 HH`_Ǣ bH]@ᗒHHS_Gâ@_Ǥ G3 < @@c]_@7@r HH8G!< @#H ___@u'H!_@ Yh `X]_@@'H ___@ 'GӀHH _@,w_ǀo_@_@1H _L H@HB H'G'G< 0A@awww``]__@'G< A@alwwwww''__Gې@c]t c]\ h*`h]wH__@@!H__@@~HHG׀ H , < #b_D H9'c]\ __@P H*`p]wH__@@H__@@IHHG׀ H , < #b_D H'Ӡ 'Gӡ< `|0ww_h_ P_@@H_H w_\ w_ow_0A@bwww_b]@@w_瀤h _ b]@@ h `x]_@@Ԓ___$@@ ___$@,_b@]@@{__ @@uHA@alww'`H]D Hw`H]D ,0`(]\@h%_`H]D ,0`(]\@@Ґw`H]D  `H]$``H]D ,0`(]\@o`H]D ,0_Ϡ@@Pc]t wϠ '`H]D H9c]\ `H]D ,0@_Ϩ@c]\ G,0t@c]\ G,0\@`(]\@?_G,0`(]\@@zwG 'G`H]D O`H]D H_@͐Hww_@Ȑw`H]D $ 'G〤 HbG'_c]\ G,0\@_א@@t HHG_@<'GH`H]D 'Ϡ 'GπH,Gӣ,0`(]\@wGGӀHH_G_א@$@kHH GӠ 'GGπOGGπH_HwG$ 'G〤 O_@_@Ww_@'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@}_w_߰@w_w_ ww_@Lw_@Hw__׀h__ߢ@_ע$@_@@PH H'Ӡ 'G'G< 0A@b w''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@ B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@bĮwww_\ h_@b{__\  h`]`]@@g IH H' '__t` _\ _,@0w'_ @bL_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@@@@d@ww__\ @@b__\`\`@dٔ@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@h/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@b$ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@c ww_b@]@ @j`?'G'G< 0ww__@ @P'G< @ww__@ @2@w_  @R@w_L /HH H' 'G'G< 0www___@@1ږw_߰@A@`ܮw/`H]L@'4 0www___@ @MS'G< 0www___@ @M['G< 0'wG_@@@@Vw_@w_L .H_L  H _L  .H _L  H H' 'G'G<  ww_ (! @4 'GӀHHE_\ h  @w_ע %`,$`__t`_\ w__ H@@jɒw_\ h  H@_t _\ w_GӠ$ < ,p,p _ ,@֒_ _GӠ$ < ,0$@A@aDwww_w_\ w_׀h HC'__\ @_ߐ  '_`]_Gӡ,0@@b GӠ 'GӀ O '_Gӡ,0@HH__Gӥ,p,p  @aGӠ 'GӀ O 'G< 0A@cĮwww_`]_@@jŔA@c\www  @w_ߢ %`,$`_h_@Z w__׀hH__@@iw_\ h  H@_t _\ w__נ$@w__נ@w '_ϐ@@U _G˥,0$@_πh __h_L HHM_Ϣ _$@w__׀hHA__נ$@w__נ@wGˠ 'Gˀ O '_Gˡ,0@HH_ hH!_G˥,p,p _@_$ w_ wGˠ 'Gˀ O_ov_H w_ߐ@jH `]@w_@w_\ w_hH_@j) _@`_t  _$`A@cww' '_\ hGH'_\ G,0\@w_ (! @4 H3G4 _ H(_\ G,0\@ H_ H@@HG 'G4 _ H_\ G,0\@ H_ H@@OG$ Hi'_\ _ H@@ifw_߀hGHZ'_\ w_׀hGHP'_G@# '_Gӡ,0@HH'_Gӥ,p,p _ H`@Ϙ w__ǐ@| H  '`]_ H@@KGӠ 'GӀ OGπHGH '_@_t _G@K'G< 0ww_\ hH2 ' 4 _ H)_\ G,0\@w__ߤ`H>@:  @0kHH_G@'G 'G4 _  O@'!?< @4 !? @4 !?# @,  `, '4  ww_@nw___ߐ@@n__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@/_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@`Ԯw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@Ȕw@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@awww_\ h_@[-__\  h`]`]@@ IH H' '__t` _\ _,@0w'_ @Z_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@@@@]@ww__\ @@ZŒ__\`\`@]@w_$ _$ _t 0ww_ (! @'__@ @@i_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@~aԮww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@@*w_߰@A@}cw/`H]L@'4 0www___@ @F'G< 0www___@ @F 'G< 0'wG_@5@@@w_@w_L .H_L  H _L  .H _L  H H' 'G'G< A@}atww'w_wG_@@ @w_+H __@@}HGH __G@@_G@_@@i_ϐHH:_HMXw_hH+_@w_@: w_@ w___@@H__ǀh__L@ H__L@ /H `]_א@@5__L@ /H_ w__@_$@@_w_߰ A@|c ww'w_h _L H_H_Hwa]_G@@NGa]@_@@Ւa]w_߰㾠A@|bwOww_h _@@: _@HwW_wW_Ww_ߠ_@@H H'@B H@B H H '`]_@@G<  A@|`www_ۨ_@ @Dn`]_@@y0A@|`twww_L -H `]_@@\__@HH__@0A@{cĮwww@8H@ HH_L -HH__@LHH `]_@@A@{bwww_\ w_h_߀hHKw_߀h_`]t``]t `]H9w_w_w_h_@*Hww_'_\ h_G_\ @w__t`_Ϡ w_נ w_\ o_t _\ hHw_w_0A@{aTww_@w_`]@@$ h `]_@ <@_@@ra]@uha]@m@s H H>'H_@`]@@R@^ H H)'_@a]@@?@K H H'_@a]@@,H H' 'G< @A@zc\wc]D H U@5Ic]$ c]D 'G< @A@zbĮwcH]D H V@5#cH]$ cH]D 'G< 0A@zb,ww$@Vha]@ʐ@}b]t  cH]$`w_0A@za|wb8]D HH-UwUnw_,H_@hw_h_@xHa ]@__@V} b8]$`0A@z`twwcX]"`]D H'_h a(]`]D @ @Ma(]`]D @ @a0]@?_$`?H' 'G< 㾀A@yc\w/w_W@@bHHwg!< @# HHw_@  'SGS H a8]_@@"_ @wGS__@@ؔwGGS@_G_h a@]_@@ __G,@_aH]@@SH aP]_@@ϒ__G$ L@ H __G$ L@ H_G$ wG__G$ L@ O__G$ L@ O_G h aX]_@@ __G,@_ w_-H5_@X /w_׀h,_ע _$@w?_?_G@$ w7_7 @kw___?@@m__?@_ _G$ @@b __7,@_@k_w_H a`]_ߐ@@X_@)w_@U_w_pA@xcĮwwah]@,wap]@&ww '_h_$ ax]@w_׀hv_@g#h a]ax]@@_fHQ_瀤h_&w'_HHw_'Hw_HHwa]T @$@2w'_'h_'L HHw_@ީT_@ H a]_@@a]a]@@y_'Hw_h _$`Hwa]_א@@` ]$@'ha]@` ]_ߐ@Ȓ'GÀ HH '` ]@Ґ'G'a] s `?'+G+'GHa]/@@Ha]@_/wa]6w_πh_TVHa]@NHa]HHa]H< c]$`_瀤h cH]$`GGˀH` ]'GGHGH''G'' G'` ],@` ]T Ha]T_HwGǠ$ 'GGÀHG` ]L@ /HGǠ$ 'GGÀH G` ]L@ /OGGÀH!_h` ]@Ha]@ _$`Hwa]a]@@GHEa]/@@H G` ],@a]` ]@@_/_h'_h` ]@}Ha]@ _$`Hzw G` ],@a]` ]@@ua]@\H G` ],@b]` ]@@O c]$`_瀤h cH]$`` ]+'GGHGH''G''` ]G'@@b]t _HH!wGGˀHHwGǠ 'Ǧ /G` ],@Gˠ '˦ G` ],@G` ]@w_ A@vbwww_h!H'_b]@@H Hs'_b]@@ےH!Hf'_b]@@ΒH_b ]@@ĒH _b(]@@H!HE'_ߐ@@. '_L H__@!@?,@'GH,'H  H''!H$'!H!'G瀤 H , < #bP_ϠDG !!H b0]G@@yG ! 'G<  A@v`Lwwww_b8]@@R H __@`]$ H]_b@]@@= H __@9`]$ HH_bH]@@( H__@ a(]$ a(]D H?cH]$`H'_bP]@@ H_h_'H'b]\ @_@b]t ?cH]$` 'G< @o'G<  A@uawwcH]D HG_h _@HbX]@Ӑbh]P@$@/kw_瀤h_L HP@Hb`]@_瀤h_L H _a]@@=Hww_Hw_w_@'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@U_w_߰@w_w_ ww_@$w_@ w__׀h__ߢ@_ע$@_@@(H H'Ӡ 'G'G< 0A@talw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@" _G@@w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@sb$www_\ h_@NS__\  hbp]bx]@@? IH H' '__t` _\ _,@0w'_ @N$_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@Ő@@@Pʔ@ww__\ @@M__\`\`@P@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@raww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@qblww_b@]@ @B`?'G'G< 0ww__@ @('G< @ww__@ @ @w_  @*@w_L /HH H' 'G'G< 0www___@@w_߰@A@q`<w/`H]L@'4 0www___@ @9+'G< 0www___@ @93'G< 0'wG_@[@@@.w_wwwwww__@Axw_h_____ϐ@@C@w_C_Hww_ǰww_\ wwϠ '_ w_ߠ w__ע Gˡ,PwGˠ '_נ h__ o__t`_w_@w_\ 0h _\ 0\ h H' 'G'G< 0A@p`ww_b]@@w_@w_w_㾰w_3@: @w_瀤he_@Ғ /w_瀤hHZ_ w_L /H _ w_L /O_L HHC_$ w _,`_g@@aHw!< @#H /_,`?H*'H_@!H /_,`?H'_ƒ H /_,`?H' /_,`_ w_瀤o 'G< 0w_@w_}'_@ѐG'G<  A@oawww '_G @'_G,`< ` 4H < w4 4b]L@_,`_ߠ w 4b]L@_,`G 'G뀤 O A@o`wwKw_@'G +$H b]_@@{h`]_G@@ /G`],@ /G < `],@ G *< `],@G`] @_@`]w_A@nctwwwww_\ h#Jw_@g'G << @_t _\ b]__ߐ@@+_\ G@ _t`_\ wϠ '_ '_ wǡ4 4b]L@_,`_Ϡ wǠ 4b]L@_,`_Ϡ wGˠ 'Gˀ O_\ w_װ0A@naԮww_c]a@]b]@w_0A@naLww_`X]ax]b]@bw_A@n`Įww'w'JwG'Gà +'?'_ِH_瀤hG H_@ 'GG@'GG@'G @;w_H#_瀤h_Ϣ _G$ < @@ /_G$ < @,`_G@ _G@@H _Ϣ _GÐ@@}_Gà@ t` /_Gà < @,` _G$ < @,` _Gâ@,`_Ϡ @ӵHb]_Ϡ @@z_@?HP'a]\ w_ǀh_Ϣ _Ǥ GÐ@@FH_@?H5'_\ w_ǀo_Ϣ _GÐ@@.H_@Ȑ?H'_c]\ t`_c]t`_t _Ϣ G @@Œ /_Gâ@,` 'Gߡ< A@mawww'w'Gۀ H b]_ߐ@@z H_w__h_w__h%_L #H __h_L G뀤H_Ϡ w__h_L G뀤O_Ϡ wH[__h_L G뀤H_Ϡ w__h_L G뀤O__πh"_HGۀH b]__ǐ@@yH __Ϡ$< _Gې@__h_L G뀤H_Ϡ w__h_L G뀤O_w__ oz㺀A@lb\w/w'b]O ` @O,>b]_O@@>@ޒ '7G7 HH4G7_@@ڒH_ hG7@H#_Őw_G7@  @wG7@__ߦ@_G@ __ߐ@@ A@l`Ԯww $@cw_b]Ґ@c 'b]_@wG_@@@@Y_cHb]@xTc]\ h _@@@@  0A@kcwww@&a]\ w_瀤h___@HH _\ w_瀤o@A@kbwc]\ hH&b]@w_hb]wa]c]t`_@1@@@ :F G 0w_Kw_@’ `?'G'G< 0A@kawwa]\ w_瀤h_\ _@_ @ H H '_\ w_瀤o 'G< @w_H_H H' 'G'G< 0A@k`,wcp]!c]@3: a]\ a`]\ ܐ@@@Әcp]!c]ax] `] `] `] c]\ a@]\ @@s@s`A@jbwww_@ 'G H?H'G/@@HG@?H'_Ww_π$(hG@ߐc]_@@whH'_G@  @wG@ɐ_w_ #"cH_ 'ˀ  Hˀ H__ϐ@@Ȓc]_ː@@w7H'H 'ˠ '_wˀ H_ w 'À!H#_á,0@'++ǀH__ϐ@@c ]_@@w H}'+'à 'À! Oˀ H_ǥ,`,`$(4h__ϐ@@tc(]_@@vHX'HGˀ HCǥ,`,`$$04w'_'wǀH _Ǡ$ , 4@w__' h__h__ϐ@@Bc0]_@@vH&'__'hH__ϐ@@-c8]_@@vH'ˡ< _$`8__t`__t` _$`( 'G<  A@ibLww_\ h H"'_ a@w_@~c@]@~$@cH]@@~__@'_@~G'G<  wwwww_\ w_׀h&_ $H_\ h _ _\  H __t`__t`__t`_w_\ w_׀oA@i`|ww'www_h _ߦנϐ@c(]\ w_瀤hϨע_@_\ w_瀤o_߀hB_\`\`@[c]\ _\ $@c]t`_πh_\ _t`H_\ _t`_\ h_D HGH _D H@~?_$`H_@~ `] $ `]$` H' 'G< 0A@hbtww'c]\ w__c]\ $@ hGeH__c]\ $@ h GTO0A@haww_\ h8_\ w_ $H cP]_ a@@t_\`\`@~c]\ _\ $@c]t``] $ `]$`_\ _t`_@}_\ o@w_\ w_h _ $`$ $`_t @w_\ h _\`\` @~m_t 0A@gcwwc(]w_\ h3_\ w__ a@@}! H @ f_g_D H?H_D H@} __\ 0@}_\ _t`_@} H _w_\ o㾀A@gbw7w_\ h_H cX]_ a@@tvH('_ a@} _$ H_D H H(@|6B H"_R?H_ a@}Β _$ H_D H H@|B H _9?O_D H H _D Ho@@}H?H'_\ h!< @# H c`]_ a@@tH'__t`H_\ _h ch]_ a@@tH'_D H @~ : w?_? h cp]@sH'_? w?_D H_?@ @~?H cx]@sH'_D Hc@ @ h c]_ a@@s̒H~'c#PcKH c]_ a@@sHn'g Hg Hc]_ ag@@sHZ'_ (kHc]_`ak`(@sHF'_D`H\`$ @|Z ?h c]_ a@@sH1'_D HO@ @ h c]_ a@@sjH'_\`\` $ (wGO_G@H c]_ a@@sRH' 'G< @w_H H'_D H?H _D H@{?_$`H?'G<  ww_\ w__h_ __\ @h H'۠ 'G'G< A@f`$wwwww_\ w_D H?H_H c]_ a@@r<__\ $ hc]@r0_πh __@H_πh_Ϣ $`$ $`_\ w_πh__@HH _\ w_πo_πha]\ 40wǐ  (@w__Ǡl@L_t`_\ _\ $@w_a]\ ha]\ w__t`c]\ _\ @c]t`a`]\ c]\ h_D`H@MHa`]\ c]\ h _D`H@:O_\`_D H\`  @ۚ_t _\ ?hc]_@y aB @zO@@q`]  `]$``]  `]$`c]\ a@]\ h c]\ a@]t``] `] H `] `]$`_\ _t`__t`__\ hax] 'ˠ ax]$`_$` _Ϣ $` $`__t`__\ $@w_߀h _\ _3$@_$`_\ _@w_װ0'G h@}w_  h@y?_$`H_w_㾠A@dblwWw''G ِw_GcH]@y&$@'G H__@yՐHjw__ a_G@@y#__G@ ac]@@y__ a@z H __ L%`,$`__G@ ac@]@@y__ ag@@{H w!< @# H__@yH*w___t`__ LG4 , !<,@$___t`@G (H_G@$ (__ M@KH__ MŐ__w_ ww_w_@x < Fw_ע a_ߐ@@y3_ע M_@__א@>H_@y:Hw_w_@A@ccTwwc(]\ _t`_c(]t`A@cbww'c]_@@y!@xF'@{*w_߀h*@x[B H$c(]\ t?H@{w_߀h@xCB H c(]\ \?O_߀h@x/B Hc]@x&B @x{@@pNHz /G,@G '_@zw_׀hh_נ @w'_ע cH]@HHNGGӠ@ < $hHDG@_נ @@xic(]\ w_ǀh_Ǥ aGGӠ@$ < @w̔HH _\ w_ǀo_ǀhHGGӢ@G@aw_ǀhH__@z|w_׀o_@z ww_\ w_\ w_ L4  < _ L4  < $@'GӀHGӠ H'_\ @_\ @h H'_\ @_\ @h H'?'G< 0A@bb|wߠ 'c(]\ w_h G '_\ w_oG瀤 HHUG @|w 'c(]\ w_h__G,0t@G '_\ w_o_Gc]@ @x+ 'G$ H_G< ,0\@,\@t`G 'GG$ O_G$ < ,0\@t _\ c(]t`_@w@A@b`wa]D HH->  VVa]\ w_h / _\ ,_ A _\ ,_\ w_oQ a]$`@A@acw@7* a]$`0ww '_ ,H__\ 0,`,`49HH) '_ , O_\`0`, ,`,`4@{_t 0_\`0`,,`,`4_@(_ ,` $` A@aawwc(]\ w_瀤h, '_ ,H__\ 0,`,`4H H' '_ , O_\ w_瀤o 'G< wwww___ߠǐ@@r_ǐ@禍`??'G'Gۡ< 0A@a`Dww_b]D @@wi'G H@uB Hb]D H_@wS 'G H b]$`G'G<  A@`cTww_w_'G HGH-'@uz " a]\ w_߀h_\ w__@>_ߠ 'G HGH '_\ w_߀o?'G< 㾠ww_'wGۀ HG۠W@@vH_ې_t _\ Gې@  @zwG@u_w_@w_ `,  `'_ xHt`L $@H H' H'G< A@``wwwwww__ @w_ w_40 _$`_ wǠ '_ h)__hӀ @ h c]@lHw__ @w_ w__ ӡ< ,PwӠ '_ o__t`_w_װA@_cTwwwwww_  p@t__t`__$`__t`__$` _bH _@{_@|l HC'__ǐ@ww_h G`X]L@H?H*'__@w__$@w__t`__$`_@{__c]G4 e_ǐ@@@t՘ < _t`( 'Gӡ< A@_atwwwww_@s '_@xАwǠ '_\ (GӠ$@w__瀤h_w__GӢ@_@@s_'G_瀤h_GӠ@_t`_GӠ$@_$` G H _@{ے 'G OG H _ H_@{_H%wG H`]_F@@kH_ H `]_F@@k֒_@t;w_װww '_L /_ wO΀ HHO΢G,@G۠ 'Gۀ h?HC'O G,@_L $ 0< w_ w_π h?H.'_πh_L $ 0< w_ǀ hH_ w_ϥ,p,p_Ǡ@wO__t`_L H?H'ˠd'G'G< ~wwwwwעW__ W@'Gˀ H W_ߐ@u S' SG_$` SG HHwϢW W_\ _א@Ֆw_ϰ㾀A@^`w7wwwG  p@rw_ 'gG@zR___O@w_wGG@z 'C__G_$@wGC HGC?H_o  oG@zGC H _o h `]@jːH wߠwנאYאUw_߰A@]cwwww'w___\ @wG〤 HL '_ @/ ' < w H%_ w_h _!<,0 @hHBw_ @/ '_,0 < @w O__$@w_ h__׀hH w_\@tHG〤 H__@@֒w_\` t`H`]@i_w_ϰwww'ww__G_א@@lw_h?HH'__@@B'G〤 H'__@@1w_h?H0'__ @@w__@<_@Β'G〤 H?H'_πh___@ɔ_t _\ h?'G'Gˡ< wwww___\ ː@w_˨_ߐ@w_h?'H_\_@tG'Gۡ< A@\bwwwwwwww_$ _w__@'__@@1`w_\ _$@_t`Hf` ]_ aG@@hRG4 brHuw_G_@@w_h`(]_ aG4 bW@@h_ H__@@1w___ @@ΒH-__@ w_ H__@깒__@@wHG$  O, < #b_D_` $`_w_@"'Oz_ǰA@[cwwwww_w_צǠϐ@'H+__G__@@}'H+_瀤h__t`H"`0]G__ a@@h?'HGà$  O, < #a_DtG'G< \\㾠wwww_@twW_  p@pI_Www_ߠ '_@w___g@RwO_Ow__@x 'HH____O$@wGӀ OGӀ?O_@wѐGӀ H__߀h_W@pHw_Ww_װ0ww__@w_ߥ4p4p@w_ߠ w_A@[`wwwww'__@ؒw_,p,p$bp]@w_\ w_ǀh_\ _h_\ _h ____@@H.wGۀH_t _\`\`t`_\`\`t`b]\ _\ ($@b]t`H _\`\`(@tWw_D 0_$`_\ (_t`_w_ϰ@A@Zb\ww_\ h_\ @oސ_t _\`\`t`_\`\`t`b]\ _\ ($@b]t`@A@Zaww_,p,p$bp]@Ð_ w_! oA@Za$wwwww'__@w_ϥ,p,p$bp]@w_b]\ _ߠ@b]t`a]\ wb]\ cP]\ h(_a]h!_w_D 0 H_l_\ wb]\ cP]\ h _a]oa]\ wb]\ cP]\ h#_a]h_w_;_\ wb]\ cP]\ h _a]o__t`__t` G_$`0__t`__t`(a]_t`a]\ _t`_a]\ t`_a]t`СA@Yb4wwwwwww_w___D _א@@w_h`8]__ a@@fSHw_F___ϐ@R w_h7__@@.*w_hHnw__ @@%w`@]_@__ a@@f__@b__Ϡ@@[w_hHEw____ߐ@w_h`H]__ a@@e_@nWH(w_____ǐ@@@08w_h`P]@e<_@n=_____D @@_w_A@Xc4wwwwww_w`8]D H<_D 8 H7__@@-w_\ _$@w____ @@@% H__ @@{w`X]_@H@@ex__@hHTw_Ϩ_ߐ@_$ H1___^ __@@swH0___\ @ߖwH#w`0]_D __ a@@e4H_D $  O, < #b_D_w_װhh0w'_\ w_߀h _}HH+w_\ w_ ( HH w_ߠ$w_D 8 H _,`,`4 Hw_ߠ w_,`,`4Hw_ w'_\ w_ߠ$w_D 8 H _,`,`4H0w__ (,`,` 4w_, 4@'!< @HHw__ (, !<,@, 4w_`,0 `Hw_A@Wbwww_\ w_\ w`]D H``]@mh H'Ǡ 'G`]$`_׀h_ƐHHw_\ w_\ w_D 8 H_ߠ w_נ w_נ$w__ ,0@'_ H H '__ $ < ,0@''_D 8 H 'H '_נ w`]D H`h]_ ` ` `< _ (@@@ntsb]D H`p]@m8h H'à 'Gb]$`b]D H __ (_@@@s'GÀ HHJw_Gá4 @HBwӠ@4 '_ˠL@4_@7'`]D H`x]Ө$@Ð@@nGH _Ð@HwG H'Hà 'Ӏ Ow_A@VaԮwwwJc(]\ h H'`]\ hc(]\ Hw`]\ w_w_ ,H$ '_ ,H__\ 0˥,`,`4仒HH>ˠ '_ , O__ߐ@w_׀h+_D H?H_ÐH c]_ߠ a@@bH__t`__t` _ _@䘒_`]t` H%'_`]\ h c(]\ wH_\ w_`]\ h_\ w_߀o 'G< 0ww_w_߀h__ߐ@4h_H w_\ w_߀ow_㾀A@Ub|w7ww_ߐ@ w_πh`]_=B@@brHE'___ߠ?@  H`]_=(@@bX'H ?א@'G瀤 H`]_=@@bA'H _h__t`_@qܐ__ߐ@@kG'G< ww_@H__@'G HGH(':_@HGH'___@('G H __@6__@ǒ'G'G< A@Tcwwww_@hHH/w____@ɘ w_h`]_<|__נ a@@a__@___@@w_w_߰ A@Tb|wwb]\ wߠ 'a]D H__@ H_H"wG '_w_ߠ (wGa]D O_a ]@Ha ]Hww_A@TaTwww'w__G4 ZP_ߐ@@_@lH_h H]'`]D a]D H#`]D  ,`@<` @< `]$`b]\ `]D ,p,p@mb]t a]D 'Gˠ a]$`b]\ G˥,p,pw__t` G_$`_@m"_t _\ __@@h__ߐ@s 'Gۡ< www_%w_h_D _$`_\ _t`_\`\` @mZH9w___@w_ǀh_H*w_א@w_πh_____@@w__א@@i_H w___@w_߰A@Sawwwww_@Sǐw___@w_ǀh__h`]_;_:@@__ǀh_ H_H `]_:@@_v_߀h__t`_w_װA@S`<wwwww_Y+'Ϣ_@ᙒ?'wӠ@͔w_ǀhHXwGGπH__t`_߀h_ߠ@w_HDwGӀ H`]wHGӀ H`]wH_@hH+w_@gZ'G (< _h__G@@gcH _G@@:'H_@gHw_@gO_װ㾰A@Rb,wgwwwwww_c]__@@g _$`o@go__D @@go__@@g_ߠo@@g0A@RaDwww 'ch]D HH__@@j(H@fB 'G瀤HG瀤 H__@@i<HH@fB '_@oG瀤HG瀤 H`]_G@g!@@^H'_n H `]_@@^H' 'G<  A@Qcw'wwG__@@mL h`]@fB @f@@^H' 'G< wwww____ߤ@@ 'Gۡ< @A@Qb\w'b]D H G`]@@tG@fސ H`]@]0w_@g /w_瀤h H'__$@ 'G<  A@QaDwwww_ؐ'G۠ < _h@e N" ?HT'__Gې@@eߔ_Gۢ@`]@@ff_j!$'G߀ H9GۀH5@eB H/__Gې@@e _G۠$ < ,@_@h!H _m H?H'_G۠@$ `]@@f+_jw!$'G'G< ~A@Pc\www'www_ᛐwc]_ϐ@$p'Gǀ H/@ewB H)c(]\ 琒?Hc]_ϐ@$S'Gǀ H@eZB H c(]\ s?OGǀ H%@eFB H `],w@@]iH'a]c]@e/B @e@@]WH'ˠO  p@e?O`8]D a]@@e pwg!'o@eː_wOG'WO@e H O@e O_G@@e_wO_'W_OwǠO@eВ 'â__O$@@eGǦ_g$@5 Ha]@\cwg!'oGÀ OGÀ Ha]_7GÐ@@\KO@e'GÀ Ha ]_7GÐ@@\7۠@@en_ې@a H a(]_7@@\ G5_πh _w_wc]@@g Ha0]c]@doB @dĐ@@\c]_ϐ@R'Gˡ< wwww___Ǫ@@_߀h_ߠǐ@@H H'ۢǦG__@@'Gۡ< A@Oadwww_%H H>'_Ӡא@Zw_߀ha8]_6@@\!H&'c]Gӡ4 U3_א@@d '_G___@@;'_@deG'G< w_ૐo@fH H' 'G< w_ǐ@k'G< w_ǐ@WH H'_㘐'G< A@NbԮwwww''wנ 'G߀H 'G߀ H1_׀h-a@] `\@OtG〤HaP]D H' '___G@@H @@)w 'G〤H__Gߡ4 To_@ʖ'H__Gߡ4 T__@'GˀH_@cG'Gӡ< A@N`ܮww'w''w_\ (ِw_ !< @# H,aH] `\@OtG@@-c h___GG_א@@K'H?'Ϡ@(oHg_ǀh__GG_א@@1'HT_# h2_@gw_G_@@ih___GG_א@@'HaP]@bxB @b͐@@Z'_@cH_G@  @h6w___GG_א@@'__ǐ@@dG@bG'Gӡ< A@Mawwww'aX] `\@OtH_@c͒ 'Gۀ Ha`]@b_B @bp@@ZCH'_G_G_@@  H ah]_@@Z+H~'Hz__\ (@@,H@aB @b?wap]__@@Z H`'G〤H__ax]\ _@WH__ax]\ _@kH ah]_@@YH6'ߠ@'hH/_ax]_@H&'a]_@@YʒH'_ !< @<# H#OO# O^#(OO 'Gߡ< 0'wG_@ @g h?H'_ #PcKH?H'_  H _  H?H' 'G< 0A@Laww'_B'G H a]_4#@@XGGHa]_4G4 R_@@X@'!?< @4 !? @4 !?# @,  `, '4  ww_@`w___ߐ@@`__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@`_w_߰@w_w_ ww_@`Pw_@`Lw__׀h__ߢ@_ע$@_@@`TH H'Ӡ 'G'G< 0A@Kbw''G,084084a] @ HG ?G@'G'G<  w'w@`" _G@@`w@`B H_L H __h_ס<`4h?H'__$` 'G<  w'w@_" _G@@_w@_B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@JbԮwww_\ h_@%__\  ha]a]@@_k IH H' '__t` _\ _,@0w'_ @%P_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@^@@@'@ww__\ @@%__\`\`@'ݔ@w_$ _$ _t 0ww_ (! @'__@ @@^_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@^l/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@Ib4ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@Hcww_b@]@ @]n`?'G'G< 0ww__@ @]T'G< @ww__@ @]6@w_  @]V@w_L /HH H' 'G'G< 0www___@ޖw_߰@A@H`w/`H]L@'4 0www___@ @W'G< 0www___@ @_'G< 0'wG_@\@@@cZw_@w_\ 0h _\ 0\ h H' 'G'G<  www_\ _t`_ !< @# H_  @@?? !'G! @H'_ !< @#(H!(H'_ !< @!$@#8@?!'< @'G'G'Gۡ4 _$`_\ w_߰0ww__$@$ 'G< 0ww_D _\`\`'G< 0A@G`ww_a]@@\w_A@G`w'ww 'a]\ h2# w_@['G +'G @`Ba]t a]\  _G@@[a]\ G@ a]\ t` /a]\ \ ,a]\ a]\ t`a]_@@\Ha]\ w_׀hrG Hn_\ a]_@@\1_נ @^=w_hHN_@^;w_hC_ @[B &hH2_ _ G$ < @@[JHH"GH_ @ &@[1G 'H_ @ &@[,H 'H _@]w_o_@^_\ w_׀hG OG H _@-Ԓ  "H H'G'G< 0'ww_ _ H H '_ w_ w$ ' OH _ _ @ H H' 'G< A@F` w'wwwϠ 'c(]\ w_׀hGˀ H '_R_ (''ÀH+à@4 '_@w__@$'GH'HG H 'H'À OǀHF_@퍒wG4 __@dH5_ @{w_h G4 __@NHGˀH _wGˠ 'H__@ϒH 'HH 'H _\ w_׀hGˀ OvGˀ H__ϐ@ĒG'G< 'wwwهG_@'G_@.'G׀H GӀH?H5'G׀ HGӀ H?H*'GGӀHGӀ@?,@ @w__@v H'ۢ@XH?H '_ߠ@c 'Gۡ< СA@Dcww'w'G HG (H?H'ߠP x (@Y 'GHR_GL@/ 0H  9H$ 0/H" aH  fH$ W/H AH FH $ 7/ /H?H@'G,@G۠ H, /Gۡ< < @w_ @,G۠ 'GGOGۦ_@%'G〤HG׀?H a]G@@QEH'G'Gߡ< A@Daww'_m'a]_+@ (@XG (HGHa]H5wG (H-a]Gː@1 'G߀HG〤HHG〤?H  Ga],@a]H wG 'G (Oa]w_0w' ' 'GH6HH(G뀤HH" 'H H 'H_w_ w_L .H OOO /OOH G 'GGOG'G< A@Cbdwwwa]ς `\@Ot_\ ϐ@@ 'G_D HϠ@@_t __$`_\ Hww_СA@Caww'wwߢ@wϠ '_t `(]w_\ h]G@?,@_ @w_w_\ G_@$YӘ_@ w_hG'G 'G'GH_@\_t b]D HH!HG Ha]@@XJH a]@@P_Ǡ w_\ o_@X=G'Gۡ< PA@Bcdww'wwߢ@3wϠ '_t `(]w_\ h{+_\ G_@$YV++@ Ew_hHZa]+@ZK?@Z0H O!< @# H+wH$_+@@WՒHa]_@Z+?@ZH O!< @# H_wHH G'Gà 'G'GH _@[d_t _+@Œb]D HH _Ǡ w_\ o_@WG'Gۡ<  A@B`̮ww'a]\t`\t` 'ۢGۡ,0\@@V'GGH_Gۡ,0\@Gא@@V”HGH 'G۠ 'Gۀ  o 'G< СA@AcĮww'wwנ 'G (H __@)aH Hz' 'Ϡ 'GH;_G$ < L@ }H3G$ 'Gπ H,_GL@ @H_GϠ < L@ {H_GϢG$@xH G$ GϠ $@'G'H GϠ$ 'Gπ OGH _G@H?H*'GH9GˀH5a] `\@Ot_GϢ@@@o'G H __< _@@H'G H?H'a]_א@ 'HGˀH_G_א@g'H _G_א@Ӗ'GӀH?H'b]D HGӀ Hcp]!b]\ G_@@UGˀH_GϠ < L@ -H?H' 'Ǡ 'GGˀH*_GGà@ < L/O 0HO 9H Gǥ,`,`O@$ 0'H?'Gà 'Gǀ HGGˀOG#}xaHGw?'H$Gǀ HwH '_GϠ@ Gː@@Ztw_@w_@VGH?HF'__G_@@ssH._hcp]!a]G__G@ @@UdHcp]!b]G_G@@@UR_@U 'G< w'w'_Gː@@'GǀHGHD'ߠϐw_h?H9'_ĐH?H0'G〤H __ @ H#'_\ 0w_hG$ 'G〤H __\  @z H '_\ w_o?'Gߡ< w'w'_Gː@@2'GHGH='ߠmw_h?H2'G'G$ 'G'GH_WH_\ 0h?H'_\ 0\ wG'G$ 'G'GO__ @ 'Gߡ< A@?b\ww'w'_h GH_@T!'_瀤h_GÐ@@ÔHH_wנH9w_瀤h_ 4  H _ H)hHGw_ 4  < G〤H_H;w_ 4  < H_\ wH,_ 4  < H_\ 8wH_hb]G_G4 E1_ 4  E*@@@LHwO_װA@?`$ww'w 'G H _G$ < L@ }H?H'_G@$ w__ǀh_L 'G {H__ǀh _L? ^HH _Ǡ$ w__ǀo__h?H'_Ǡ wc]\ _ǐ@ @S\H _L  }H 'H:cH]\ _ǐ@ @SFH _L  }H 'H$ax]\ _ǐ@ @S0H _L  }H 'H_L }H 'H?Hf'__Ǡ$$ < ϐ@@H?HV'G)w_h?HK'ÀH*___Ǡ$$ < @@+Ŕw_h_ 4  H _ Gh?H''__ @H_G_á< @w_h __ @ H'?H' 'G<  w'w_G@$ w_ _׀h1_L /O?HOր gH_L? -H_נ wG__$@< $'_G_@ H '_נ$ w_ _׀o?'G< w'w '_G@$ w__πh#_L 'Gˀ 0HGˀ 9HHGˀ ~HGˀ ^HG'H _Ϡ$ w__πoGۀHK '__$@'_Ϡ w__G@hG˥,`,`_L @$ 0'_Ϡ w__G@ oGˀH GG$ H 'Gۀ ^H _G_Gː@HC'_G_Gː@JH8'_G_@)'G߀H H)'_G_@2'G߀H H'_G_@'G߀H H '_G_@ 'G< ww'w_w_Ew_ǀh H-'_ 4  < H__@Qv@: @@*Ow_ǀh H'_ 4  < H H '__ϐ@ 'Gۡ< 㾠A@<bwWwwwwנ?'w_L !H_L  !H b]_@@H_ w_@@7 H b]_@@Hϒb]ߐ@_w_πh_\ j@_\ w_πo_߀hWߐwo_o EhHD_@Q_o\ @h_o\ @wwH_o k_@ww_whH(_ww_wb ]@@Sww_whH_w @ @q H __o @ 'H_߀o@c_@QZ_?_w_πh_\ _\ w_πoG'G< СA@;c|wwwww'w_w_www_b(]@@H_b(]@P\@w_b0]@@S3w_h_h H/'__$@w_w_\ w_ _t`__\ l@L$@'_\ G,p,pג_\ G,p,p__@@& 'Gá< A@;awww_L @H _L  {H_L  -H?H'_@Q0 }w_h?H'_ @@P w__h?H'_߀ h?H'_w_ߥ,p,p@TgwϠ '_߀h_Gۥ,p,p @pG۠ 'G_߀ow 'a]b8]@(a__߀hwa]b8]@__߀hH__ߡl@L$@'_-__Gۥ,p,p\`\`@|__$@ 'נ '_߀h_Gۥ,p,p@9G۠ 'G_߀o_@P G'G< СA@:bwww_b@]@@Rw_h __@@gH'__h a]@@Y'H__$@@͒__$@@_@@B'@ސGHGHT'˒ w_߀h?HH'_L H_ Hwa]w_@@H?H0'ħ w_׀h?H$'__א@ρ w_πh_\ h?'H  '__\  @_MG'G< A@:`www_@N'__@'GπHGH'Gπ H GGӀHGH'Gπ H?bH] `\@Ot_\ '__GϦGӠ$< @@j_\ @'G H _G@GH|'__@b@GG$@GϠ@Hi'_@Ov @w_߀h?H]'_G_$@$< @U'GˀH?HL'_Gˢ@_$@'__ߠ$@@S3w_L _ߠ@?,@w_@-Đw_׀h_\ (h _\ (\ \ h bP]_ߐ@@F>H'_@N_\ (\ \  w___ߐ@_@NG'G<  ww__Ӑ@'G< 㾀A@9`Lw7wwww_hbX]w__@@P+H b`]__ߐ@@EC@MB H@MB H9_@M_@M|@ @Rw?_?_@@N_?_@@Oݒ__?KG@@BHbh]_?__ߐ@@E bp]__ߐ@@E㾐A@8bw?'ww_@M8'W_hbx]wap]_W< @{є'[G[ HG[ $ '[G[4 ap] H,ap]\ G[,0\@w___Wh__ H_W@@MHb]_G__ (! @4 @@D@W_@L@'SS 4@Q|wG_G_@@Mx_G_@@OCap]_GS< @{u'[G[ HG[ $ '[G[4 ap] H,ap]\ G[,0\@w___.Sh__ H_GS@@LHb]_G___ (! @4 @@DA@_g@@OH b]_@@D2@LB H@LB H b]_@@D_G@M www'w__G_א@@@'_$`G'Gӡ< A@7b|wwww'w_@L0'_ $@Li! _$d_G_@͔'GπHGHX'_L :H 'Gǀ H_L  /H _ _@H@'Gǀ H_L  :H _L  0H_L  3H_ wH _L $ 0'_ wG__$@< $'_ _@$@Ly _,dap]\ hap]ap]_Gǐ@ze'G HG $ 'G4 ap] H=ap]\ G,0\@w_Gǀh _ H_Gǐ@@KHH!_ (! @4 G4 H __ ,@M H'G 'G4 ap]  OG〤H G__@?H'_w '_L H-_L {HGˠ 'HGˀH _L }HGˠ$ 'H GˀH _L :HH _ w_L O_L :HdwG〤H__$@ @Ow___$@@K ___$@,__$< @H3_ w___$@@@'G〤H___@4_@K_@_ _@$@K| _,dGH'G〤H b]_@@BwG'Gӡ< @'!?< @4 !? @4 !?# @,  `, '4  ww_@Jw___ߐ@@J__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@JQ_w_߰@w_w_ ww_@J w_@Jw__׀h__ߢ@_ע$@_@@J$H H'Ӡ 'G'G< 0A@5a\w''G,084084a] @ HG ?G@'G'G<  w'w@I" _G@@Iw@IB H_L H __h_ס<`4h?H'__$` 'G<  w'w@I" _G@@Iw@IB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@4bwww_\ h_@O__\  hb]b]@@I; IH H' '__t` _\ _,@0w'_ @ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@H@@@Ɣ@ww__\ @@__\`\`@@w_$ _$ _t 0ww_ (! @'__@ @@H_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@H</w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@3atww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@2b\ww_b@]@ @G>`?'G'G< 0ww__@ @G$'G< @ww__@ @G@w_  @G&@w_L /HH H' 'G'G< 0www___@ޮw_߰@A@2`,w/`H]L@'4 0www___@ ''G< 0www___@ /'G< 0'wG_@FW@@@M*w_@w_\ 0h _\ 0\ h H' 'G'G< 0w ,@Jw_#w__@?_$`_߀h _ 4  H_t 0_F 'G<  A@1adwc8]D Hc8]D HM'b]Hb]@FKw_h  c8]$`c8]D H3' c8]$`_@$@Ghې@H b]@@=b_@$@Fo_@F/c8]D 'G< A@0cww''' '۠ 'wb]\t`\t`w_h Gۧ4 _  HH_hJGۧ4 _ H5_\ Gۥ,p,p\wG۠ '_w_@( w_h _ 4  < HwH_\ h @I_t _\ $  'H$ '_,p,p\w_\ D '_ Hb]@<Đ_`%,< G@4 , %`Gנ '_\ 0ww_h]_\ \ h @Iw__\ t`G_$`H_\ \ wG_D HH7G_$`GGH_\ h _\ @9H _\ w_\ D 'H_\ ϐ@_\ w_`%,< G@4 , %`_\ w_o_o4Gۧ4 _  O-O)_w_߰@'!?< @4 !? @4 !?# @,  `, '4  ww_@D\w___ߐ@@D\__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@D_w_߰@w_w_ ww_@Cw_@Cw__׀h__ߢ@_ע$@_@@CH H'Ӡ 'G'G< 0A@/`w''G,084084a] @ HG ?G@'G'G<  w'w@C" _G@@Cw@CB H_L H __h_ס<`4h?H'__$` 'G<  w'w@C~" _G@@Cw@CsB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@.aDwww_\ h_@ __\  hb]b]@@C IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@B@@@ @ww__\ @@__\`\`@ y@w_$ _$ _t 0ww_ (! @'__@ @@BW_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@B/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@-`ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@,aww_b@]@ @A `?'G'G< 0ww__@ @@'G< @ww__@ @@@w_  @@@w_L /HH H' 'G'G< 0www___@zw_߰@A@+c\w/`H]L@'4 0www___@ 'G< 0www___@ 'G< 0'wG_@@#@@@Fw_@A@+a̮w'G HG H b]G@@7 A@+aTw'wG,0a]@wGҐ_D` D` H*_D  ,`@<` @< D H_D` $` H _ߤ D ,`@<` @< $_\`D` ,0@D_t G_@@Bv_\`D`,0t_\`D`,0\?h?H '_ߢ D` $` 'G< 0A@*cw'G,0a]@wGk_D  H H'G_\`D`$ < ,0\@B0?h?H '_ D`$ $` 'G< @w_ X_ T_ P_ L_ H@'!?< @4 !? @4 !?# @,  `, '4  ww_@? w___ߐ@@? __ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@>͔_w_߰@w_w_ ww_@>w_@>w__׀h__ߢ@_ע$@_@@>H H'Ӡ 'G'G< 0A@)cLw''G,084084a] @ HG ?G@'G'G<  w'w@>f" _G@@>fw@>[B H_L H __h_ס<`4h?H'__$` 'G<  w'w@>." _G@@>6w@>#B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@)`www_\ h_@˒__\  hb]c]@@= IH H' '__t` _\ _,@0w'_ @_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@==@@@B@ww__\ @@c__\`\`@)@w_$ _$ _t 0ww_ (! @'__@ @@=_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@</w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@'cdww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@'`Lww_b@]@ @;`?'G'G< 0ww__@ @;'G< @ww__@ @;@w_  @;@w_L /HH H' 'G'G< 0www___@*w_߰@A@&bw/`H]L@'4 0www___@ 'G< 0www___@ 'G< 0'wG_@:Ӑ@@@Aw_0A@&`ww_c]@@:w_0ww_L H H '_L _L H_L ,08408< _L ,08408< $@H '_ w_w_ wOG< 0ww_@:n'_@:j'GG〤H?H '_G@G$@_@@;H'G< 0A@%bwww_t _t c]_t`_h__@@l@w_\ h _\ @:Ր_ђ ww_\ h_\ Hww_w_h_\ _t`__w_0wwww___t`__t`__t`_@ _\`\`-0A@%`www_\ _@ hc]@1e_\ h_t _\_@ \h&_\ ,p@40\_@ h _\_@ tH _\ ,p@40t_\`\`@>ǒ_t 0A@$cTww_\ w_\  h+_\`\`$ L,084084a] @ H_ \`$ t`_\  h_\`\`$ L,084084a] @  O_\  h(_L ,084084a] @ H_ w_ \`$ t`_\  h_L ,084084a] @  O_\`_\`@: _\`\`-@A@$atww_\  h+_\`\`$ L,084084a] @ H_ \`$ t`_\  h_\`\`$ L,084084a] @  O _\`\`-0A@$`lww_\ w_\  h(_L ,084084a] @ H_ w_ \`$ t`_\  h_L ,084084a] @  O_\`_\`@: _\`\`-w' ' 'G @=w_\ w_w__\`\`hV_G_\`\`%$@@: wGߠ G〤H G, '_G,0@@=w_׀h_\`\`$ w__Ϡ$@ ' @<w_G@ڒ__G@@%__Gߧ,0t@Gߠ '_Gߡ,0t@_נ w_w__\`\` o_w_0w_w_\ h_\ Ð_\ w_ w_w_@8_\ o_@8 ww_\ _\ h_\ Hw_\ w_'_\ _\ G@@7ʔ'GۀHGH'_\ _\ h?H '_\ _\ @ 2h 'G'G<  A@"cwwwwww__@hc]@/(__\ hc]@/__@_\ hc ]@/__瀤 h ___$@_\_@_׬@_@\$$@@8ɔ_\ _@__א@@7G_\_ע@_$@0wwww____ߐ@0www___@y0www__@+_\`\`__@@6_\_@@0www__@ _\`\`__@@6Ҕ_\_@ wwwwA@"`<www_h_ @w_\`\`\`$__א@@7$'G〤 Hc(]@.6G_Đ@h#_G@w_\`\`\`$__א@@6'G_@hc0]@._\G@ wwww_S %w___נ$@B_L HH-_נ w_L %H _ %_ wO____ߐ@w_πh__Ϡ@wH_ %O www_w_\ h5_\ @5w_πh,__\ _ϐ@@5H_\ h__\ @_Hw_נ w_\ h _\ @5Őw_πow_߰0ww_\ ' 'G瀠H_\ GL@ %H_7 %__\ GL@/G 'GG瀤O www_\ w__@_\`\`__@ @6w_׀ h _\_נ@ÒH _π h_2_w_߰w'w_\ w_\ w_瀤h_Hw!w__ǐ@pG_\`\`\`$$ @@;hw_ h_π h_H__א@w?Hw_hH_ \_@t_=O _\`\`-_\ _נ$@w_߰ www_\ w_瀤 h w_瀤(h2__@__\ _@@74'GӀ H @4B "HHHG_瀤h _GӐ@ H'_,0w_瀤( o_׀ h_|?'G<  ww'_Ȓ_@5H?HD'__@5'G〤?H%_ _ w_\ w_Ϡ _t`G_\ _,@GG뀤HH _@5'G〤?OG〤?H _\  h?H ' _\`\`- 'G< 0ww'__G@H?H'_\`\`$ LG뀤H_\`$ @x 'G<  www_@5Ȓ 'G߀ H?H'_G_@'G@4Gۀ H?H'G'G< 0ww_@3Ґ'__@.G瀢H H '__G@ȔG'G< 0A@`Dwww__@͒_c8]@ _\ 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@3bw___ߐ@@3b__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@3#_w_߰@w_w_ ww_@2w_@2w__׀h__ߢ@_ע$@_@@2H H'Ӡ 'G'G< 0A@`w''G,084084a] @ HG ?G@'G'G<  w'w@2" _G@@2w@2B H_L H __h_ס<`4h?H'__$` 'G<  w'w@2" _G@@2w@2yB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@a\www_\ h_!__\  hc@]cH]@@2  IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@1@@@ww__\ @__\`\`@w_$ _$ _t 0ww_ (! @'__@ @@1]_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@1/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@aww_b@]@ @0`?'G'G< 0ww__@ @/'G< @ww__@ @/@w_  @/@w_L /HH H' 'G'G< 0www___@ǀw_߰@A@ctw/`H]L@'4 0www___@ 'G< 0www___@ 'G< 0'wG_@/)@@@5w_ www?'_ 'Gߠ GۀH0GGۢ@<` @< '__\ Gס,0\@@/'GӀ HG'HGӀ HG'H _$`GH 'Gߠ GۀO_$ G'G<  'ww 'G?HGH '__ߐ@'G'G߀HG۠ $ HM'_` `  H_ ` $`_\`` ,0@3_t Gۧ4 _ H_] Gۡ,` 4$@4,0@@0 _ 4  H_@2Hw_w__\ Gۧ,0t@_\ Gۡ,0@t _ ` $`G'G< 0ww__@?@ w_ w'wG__@m'Gۀ HG۠ $ '_\ Gۡ,0@w_߰0ww__@G'G<  ww'__@'G〤H G뀠Gߠ@?,@'G۠ $ 'G'G< 0ww__@'G〤HHw_\ Gߡ,0@w_ www '۠ 'ߠ 4 _ H__\ Gߡ,0@_@'GۀHH Gߠ 'Gߧ4 _  OG'G< @w'_\ hE_ 4  H ' 4 _ H_\ G,0\@@.G 'G4 _  OGH ' 4 _ H_\ G,0@\ @-G 'G4 _  O_\ @-_t _$ _$ 0ww_\ hI_h! ' 4 _ H__\ G,0@\`\`G 'G4 _  O_ 4  H ' 4 _ H_\ G,0\@@-G 'G4 _  O_\ @-_t _$ _$ 0A@`www_h cP]_@@/ ' 4 _ HcX]_\ G,0@\`\`@/zG 'G4 _  O ww_` ` H$_  ,`@4  H_` $` H _  ,`@4 $_\`` ,0@1_t _ 4  H_@0ŐHw_w__\``,0u_ w_ 'Ӡ _$`_\ ӡ,0@w_ ww_w_w_\ _\ @@,ؒ'G< @A@aww_\``4c`]@ @-A0ww ' 4 _ H__\ G,0\@@,H _\ G,0@H wG 'G4 _  Ow_0ww__@ƒ 2h 'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@+w___ߐ@@+__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@+O_w_߰@w_w_ ww_@+w_@+w__׀h__ߢ@_ע$@_@@+"H H'Ӡ 'G'G< 0A@aTw''G,084084a] @ HG ?G@'G'G<  w'w@*" _G@@*w@*B H_L H __h_ס<`4h?H'__$` 'G<  w'w@*" _G@@*w@*B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@b www_\ h_M__\  hch]cp]@@*9 IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@)@@Ĕ@ww__\ @__\`\`@w_$ _$ _t 0ww_ (! @'__@ @@)_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@):/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@alww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@bTww_b@]@ @(<`?'G'G< 0ww__@ @("'G< @ww__@ @(@w_  @($@w_L /HH H' 'G'G< 0www___@w_߰@A@`$w/`H]L@'4 0www___@ %'G< 0www___@ -'G< 0'wG_@'U@@@.(w_@w_\ 0h _\ 0\ h H' 'G'G<  A@bwww'_h$_ 4  < H_\ h _\  1wHw_h _ 4  < O_h_hG뀤H_@&'cx]G_@@,_w_߰0A@`Įww_w_瀤h @_@ H.w_ 4  H _%!,@$_ 4  < Hc]__ 4  @@Hw_w_ ww__h_L '_ wG'G׀ >H__h_L '_ wG'Gπ >O__ hH3w_w__h_L '_ wG'G׀ H__h_L '_ wG'Gπ O__ hHw_ߐ@&N w_A@awwww_w__@w_ 4  H H'_%`,$`_瀤 @h?H'c]_@ @&H_ ϐ@ȒH_L / H?H'_ 0w_c]@'H?H'_ w__$@ @'}w_h__$@  h?H'__$@@&O __$@,_ wax]\ @@&FH @_t HAcH]\ @@&4H @H_t H/c]\ @@&"H r_t Hb ]\ @@&H _t H c]@@_t _c]@ꠒH?H6'_ w__$@ @&w_h?H$'__$@@*4_t _ w_c]@vH __@_t (H_t ( 'G<  A@aww_ 4  H H='_ ߐ@*w_瀤hc]_ @@(H#'G H_@%c]_ @@H'___ߐ@'_@%oG'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@$w___ߐ@@$__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@$E_w_߰@w_w_ ww_@$w_@$w__׀h__ߢ@_ע$@_@@$H H'Ӡ 'G'G< 0A@a,w''G,084084a] @ HG ?G@'G'G<  w'w@#" _G@@#ޔw@#B H_L H __h_ס<`4h?H'__$` 'G<  w'w@#" _G@@#w@#B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@awww_\ h_C__\  hc]c]@@#/ IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@"@@뺔@ww__\ @ے__\`\`롔@w_$ _$ _t 0ww_ (! @'__@ @@"_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@"0/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@ aDww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@ b,ww_b@]@ @!2`?'G'G< 0ww__@ @!'G< @ww__@ @ @w_  @!@w_L /HH H' 'G'G< 0www___@w_߰@A@ cw/`H]L@'4 0www___@ 'G< 0www___@ #'G< 0'wG_@ K@@@'w_@w A@ bDwwc]@!̐w_瀤h _c]@@ ȒH_c]@@ H _c]@@!H H}'_c]@@ H _c]@@!}H Hf'_@ h_L ,084084a] @ H_@#2HO'_H'_! @!!'G〤?Hcp]!`]@_B @ ,@@ H*' _$`GH$'cp]!`]_@@cp]!`]@@ߒcp]!`]@@Ւ 'G< wwwwwA@ cPww 'à9'GˀHH`c]@#א @w___ϐ@@'GǠ@ h&G @$ @sw___ϐ@@'GǠ@ h` ]@АGǐ@G__c ]@@-GÀHG@wwwwA@ a\www ''GÀHHjc]@#)ϐ @w_ߠ__ǐ@@b'G@ h&G@$ @w_ߠ__ǐ@@C'Gt@ h` ]@RG@_@;ϐ G__c ]@@-Z[GHG@:@'!?< @4 !? @4 !?# @,  `, '4  ww_@Lw___ߐ@@L__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@ _w_߰@w_w_ ww_@ܐw_@ؐw__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@ `Lw''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@n" _G@@vw@cB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@awww_\ h_ __\  h`(]`0]@@ IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@}@@傔@ww__\ @⣒__\`\`i@w_$ _$ _t 0ww_ (! @'__@ @@G_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@`dww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@aLww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@jw_߰@A@cw/`H]L@'4 0www___@ 'G< 0www___@ 'G< 0'wG_@@@@ w_ www_\ _t`_ !< @# H_  @@?? !'G! @H'_ !< @#(H!(H'_ !< @!$@#8@?!'< @'G'G'Gۡ4 _$`_\ w_߰0ww__$@$ 'G< 0ww_D _\`\`'G< @w_\ 0h _\ 0\ h H' 'G'G< A@c,www'w''G'!< @#H HF'_@O'GGˠ@<  P ?'ǐ @ݒwU_$ GGˠ@< Gס4 @_$ (_ H_G@@:_G@ H_Gˠ < @@._ ,_@^ap]_GӐ@O'Gϡ< ww'w''_w__G_Gא@@ s'Gˡ< ww'w''_w__G_Gא@@ ^s'Gˡ< w'w'w_׀h H'_@'_\ w_w_נ w_w_h_@'GGH*__G@@HH^GHGGH_GL@ /H _G$ < L@ /H HT'HB__G@@kHH6_G@wGG$@'GGHH'GGÀH_GL@ /HH!< @#HH__GÐ@@:HH H'_\ w_w_נ w_w_o 'Gӡ< 㾐A@awOww''ww_w_@=H?H '˦_\`\` @A@@H_G__ߐ@@$HHH HH?H'___G_G_ϐ@@s@ O OO!< @#HK__@'sG 4 s@4@=ww_w_G@@_wG@_s@@ /_wG4 s@4,_@M_wG4 s@ < G___ϒ@@es'_w@%GH?H{'Ho!< @#8Hh__@'cgG4 c@ 4g_G@Pg_c@Ig /_>wW_Wh`H]_I_w@@۔_WH`P]_7_w@@ɔ_W\ 8_w_o< G___ϐ@@s'gڐGH?H'H@@O  'Gˡ<  ww_\ w_\ w_ߦ`H`(< _פ`H`(< E'G< A@`ww'wwנ '_׀h1Gӥ4 ap] H(ap]\ Gӡ,0\@w_ (! @4 G4 H`8]wGӠ '_׀h Gӥ4 ap]  O_׀h`@]w_``]G__א@ @^s'_`8]hGπHGH'ap] @9ap]\ ap] `X]@ @} 'G< 0A@bdww_bw_瀤h @s}_@  H.w_ 4  H _%!,@$_ 4  < H`h]_ _ 4  i@@HHw_w_0www_ 4  H H'_%`,$`__t`__t`  'G<  A@`ww_ 4  H H8'_ ߐ@.w_瀤h`p]_ 躐@@ H'G H_@I`x]_ 覐@@ ֒H '___ߐ@'G< 0w_ w_瀤hH6w_ 4  < H_H+w_ 4  < H_\ 8wH_ 4  < H_\ wHHw_ 4  H_  ^O_@'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@cw''G,084084a] @ HG ?G@'G'G<  w'w@Z" _G@@Zw@OB H_L H __h_ס<`4h?H'__$` 'G<  w'w@"" _G@@*w@B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@cԮwww_\ h_ٿ__\  h`]`]@@ IH H' '__t` _\ _,@0w'_ ِ_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@1@@6@ww__\ @W__\`\`@w_$ _$ _t 0ww_ (! @'__@ @@_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@c4ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@`ww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @v@w_  @@w_L /HH H' 'G'G< 0www___@w_߰@A@aw/`H]L@'4 0www___@ ė'G< 0www___@ ğ'G< 0'wG_@ǐ@@@w_ www_\ _t`_ !< @# H_  @@?? !'G! @H'_ !< @#(H!(H'_ !< @!$@#8@?!'< @'G'G'Gۡ4 _$`_\ w_߰0ww__$@$ 'G< 0ww_D _\`\`'G<  ww '_L HH4w_L /'_ wG'G׀ H!怤 0H怤 7HHwߣ, $ 04 @'_L /'_ wG'Gπ O_$`_w_ A@a\wwww_瀤  h __$ L@H`]@_ې@w_߀h_L H`]@}_@Ɛ '__t`_$`_נ@_t`0www__t`__$` _瀤h ___@ A@c̮wwwww_h_cH]\ ߐ@͖w_׀h `]_⊐@@___ߐ@_w_@w_  @@ww_ _ `\@Ot A@bLww_\ w_\  w_ w__$@w__׀h`]@ǐ_w__נ$@w__t`__$` _߀h ___ߐ@0ww_ H H'_ _ `\@Ot_ 'G< 0A@`Įwww_@Ő'_  @@G뀤H _G$ < L@ /HG$ 'G뀠G @?,@'G_$` __t`_G@ _t`G뀤Ha]_t` www_\`\`'_D 'ר _GG۠@< ,_Gע@_\ Gې@@{G׀HHGנ$ 'צ /_G,@_ w_\`\`'_\ wGG۠$@'O_w_߰ w'w'GG〤HGH'G'G'__Gې@@>'GӀHGH'GG$@'Gߡ<  w'w'_G_G@Ė'GۀHGH#'Gۀ H H'GG〤H__G@@ H _GL@ /H H'ߠ?'Gߡ< www'_ H_kH)__@t_\ (w_h_\ _\ hH _\ w_o_hH_hO_瀤h_\ hHf_\ w_\`\`'H _(HUH_G_GӐ@c?O OO_ (`\@OtǀH3_@_\ w_\`\`'H _H H_G_GӐ@+?O OOǀO_Ӑ0ww_\ _\ h_H @w_\ _t`_\ (_t`__t`(0w_\ (w_瀤h_\ w_@ _w_瀤o'ww 'G,p,p@lwG 0@bwǠ 'GH_Gӧ,p,p_ǡ,p (`]OtGӠ 'GGOw ' 'GH_Gӥ,p,pw_Gӥ,p,p_@Ė GӠ 'GGO 'GH<_Gӥ,p,pw_\ hH&_\`\`'_h _\ wG'H_\ G_G@ H_\ wG'GӠ 'GGO_hC 'GH=_Gӥ,p,pw_Gӥ,p,p__G@]_\ hH_\`\`'_\ G_G@ӖH_GӠ 'GGOww 'GH/_Gӥ,p,p\ hH_ Gӡ,Pw_Gӥ,p,p !< @#H_ Gӡ,PwGӠ 'GGO_hH@_]`0G___א@@ŀ'G߀ HH,_Gߠ @w 'ߠ 'GH_ Gӡ,Ph_Gӫ-0-0@_ס-0@vGӠ 'GGOO_@ = 'GH_Gӥ,p,pGӠ 'GGO_@ $G'G< wwww_@ V'_ Ho__ߐ@w___ǐ@ؒ'GG׀HHR__GÐ@@ I'G HHDG HHCGG׀H __ǐ@ H8'_GL@ /HH)_ !< @#HH$Gà 'GG׀H __ǐ@ H'__Gâ@__ߐ@@H '_ O?'Gۡ< СA@`ܮwwwww_cH]\ Ǡ@w_πh?H('_L H _@@_@ c H'ۢ__ǐ@___ߐ@<'_@ LG'Gۡ< @'!?< @4 !? @4 !?# @,  `, '4  ww_@ `w___ߐ@@ `__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@ !_w_߰@w_w_ ww_@ w_@ w__׀h__ߢ@_ע$@_@@ H H'Ӡ 'G'G< 0A@`w''G,084084a] @ HG ?G@'G'G<  w'w@ " _G@@ w@ B H_L H __h_ס<`4h?H'__$` 'G<  w'w@ " _G@@ w@ wB H_L H __h_ס<`< h?H'__$` 'G< 0A@b<wwww__@@ cp]!`]_@@ _0A@awww`]__@ǔ@ 0A@a,www`]__@@ǐ 0A@`www`]__@0A@`Twww`]__@u@A@cww_c]t`0wwwwwA@cwww`]\ __@H@A@cww`]_@Ғ0wwwwwA@bwwwc]\ __@HwwwwwA@b ww@>B @wߠ 'נ '_L H:G׀ h6_GL@/OҢG,@O'G۠ 'Gנ 'G'Gˀ %HHG׀ h  %G,@Gנ 'HGנ$ 'H _GL@HG׀ o G,@`]_@$@wc]\ _@H wwwwwA@`8www`]\ __ߐ@?'G< 0wwwwwA@cwwwc]\ __@H@'!?< @4 !? @4 !?# @,  `, '4  ww_@Dw___ߐ@@D__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@Ԑw_@Аw__׀h__ߢ@_ע$@_@@ؔH H'Ӡ 'G'G< 0A@`,w''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@f" _G@@nw@[B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@`www_\ h___\  h`]`]@@ IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@u@@z@ww__\ @˛__\`\`a0'w' '_D 4  H _G,0@D 4 H H6'GG〤H/GG@<` @< '_Gߡ,0@D 4 HGߠ 'H_Gߡ,0D@4 HGߠ$ 'H H 'GG〤O 'G< 0A@aTw' H H' H   H  H?H'`]@ zH H'#a Hz#a_Hq#c)Hk#c*He# b H#)`πH# `?HS#+ H#5cHH#>a H#>bH<#?b0 H#?boH0#?c H#?c`H$#?c H#?c怤H# H#cH # H #cH H' 'G 'G< ww_\ w_h_\ Hw#w_w_π hH_ H _ '۠ wH_ H _π  h_  H_ HH_ ` ,  ` ?'۠ wH_ HJ_π  h4_  H._  H(_ H_  H_ H_  H_ H_  H _  HH^_ ` , ` ?,  ` ?'۠ wHS_ HH_π  h._  H(_  H"_  H_ H_  H_ H_  H_ HH_ ` ,  ` ?,  ` ?,  ` ?'۠ wH_t  H'_\_Ǡ@t_h__Ǡ$@_t`'4 0ww__@'_\ h H'2'G< 0w '_w_h_@ H'_L HGH 'Gƒ@'OG< @w_L H&_L H _L H_L H_ wH _h H '_L O 'G< 0w'w__@_\`\`G_@@_\_@Ē0ww''G뀤 H '_L H&_X w_L H_ߠ w_G@ ___ߠ$@q_wG'_L O A@bܮww_w_L '_ wG'G߀ HHIw_L '_ wG'G߀ [HH;w_L ,084084a] @  H_L ;H_ w_L ,084084a] @  O_L ;O_L '_ wG'G߀ mHHw__$@w_A@a<www''' '_w_\ wG〤 H__GG@? H'_wG'G'wGۀ HG۠ '_w_ew_h__@w_Yw_o_L /OH O,084084a] @ HGG〤H_he_wOH __hGH'_h_wH_Gې@ ___$@sOHGH|'_wO HGנ 'H'O H#_ w_L H_ H_L ,084084a] @  HH_ Gנ '_ wH_ _L,084084a] @   2H < w_wwG'G'ORGӀHGנ͒@'_h  '_w__@O(HGנ '_ wO.Gߡ< 0A@`ww_h H'_`]@@WH _`]@@MH H' 'G< www_瀤hHdw__@@w_׀?hHXw_@w_w_Ǡ w_@&w_w_w_פϨǐ@@w_?h+@B H _@"_@ߐH)w__$@w__ϡ,0@ w__@@w__@w__$@$ wH _,`HO_@_w_߰@'!?< @4 !? @4 !?# @,  `, '4  ww_@w___ߐ@@__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@ϔ_w_߰@w_w_ ww_@w_@w__׀h__ߢ@_ע$@_@@H H'Ӡ 'G'G< 0A@cTw''G,084084a] @ HG ?G@'G'G<  w'w@h" _G@@hw@]B H_L H __h_ס<`4h?H'__$` 'G<  w'w@0" _G@@8w@%B H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@` www_\ h_͒__\  h`]a]@@ IH H' '__t` _\ _,@0w'_ Ğ_ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@?@@D@ww__\ @e__\`\`+@w_$ _$ _t 0ww_ (! @'__@ @@ _ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@clww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@`Tww_b@]@ @`?'G'G< 0ww__@ @'G< @ww__@ @@w_  @@w_L /HH H' 'G'G< 0www___@,w_߰@A@b$w/`H]L@'4 0www___@ 'G< 0www___@ 'G< 0'wG_@Ր@@@w_@w_~?0A@`\wwcx]\ w_cx]t`_w_0A@cww_@w_瀤hcx]\ _@ @_@w_瀤ha]%_w_0A@cww_@rw_瀤h _h @fw_瀤h#cx]\ _@_@Vw_瀤h _h @Jw_瀤ha]ܐ_w_0A@aww_`ha]_ w __,@_w_0ww_֐__@@w_ ww__@ @w_߀h__$@Hw_w__א@Ғw_ A@`lwww__@@Ւw_߀h _h_ @w_߀h'cx]\ _@__@@w_߀h _h_ @w_߀ha ])_w_ A@c$www__@@{w_߀h_h_h  @jw_߀h,cx]\ __L@__@@Vw_߀h_h_h  @Ew_߀ha(]ϐ_w_A@awww'''w__GGG_א@@@}w_ǀ?h$_hH w_G@|__GGG_א@@@`w_ǀ?ha0]_w_ϰ 'wwG__@@w_׀ h@B H@B HO_Hw_߰ 'wwG__@@w_׀ h@B H@B HO_Hw_߰'ww_ww_瀤 h*G__@w_ǀ h_πh_Hw_w_Hw__Ǡ$@w__Ǡ@w__Ǡ@w_瀤 o_w_߰'ww_ww_瀤 h+G__@w_ǀ h?Hw_ǀh@* " ?Hw__Ǡ$@w__Ǡ@w__Ǡ@w_瀤 o_w_߰0A@a,w'G@x'G Ha8]G'G<  A@`w'wG_@@w_߀ha@]_w_0A@`ww_@"'G HaH]fG'G< 0A@cww'_G@e'G뀤 HaH]AG'G< 0A@bwwHHBaP]wH&aX]wH a`]wH_ah]@@ٔ p?H?OOO OO_\ 0h_\ 0Hwax]wap]__@ǔ0A@aww_@ H_\ 0h_\ 0Hwa]wa]_@9 A@aww'_G@@'Ha]a]H$wa]Hwa]Hwa]HwGH*'G$? O, < #a_ϠD_\ 0h_\ 0Hwax]wa]__א@ޔG'G< <dxP A@cdwwww!$'__a]_@@@&_Gې@I'G߀ HGH'__a]_@@@ _r_Gې@'G<  A@b<wwww__a]x_6@@@ۘ_%@֔!'G߀ HGH '_rc_%@Ɣ!'G<  A@aLwww'G뀤 H@B 'G〤 Ha]_@_B @L@D@G" G'G< 0A@`|wwa]_@1@@'G< 0A@`wwa]_@K@@'G< 0'w!< @#8H_אH'_'G'G< @'!?< @4 !? @4 !?# @,  `, '4  ww_@<w___ߐ@@<__ߢ@@? @w_w_0w'_L H_L GH_ w_L H_L GO_w_0www___@@_w_߰@w_w_ ww_@̐w_@Ȑw__׀h__ߢ@_ע$@_@@ДH H'Ӡ 'G'G< 0A@` w''G,084084a] @ HG ?G@'G'G<  w'w@" _G@@w@B H_L H __h_ס<`4h?H'__$` 'G<  w'w@^" _G@@fw@SB H_L H __h_ס<`< h?H'__$` 'G<  ww_ׂ `\@Ot__ `\@Ot_ `\@Ot0w_\ h _\`\`$$ Hww_w_0A@`Įwww_\ h___\  ha]a]@@ IH H' '__t` _\ _,@0w'_ _ w_\ w_ߠ _t`G_\ _,@ _\`\`-@ww__@m@@r@ww__\ @__\`\`Y@w_$ _$ _t 0ww_ (! @'__@ @@7_ (! ,@@$@w'_/ h/w_, 4@'4 0w_ ( /4w_瀤/h_H w_! G@@/w_@'!< @#(H!(H'!< @#H !< @#8H!8H' @@?? !'G! @'4 0A@߂`$ww'a]D H!< @# H_h_ !< @#(H_ H('a]D H!< @# H_h_ !< @# H_ H '!H''4 @w_ '!< @# H H '!< @#H !< @#8H H'!< @#(H H' H'G< @w_ (!@$@'!< @#H H'!< @!8$@@??  'G'G'G< 0A@