+.config/yt-dlp.conf master
authorJan Kratochvil <jan@jankratochvil.net>
Fri, 19 Apr 2024 13:39:16 +0000 (21:39 +0800)
committerJan Kratochvil <jan@jankratochvil.net>
Fri, 19 Apr 2024 13:39:16 +0000 (21:39 +0800)
54 files changed:
.bashrc
.config/youtube-dl/config
.config/yt-dlp.conf [new file with mode: 0644]
.cvsignore [deleted file]
.gdbinit
.gitattributes [deleted file]
.gitconfig
.gnupg/gpg-agent.conf [new file with mode: 0644]
.muttrc
.muttrc.addons
.ssh/.cvsignore [deleted file]
.ssh/config
.vimrc
bin/.cvsignore [deleted file]
bin/ccache-build [new file with mode: 0755]
bin/cherrycompare [new file with mode: 0755]
bin/cherrydownload [new file with mode: 0755]
bin/cherryfilter [new file with mode: 0755]
bin/errs12
bin/exx
bin/git-bisect-status [new file with mode: 0755]
bin/git-show-ls [new file with mode: 0755]
bin/heat [new file with mode: 0755]
bin/hog [new file with mode: 0755]
bin/java_error-findaddr [new file with mode: 0755]
bin/jtreg-pass-fail [new file with mode: 0755]
bin/jtreg-pass-fail2 [new file with mode: 0755]
bin/makeinfo [deleted file]
bin/nn [new file with mode: 0755]
bin/openjdk-find-ccache-bug [new file with mode: 0755]
bin/psu [new file with mode: 0755]
bin/rpmmerge
bin/shutdown-detect [new file with mode: 0755]
bin/shutdown-detect-boot [new file with mode: 0755]
bin/shutdown-detect.service [new file with mode: 0644]
bin/taillog
bin/upsc-log [new file with mode: 0755]
bin/upsc-log-diff [new file with mode: 0755]
bin/usbrelay [new file with mode: 0755]
bin/vi-git-both-modified [new file with mode: 0755]
bin/wcsrc [new file with mode: 0755]
src/.cvsignore [deleted file]
src/Makefile
src/rpm/.cvsignore [deleted file]
src/rpm/SOURCES/.cvsignore [deleted file]
src/safeio.C [new file with mode: 0644]
src/safeio.h [new file with mode: 0644]
src/socket.C [new file with mode: 0644]
src/socket.h [new file with mode: 0644]
src/streamfer-client.C [new file with mode: 0644]
src/streamfer-server.C [new file with mode: 0644]
src/streamfer.C [new file with mode: 0644]
src/streamfer.h [new file with mode: 0644]
src/stringf.h [new file with mode: 0644]

diff --git a/.bashrc b/.bashrc
index d2f99d3..82e5139 100644 (file)
--- a/.bashrc
+++ b/.bashrc
@@ -128,8 +128,13 @@ unalias 2>/dev/null fgrep  # /etc/profile.d/colorgrep.sh
 # `eval':
 eval '
        function grep {(unset grep; ( ulimitme; grep "$@"; ); );}
-       function ls {(unset ls; ( ls -bF "$@"; ); );}
-       function l { ls -lbF "$@"; }
+       if ls -d -bF / &>/dev/null;then
+               function ls {(unset ls; ( ls -bF "$@"; ); );}
+       else
+               # Alpine Linux
+               function ls {(unset ls; ( ls "$@"; ); );}
+       fi
+       function l { ls -l "$@"; }
        if which vim &>/dev/null;then
                alias vi="vim"
        else
@@ -198,7 +203,7 @@ if [ -n "$PS1" ];then       # set only in interactive sessions
                export PS1='[bash]${LOGNAME}@MOCK-'"`cat /MOCK`"':${PWD}# '
        fi
        export PS1='\[\017\]'"$PS1"     # \017=std charmap
-       if [ "${SHELL#*/com.termux/}" = "$SHELL" ];then
+       if [ "${SHELL#*/com.termux/}" = "$SHELL" -a "${SSH_CLIENT#2a02:2b88:6:3b57::8f }" = "$SSH_CLIENT" ];then
                export PS1='\[\033%G\]'"$PS1"   # \033%G=utf-8
        fi
        # Do not: kbd_mode -u # utf-8
@@ -209,12 +214,16 @@ export HISTSIZE=10000000
 export HISTFILESIZE="$HISTSIZE"
 export GDBHISTFILE="$HOME/.gdb_history"
 export CVS_RSH="ssh"
-export TZ="Europe/Prague"
-#export TZ="Canada/Eastern"
+export TZ="Asia/Manila"
+#export TZ="Europe/Prague"
+if [ "$TZ" = Asia/Manila ];then
+  alias date='TZ=Asia/Manila date;TZ=Europe/Prague date'
+fi
 export PYTHONUNBUFFERED=1
 export ASAN_OPTIONS=detect_leaks=0
 export GTK_OVERLAY_SCROLLING=0
 export MAKEFLAGS=
+export DEBUGINFOD_URLS=
 cpus="`getconf _NPROCESSORS_ONLN`"
 if [ -n "$cpus" ];then
        # *3/2 was OOM for host1 32 CPUs + 16GB RAM
@@ -258,9 +267,13 @@ export ULIMITME_OPTS=""
 for i in "-v 2000000";do
        (ulimit $i 2>/dev/null) && ULIMITME_OPTS="$ULIMITME_OPTS $i"
        done
+if [ -z "$ULIMITME_OPTS" ];then
+       # MinGW64 would be printing "unlimited" on each ulimitme() execution.
+       ULIMITME_OPTS="-v unlimited"
+fi
 # Prevent: ulimit:  -m 100000 -d 150000 -v 200000: invalid number
 # by forcing our known $IFS value; eval(1) is needed to take $IFS to effect:
-function ulimitme { IFS=" " eval "ulimit $ULIMITME_OPTS"; }
+function ulimitme { IFS=" " eval "ulimit 2>/dev/null $ULIMITME_OPTS"; }
 function finame { find . -false `
        awk </dev/null 'BEGIN{ for (i=1;i<ARGC;i++) print " -o -iname *"ARGV[i]"*"; }' "$@"
        `; }
@@ -283,14 +296,14 @@ function ctags {(unset ctags
                                                -o -name "*.[chCy]" \
                                                -o -name "*.cc"    \
                                                -o -name "*.cxx"   \
-                                               -o -name "*.cpp"   \
+                                               -o -name "*.[ch]pp"   \
                                                -o -name "*.[Ss]"  \
                                                -o -name "*.java"  \
                                                -o -name "*.p[lm]" \
                                                -o -name "*.py" \
                                                -o -name "*.exp" \
                                                ')' \
-                               |ctags --file-tags -L - --langmap=Tcl:+.exp "$@"
+                               |ctags --extras=+f -L - --langmap=Tcl:+.exp "$@"
        fi; );}
 function ctagsh { ctags "--c-types=+px"; }
 function cpan {(unset cpan; if [ $# = 0 ];then cpan;else perl -MCPAN -e "install qw($*);";fi; );}
index 5868515..6d09993 100644 (file)
@@ -1 +1 @@
---restrict-filenames -f bestvideo[ext=webm]+bestaudio[ext=webm]/bestvideo+bestaudio/best
+--restrict-filenames -f bestvideo[vcodec^=av01][height=2160]+bestaudio[acodec^=opus]/bestvideo[ext=webm][height=2160]+bestaudio[ext=webm]/bestvideo[height=2160]+bestaudio/best[height=2160]/bestvideo[vcodec^=av01][height=1440]+bestaudio[acodec^=opus]/bestvideo[ext=webm][height=1440]+bestaudio[ext=webm]/bestvideo[height=1440]+bestaudio/best[height=1440]/bestvideo[vcodec^=av01][height=1080]+bestaudio[acodec^=opus]/bestvideo[ext=webm][height=1080]+bestaudio[ext=webm]/bestvideo[height=1080]+bestaudio/best[height=1080]/bestvideo[vcodec^=av01][height<=?2160]+bestaudio[acodec^=opus]/bestvideo[ext=webm][height<=?2160]+bestaudio[ext=webm]/bestvideo[height<=?2160]+bestaudio/best[height<=?2160]
diff --git a/.config/yt-dlp.conf b/.config/yt-dlp.conf
new file mode 100644 (file)
index 0000000..ce0622d
--- /dev/null
@@ -0,0 +1 @@
+--restrict-filenames
diff --git a/.cvsignore b/.cvsignore
deleted file mode 100644 (file)
index 34c7202..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-.lynxrc
-.procmailrc
-.esd_auth
-cgw
-.qt
-.ccache
-.koji
-.pki
-.Xauthority
-.openoffice.org
-.gconfd
-.bash_history
-.mozilla
-pserver
-.recently-used.xbel
-.lesshst
-.systemtap
-.perlmail-submit.log
-.fp
-.perlmail.log
-.cache
-.fetchmail.pid
-.lftp
-.ocp
-.emacs.d
-netpbm
-.fetchmailrc
-.gdb_history
-centrum
-.eclipse
-.spamassassin
-.aspell.en.pws
-.fedora-server-ca.cert
-.rnd
-.bashrc.local
-.python.valgrind
-.pgpkey
-include
-.gnome2_private
-priv
-.Xdefaults
-.local
-.muttrc.9000
-.hammock-result
-.kde
-.gitk
-.ssh2
-.netbeans
-.xine
-secure
-.wine
-.mysql_history
-.forward
-energie-WWW
-.lynx_cookies
-.wgetrc
-.cvspass
-.cpan
-.swo
-.aspell.en.prepl
-.swp
-.gstreamer-0.10
-.netrwhist
-.fontconfig
-Mail
-.fedora-upload-ca.cert
-d
-.gnupg
-.muttrc.priv
-.dbus
-.phorum
-.gconf
-.fedora
-.fedora.cert
-.viminfo
-.thumbnails
-.=.swp
-.gnome2
-lib
-staticnss
-.gvfs
-.ssh-redhat-last
-viewcvs
-.Scilab
-.octave_hist
-.texlive2007
-.config
index f31f82f..8a01500 100644 (file)
--- a/.gdbinit
+++ b/.gdbinit
@@ -6,13 +6,22 @@ set style enabled off
 set history save on
 # https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=106814
 set complaints 0
-set record insn-number-max 10000000
+set record full insn-number-max 10000000
+define u
+       up
+end
+document u
+up
+end
 define javadump
        call _Jv_DeepDebug($arg0)
 end
 document javadump
 call _Jv_DeepDebug($arg0)
 end
+define pjstr
+       p *$arg0._body@$arg0._length
+end
 define pglist
        if (*("$arg1")=='@')
                set var $arg1=struct $arg1
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644 (file)
index d4e37a2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ChangeLog merge=git-merge-changelog
index 5ea696d..dfa6b16 100644 (file)
@@ -1,21 +1,20 @@
 [user]
        name = Jan Kratochvil
-       email = jan.kratochvil@redhat.com
+# use: .gitconfig.local
+#      email = jan@jankratochvil.net
+[include]
+       path = .gitconfig.local
+[init]
+       defaultBranch = master
 [merge]
        conflictstyle = diff3
+       renamelimit = 10000
 [color]
        ui = never
 [pull]
        ff = only
 #      rebase = true
 
-# ChangeLog merge attempt:
-#[merge "git-merge-changelog"]
-#      name = git-merge-changelog
-#      driver = /usr/bin/git-merge-changelog %O %A %B
-# ChangeLog merge attempt:
-#[core]
-#      attributesfile = ~/.gitattributes
 # golang:
 #[http]
 #      cookiefile = /home/jkratoch/.gitcookies
 [branch]
        sort = committerdate
 # FIXME: It does not work:
-[blame]
-        ignoreRevsFile = ~/.gitconfig-ignoreblamecommit
+#[blame]
+#        ignoreRevsFile = ~/.gitconfig-ignoreblamecommit
 [format]
        signature =
+
+# Big files cludge:
+#[core]
+#      compression=1
+#      bigFileThreshold=100g
+#[pack]
+#      depth=2
+#      window=2
+#      threads=1
diff --git a/.gnupg/gpg-agent.conf b/.gnupg/gpg-agent.conf
new file mode 100644 (file)
index 0000000..be1125e
--- /dev/null
@@ -0,0 +1 @@
+pinentry-timeout 1000000000
diff --git a/.muttrc b/.muttrc
index 9f32024..09b4501 100644 (file)
--- a/.muttrc
+++ b/.muttrc
@@ -1,15 +1,19 @@
 source ~/.muttrc.addons
 source ~/.muttrc.9000
+set from="jan@jankratochvil.net"
 source ~/.muttrc.priv
 
-set from="jan@jankratochvil.net"
 set envelope_from
-set query_command="${HOME}/bin/mutt_ldap_query '%s'"
 
 alternates "^(.*@(.*\\.)?jankratochvil\\.net|short(\\+[^@]*)?@(ucw\\.cz|atrey\\.karlin\\.mff\\.cuni\\.cz|k332\\.feld\\.cvut\\.cz|(alcor|amiga)\\.ericsson\\.cz|short\\.mj\\.gts\\.cz|short\\.short|(.*\\.)?(vellum|valley)\\.cz)|.*@((.*\\.)?short\\.ucw\\.cz|dialup\\.netbeans\\.com|kratochvil\\.suse\\.cz)|kratochvil(\\+[^@]*)?@(users\\.sourceforge\\.net|(.*\\.)?suse\\.cz)|lace(\\+[^@]*)?@valinux\\.co\\.jp|(jan\\.kratochvil|jkratoch)(\\+[^@]*)?@redhat\\.com|jkratoch@sourceware.org)$"
 
+folder-hook "=jklabs" set from=jan.kratochvil@jklabs.cz
+folder-hook "=penzion" set from=info@penzionspicak.cz
+#folder-hook "=penzion" set askcc=true
+
 alias gdb gdb@sourceware.org
 alias gdbpat gdb-patches@sourceware.org
 alias archer archer@sourceware.org
 alias binutils binutils@sourceware.org
 alias gccpat gcc-patches@gcc.gnu.org
+alias steph Marie Stephanie Kratochvilova <istephielicious@gmail.com>
index 4f9a813..7205f18 100644 (file)
@@ -20,7 +20,7 @@ bind pager <Esc>\` previous-unread
 unset strict_threads
 # tw=78 is for gq and RFC 2822 section 2.1.1.
 set editor="vim -c 'set noai' -c 'set ts=8' -c 'set tw=78'"
-set send_charset="us-ascii:iso-2022-jp:utf-8"
+set send_charset="us-ascii:utf-8"
 set charset="utf-8"
 set autoedit
 set auto_tag
@@ -60,13 +60,21 @@ macro index <Esc>m "l~N|~O\n"
 macro pager <Esc>f ":set pager_index_lines=0\n
 macro pager <Esc>g ":set pager_index_lines=10\n"
 set user_agent
+set abort_noattach=ask-yes
 
+# https://unix.stackexchange.com/a/42714/296319
+auto_view text/html                                   # view html automatically
+alternative_order text/plain text/enriched text/html  # save html for last
+bind attach <return>    view-mailcap
 
 # Mail command
 ##############
 bind index \ca mail
 macro index M ":set dsn_return=hdrs\n:set dsn_notify=failure,delay,success\n^a" "DSN compose of a new mail message"
 macro index m ":unset dsn_return dsn_notify\n^a" "Standard compose of a new mail message"
+bind index \cb reply
+macro index R ":set dsn_return=hdrs\n:set dsn_notify=failure,delay,success\n^b" "DSN compose of a reply"
+macro index r ":unset dsn_return dsn_notify\n^b" "Standard compose of a reply"
 # quote 'macro' argument to prevent: macro: too many arguments
 # `spamassassin {--report|--revoke}' calls `sa-learn {--spam|--ham}' automatically.
 set pipe_split
@@ -124,7 +132,7 @@ color status brightyellow blue
 color tree brightmagenta black  # the thread tree in the index menu
 color tilde brightmagenta black
 color message brightcyan black
-color normal white black
+color normal brightwhite black
 color attachment brightmagenta black
 color search black green  # how to hilite search patterns in the pager
 color body brightyellow black "(ftp|http)://[^ ]+"  # point out URLs
diff --git a/.ssh/.cvsignore b/.ssh/.cvsignore
deleted file mode 100644 (file)
index 6f21215..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-random_seed
-identity
-identity.pub
index 18fde99..e7d34ff 100644 (file)
-IdentityFile ~/.ssh/id_dsa
 IdentityFile ~/.ssh/id_rsa
+IdentityFile ~/.ssh/id_dsa
+IdentityFile ~/.ssh/id_rsa-old
 IdentityFile ~/.ssh/identity
 IdentityFile ~/.ssh/koji-id_rsa
 IdentityFile ~/.ssh/id_dsa-sourceware
+IdentityFile ~/.ssh/id_ed25519
 
 Host localhost
 #Cipher none
 Compression no
 
-Host sebastian.redhat.com ovpn-phx2.redhat.com 209.132.183.3
-ControlMaster auto
-ControlPath /tmp/ssh-%h-%p-%r.sock
-# Required for: http://intranet.corp.redhat.com/ic/intranet/LWNReminder.html
-# =squid.rdu.redhat.com=file.rdu.redhat.com
-LocalForward 3228 squid.redhat.com:8080
-# Looks to be stucking (=squid.redhat.com=10.11.255.147):
-LocalForward 3328 squid.corp.redhat.com:3128
-LocalForward 3428 squid.brq.redhat.com:3128
-LocalForward 3528 squid.bos.redhat.com:8080
-LocalForward 3025 smtp.corp.redhat.com:25
-#LocalForward 3025 mail.stuttgart.redhat.com:25
-#LocalForward 3993 pobox.devel.redhat.com:993
-#LocalForward 3983 pobox.stuttgart.redhat.com:993
-LocalForward 3973 mail.corp.redhat.com:993
-# Now being gatewayed: LocalForward 6668 irc.freenode.net:6667
-LocalForward 6668 barstool.build.redhat.com:6688
-# http://intranet.corp.redhat.com/ic/intranet/IRCServerList.html
-# devserv.devel.redhat.com is obsolete? "administratively prohibited" now.
-LocalForward 6670 devserv.devel.redhat.com:6667
-LocalForward 6671 porky.stuttgart.redhat.com:6667
-LocalForward 6672 irc.devel.redhat.com:6667
-LocalForward 6673 irc-2.devel.redhat.com:6667
-LocalForward 6674 irc.yyz.redhat.com:6667
-#LocalForward 3389 ldap.stuttgart.redhat.com:389
-LocalForward 3389 ldap.str.redhat.com:389
-LocalForward 3046 curly.devel.redhat.com:4046
-LocalForward 3047 curly.devel.redhat.com:2049
-LocalForward 3234 engarchive.rdu.redhat.com:1234
-LocalForward 3235 engarchive.rdu.redhat.com:2049
-# RHTS:
-LocalForward 3290 rhts.redhat.com:80
-LocalForward 3293 rhts.redhat.com:443
-# www.farm.hsv.redhat.com - http://farmer.farm.hsv.redhat.com/
-LocalForward 3291 www.farm.hsv.redhat.com:80
-# /tmp/tts-fill.sh
-LocalForward 3292 tts.englab.brq.redhat.com:443
-# monotone: elfutils
-LocalForward 4691 ups.hsv.redhat.com:4691
-# Brew is too slow remotely:
-# http://brewhub.devel.redhat.com/brewhub
-#LocalForward 3280 brewhub.devel.redhat.com:80
-#LocalForward 3088 kerberos.corp.redhat.com:88
-# Using RHTS repository instead:
-#LocalForward 3180 devserv.devel.redhat.com:3180
-# [redhat.com #915466]
-LocalForward 3190 calendar.corp.redhat.com:443
-LocalForward 3196 mail06.corp.redhat.com:443
-# redhat.com XMPP
-LocalForward 3222 chat.corp.redhat.com:5222
-# brq.redhat.com XMPP
-LocalForward 3223 services.brq.redhat.com:5222
-# cvs.devel.redhat.com pserver+ssh
-LocalForward 3401 cvs.devel.redhat.com:2401
-LocalForward 3422 cvs.devel.redhat.com:22
-GatewayPorts yes
-# Kerberos
-LocalForward 3749 kerberos.corp.redhat.com:749
-LocalForward 3880 kerberos.rdu.redhat.com:88
-LocalForward 3881 kerberos.bos.redhat.com:88
-LocalForward 3882 kerberos.hsv.redhat.com:88
-LocalForward 3883 genbu.tokyo.redhat.com:88
-LocalForward 3884 kerberos.stuttgart.redhat.com:88
-LocalForward 3885 kerberos.bne.redhat.com:88
-LocalForward 3886 kerberos.iad.redhat.com:88
-
-ProxyCommand none
-StrictHostKeyChecking yes
-Host rawhide64.englab.brq.redhat.com shell.lab.bos.redhat.com
+Host rawhide64.englab.brq.redhat.com shell.lab.bos.redhat.com liver2.lab.eng.brq.redhat.com *.s390.bos.redhat.com tools-qe-01.lab.eng.brq.redhat.com
 User jkratoch
-Host tofu.yyz.redhat.com
-User hudson
+#Host tofu.yyz.redhat.com
+#User hudson
 Host power05.str.redhat.com
 User root
 Port 30
-Host *.test.redhat.com *.lab.boston.redhat.com *.rhts.boston.redhat.com *.lab.bos.redhat.com *.eng.bos.redhat.com *.rhts.bos.redhat.com *.z900.redhat.com *.cambridge.redhat.com *.gsslab.rdu.redhat.com prdell.brq.redhat.com *.englab.brq.redhat.com *.eng.brq.redhat.com *.rhts.eng.rdu.redhat.com *.rhts.eng.nay.redhat.com *.s390.bos.redhat.com dhcp45.install.bos.redhat.com
+Host *.test.redhat.com *.lab.boston.redhat.com *.rhts.boston.redhat.com *.lab.bos.redhat.com *.eng.bos.redhat.com *.rhts.bos.redhat.com *.z900.redhat.com *.cambridge.redhat.com *.gsslab.rdu.redhat.com *.gsslab.rdu2.redhat.com prdell.brq.redhat.com *.englab.brq.redhat.com *.eng.brq.redhat.com *.rhts.eng.rdu.redhat.com *.rhts.eng.nay.redhat.com *.s390.bos.redhat.com dhcp45.install.bos.redhat.com *.lab.eng.rdu.redhat.com *.rhts.gsslab.pek.redhat.com *.ss.eng.rdu.redhat.com *.rhts.eng.*.redhat.com *.lab.eng.rdu2.redhat.com
 User root
 UserKnownHostsFile /dev/null
-Host cvs.fedora.redhat.com sources.redhat.com ovpn-phx2.redhat.com 209.132.183.3
-ProxyCommand none
-StrictHostKeyChecking yes
+AddressFamily inet
 Host ovpn-phx2.redhat.com 209.132.183.3
 Port 330
 User jkratoch
-Host *.redhat.com porkchop devserv barstool 192.168.79.133 shell shell.lab.bos.redhat.com
+Host *.redhat.com porkchop devserv barstool 192.168.79.133 shell shell.lab.bos.redhat.com pkgs.devel.redhat.com
 User jkratoch
-#ProxyCommand /usr/bin/ssh sebastian.redhat.com exec nc %h %p
-#ProxyCommand /usr/bin/ssh ovpn-phx2.redhat.com exec nc %h %p
-#ProxyCommand /usr/bin/ssh 209.132.183.3 exec nc %h %p
-#ProxyCommand /usr/bin/ssh host0 /usr/bin/ssh 209.132.183.3 exec nc %h %p
-###bastion:
-###ProxyCommand /usr/bin/ssh -W %h:%p 209.132.183.3
-# iptables -t nat -A OUTPUT -p tcp -d 10.10.36.184 --dport 6667 -j DNAT --to-destination 127.0.0.1:6671
-# iptables -t nat -A OUTPUT -p tcp -d 10.4.122.10  --dport 25   -j DNAT --to-destination 127.0.0.1:3025
 StrictHostKeyChecking no
 NumberOfPasswordPrompts 6
-Host shell
-HostName shell.lab.bos.redhat.com
-HostKeyAlias shell.lab.bos.redhat.com
-Host porkchop
-HostName porkchop.devel.redhat.com
-HostKeyAlias porkchop.devel.redhat.com
-Host devserv
-HostName devserv.devel.redhat.com
-HostKeyAlias devserv.devel.redhat.com
-Host barstool
-HostName barstool.build.redhat.com
-HostKeyAlias barstool.build.redhat.com
-#Host cvsdevelrh
-#HostName 192.168.67.2
-#User jkratoch
-#Port 3422
-#HostKeyAlias cvs.devel.redhat.com
-Host git.fedorahosted.org fedorahosted.org
+Host git.fedorahosted.org fedorahosted.org *.fsffrance.org
 User jankratochvil
-Host *.fsffrance.org
+# https://fedoraproject.org/wiki/Test_Machine_Resources_For_Package_Maintainers
+Host pkgs.fedoraproject.org *.fedorainfracloud.org *.cloud.fedoraproject.org
 User jankratochvil
 
 Host virt
 # 172.1[6-9].* 172.2[0-9].* 172.3[0-1].*
-User root
+#User root
 UserKnownHostsFile /dev/null
 StrictHostKeyChecking no
 Compression no
 HostName 172.20.0.2
 
-Host bombadil.infradead.org
-User lace
+Host mac
+HostName mac10
+Host mac mac10
+User macbook
+Host virtmac macvirt
+HostName virt
+User macbook
+UserKnownHostsFile /dev/null
+StrictHostKeyChecking no
+Compression no
+HostName 172.20.0.2
 
-Host nokia6
-User root
+host rh-power-vm17.fit.vutbr.cz
+User jankratochvil
 
-Host hotelgate hotelgate.hotelsevendays.cz
-User root
-Host hotelgate
-HostName hotelgate.hotelsevendays.cz
-HostKeyAlias hotelgate.hotelsevendays.cz
+Host gitlab.gnome.org
+User jankratochvil
 
-Host gw.jklabs.cz
+Host bombadil.infradead.org
 User lace
 
-Host tercie.tone.cz
-User short
+Host gw.jklabs.cz
+User root
 
 Host fencepost.gnu.org
 User jankratochvil
@@ -177,25 +91,82 @@ HostName manon.vellum.cz
 HostKeyAlias manon.vellum.cz
 Host dolly.vellum.cz
 User short
-Host rallye.panda.cz
-User short
 
 Host atrey.karlin.mff.cuni.cz
 User short
 
-Host deda
+Host jklabs-sh-server jklabs-sh-server.jankratochvil.net
+User root
+
+Host download-stock
+HostName vps2.jankratochvil.net
+HostKeyAlias vps2.jankratochvil.net
+
+Host cosmo8 cosmo8.jankratochvil.net cosmo8t.jankratochvil.net p20
+Port 8022
+
+# Cosmo:
+Match localuser u0_a184 originalhost vps2
+Hostname vps2.jankratochvil.net
+User lace
+Match localuser u0_a184 originalhost host1
+Hostname host1.jankratochvil.net
+Match localuser u0_a184 originalhost host1,host1vps2
+User lacemail
+Compression yes
+Match localuser u0_a184 originalhost pi
+Hostname pi.jankratochvil.net
 User root
 
-Host jklabs-server
+Host host1 host1s host14
+Compression no
+Host host1vps2
+HostName vps2-ipv4.jankratochvil.net
+Port 122
+Host host14 host1vps2
+HostName host1
+HostKeyAlias host1
+
+Host host2
+ControlMaster auto
+ControlPath /tmp/ssh-%h-%p-%r.sock
+ControlPersist 60
+
+# OpenWrt
+# https://bugzilla.redhat.com/show_bug.cgi?id=1881301#c38
+Host 192.168.66.13 ap13
+#PubkeyAcceptedKeyTypes ssh-rsa
 User root
 
+Host dev? dev?.azulsystems.com
+User jkratochvil
+Host dev1 dev1.azulsystems.com dev2 dev2.azulsystems.com
+#PubkeyAcceptedKeyTypes ssh-rsa
+PubkeyAcceptedKeyTypes +ssh-rsa,ssh-dss
+HostKeyAlgorithms=+ssh-dss
+Host dev1
+HostName dev1.azulsystems.com
+Host dev2
+HostName dev2.azulsystems.com
+Host dev3
+HostName dev3.azulsystems.com
+Host rpi4-05
+HostName rpi4-05.azulsystems.com
+Host rpi4-06
+HostName rpi4-06.azulsystems.com
+Host rpi4-* rpi4-*.azulsystems.com
+User jenkins
+Host zulu-dev
+User ubuntu
+HostName 10.10.164.222
+
 Host *
 Compression yes
-CompressionLevel 9
+# no longer supported: CompressionLevel 9
 ForwardAgent no
 ConnectionAttempts 1
 UsePrivilegedPort no
-RhostsRSAAuthentication no
+# no longer supported: RhostsRSAAuthentication no
 ForwardX11 no
 ServerAliveInterval 60
 StrictHostKeyChecking ask
diff --git a/.vimrc b/.vimrc
index 5254f87..2322696 100644 (file)
--- a/.vimrc
+++ b/.vimrc
@@ -4,7 +4,7 @@
 if !exists("g:_kratochvil_vimrc")
        let g:_kratochvil_vimrc=1
 
-autocmd! filetypedetect
+"autocmd! filetypedetect
 "remove 'set tw=78':
 autocmd! BufRead *.txt
 
@@ -18,7 +18,7 @@ autocmd! BufRead *.txt
 autocmd BufCreate,VimEnter *                                             let g:BufEnter_ts= 8
 autocmd BufEnter * call BufEnter()
 autocmd VimEnter * call BufEnter()
-set runtimepath=.,$VIMRUNTIME
+set runtimepath+=.,$VIMRUNTIME
 function BufEnter()
        if exists("g:BufEnter_ts")
                unlet g:BufEnter_ts
@@ -39,11 +39,11 @@ function BufReadPost()
        endif
 endfunction
 
-set encoding=utf-8
-set termencoding=utf-8
+"set encoding=utf-8
+"set termencoding=utf-8
 " fileencoding is detected from fileencodings, first one for empty files
 " 'iso-8859-2,euc-jp' really does not work for 'euc-jp'
-set fileencodings=utf-8,euc-jp,iso-8859-2
+"set fileencodings=utf-8,euc-jp,iso-8859-2
 
 set ts=8
 set sw=2
@@ -51,7 +51,9 @@ set sts=2
 ca X x
 syntax off
 let g:loaded_matchparen = 1
-set swapsync=
+if !has('nvim')
+       set swapsync=
+endif
 " vim-7+ only
 silent! set nofsync
 set noerrorbells
@@ -66,6 +68,7 @@ set noincsearch
 set nofoldenable
 set fo=tcq1
 set nojoinspaces
+set clipboard=
 "set textwidth=78
 set viminfo='100,\"1000,:100,/40
 let g:netrw_home = $HOME
@@ -80,33 +83,40 @@ set nocst
 set tags=./tags,./TAGS,./../tags,./../TAGS,./../../tags,./../../TAGS,./../../../tags,./../../../TAGS,./../../../../tags,./../../../../TAGS,./../../../../../tags,./../../../../../TAGS,./../../../../../../tags,./../../../../../../TAGS,./../../../../../../../tags,./../../../../../../../TAGS,./../../../../../../../../tags,./../../../../../../../../TAGS,./../../../../../../../../../tags,./../../../../../../../../../TAGS,./../../../../../../../../../../tags,./../../../../../../../../../../TAGS,./../../../../../../../../../../../tags,./../../../../../../../../../../../TAGS,./reactos/tags,./../reactos/tags,./../../reactos/tags,,./../../../reactos/tags,./../../../../reactos/tags,./../../../../../reactos/tags,./../../../../../../reactos/tags,./w32/inc/tags,./../w32/inc/tags,./../../w32/inc/tags,,./../../../w32/inc/tags,./../../../../w32/inc/tags,./../../../../../w32/inc/tags,./../../../../../../w32/inc/tags,./../../../../../../../w32/inc/tags,/usr/src/redhat/BUILD/tags,/usr/src/redhat/BUILD/TAGS,/usr/include/tags,/usr/include/TAGS
 
 noremap <Esc>p :set invpaste paste?<cr>
-noremap <Esc>l :set invlist paste?<cr>
+noremap <Esc>l :set invlist list?<cr>
 noremap <Esc>q @q
 noremap <Esc>: @:
 noremap <C-n> :next<cr>zz<C-g>
 noremap <C-p> :prev<cr>zz<C-g>
 noremap <Esc>N :tn<cr>zz<C-g>
-noremap <Esc>P :tp<cr>zz<C-g>
-"<Esc>P would auto-type random data after starting it on recent distros or Termux.
-"read(0, "\33P1$r0 q\33\\\33[?12;4$y", 4096) = 19
-noremap <Esc>P$r0<Space>q <Nop>
+if $ANDROID_DATA!=#"/data" && $SSH_CLIENT!~"^2a02:2b88:2:1::3b57:[89]f "
+  " It auto-types random data after starting it from Termux
+  " Now all VIM version are affected:
+  noremap <Esc>P :tp<cr>zz<C-g>
+  noremap <Esc>P1$r0<Space>q <Nop>
+endif
 " Termux types <F11>
 noremap! <Esc>[23~ <Nop>
 noremap <Esc>M :cn<cr>zz<C-g>
 noremap <Esc>L :cp<cr>zz<C-g>
-noremap <Esc>f "myiw:grep<Space>-Ew<Space>'<C-r>m'<Space>`git ls-files`
+" dnf install vim-fugitive
+noremap <Esc>f "myiw:Ggrep<Space>-Ew<Space>'<C-r>m'<Space>
+noremap <Esc>F "myiw:grep<Space>-Ew<Space>'<C-r>m'<Space>`git ls-files`
 noremap <Esc>g "myiw:grep<Space>-rEw<Space>'<C-r>m'<Space>.
 noremap <C-]> <C-]>zz<C-g>
 noremap <C-t> <C-t>zz<C-g>
 noremap <C-d> G:r !date '+\%H:\%M:\%S-'<cr>kA
-noremap <Esc>d :set hlsearch<cr>/^[+-]\([^+-].*\\|\)$<cr>
-noremap <Esc>D :set hlsearch<cr>/^[+-][+-]\([^+-].*\\|\)$<cr>
+noremap <Esc>s :set hlsearch<cr>/^[+-]\([^+-].*\\|\)$<cr>
+noremap <Esc>S :set hlsearch<cr>/^[+-][+-]\([^+-].*\\|\)$<cr>
 noremap <Esc>w 0i <Esc>/[^ ]<cr>vf.h"kyf./[^ ]<cr>vf.h"ly0x:r !date -d "`date '+\%Y'`-<C-r>l-<C-r>k" '+\%u'<cr>yypkV:!tr '1234567' 'PUSCPSN'<cr>jV:!tr '1234567' 'otttaoe'<cr>kJxv0xk0llllllplxxjddk0
+noremap <Esc>e :syntax on<cr>:set syntax=wdiff<cr>
 noremap * :let ic_save=&ic<cr>:set noic<cr>*:let &ic=ic_save<cr>
 noremap # :let ic_save=&ic<cr>:set noic<cr>#:let &ic=ic_save<cr>
 noremap <C-k> :w<cr>:!aspell --check '%'<cr>:e<cr>
 noremap gq] gq/^. \?$<cr>
 noremap <Esc>1 :w<cr>:make -j1<cr>
-noremap <Esc>m :w<cr>:make<cr>
+noremap <Esc>3 /^<char-60><char-60><char-60><char-60><char-60><char-60><char-60><cr>kmajd$V/^<bar><bar><bar><bar><bar><bar><bar><cr>k:w! 1<cr>gvxd$V/^=======<cr>k:w! 2<cr>gvxd$V/^<char-62><char-62><char-62><char-62><char-62><char-62><char-62><cr>k:w! 3<cr>gvxdd'a0:!diff -u 2 1;diff -u 2 3<cr>
+noremap <Esc>m :w<cr>:set makeprg=make<cr>:make<cr>
+noremap <Esc>n :w<cr>:set makeprg=ninja<cr>:make<cr>
 
 endif "!exists("g:_kratochvil_vimrc")
diff --git a/bin/.cvsignore b/bin/.cvsignore
deleted file mode 100644 (file)
index f59bf6d..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-unmime
-flock
-nmap
-hexedit
-ghortident
-rmd160
-urlencode
-webcopy
-weblint
-cvs
-
-python2.1
-python
-pydoc
-
-grep
-egrep
-fgrep
-
-ldconfig-docache
-
-iconv
-mutt
-pgpring
-pgpewrap
-
-perl
-perl5.6.1
-a2p
-c2ph
-h2ph
-h2xs
-perlbug
-perldoc
-pl2pm
-splain
-perlcc
-dprofpp
-s2p
-pod2man
-find2perl
-pod2html
-pod2latex
-pod2text
-pod2usage
-podchecker
-podselect
-pstruct
-
-GET
-lwp-mirror
-lwp-request
-lwp-download
-lwp-rget
-HEAD
-POST
-
-xmlwf
-yapp
-pngtogd
-pngtogd2
-gdtopng
-gd2topng
-gd2copypal
-gdparttopng
-webpng
-bdftogd
-xpath
-dbiproxy
-dbish
-xql.pl
-ttree
-tpage
-
-php
-phpextdist
-phpize
-php-config
-pear
diff --git a/bin/ccache-build b/bin/ccache-build
new file mode 100755 (executable)
index 0000000..df63d74
--- /dev/null
@@ -0,0 +1,12 @@
+#! /bin/bash
+# openjdk: export PATH="$(echo "$PATH"|sed 's#:/usr/lib64/ccache:#:'$HOME'/ccache:#')";bash configure --disable-precompiled-headers --disable-ccache
+set -ex
+rm -rf ~/ccache
+mkdir ~/ccache
+cd ~/ccache
+for i in /usr/lib64/ccache/*;do
+  j=`basename $i`
+  echo -e '#! /bin/bash\nexport PATH="$(echo "$PATH"|sed s#:$HOME/ccache:#:#)"\nexec '$i' "$@"' >$j
+  chmod +x $j
+done
+echo done
diff --git a/bin/cherrycompare b/bin/cherrycompare
new file mode 100755 (executable)
index 0000000..55a92cd
--- /dev/null
@@ -0,0 +1,34 @@
+#! /bin/bash
+n=false
+if [ $1 = -n ];then
+  n=true
+  shift
+fi
+diff_u="diff $(if $n;then echo -U999999999;else echo -u;fi)"
+if [ $# != 2 ];then
+  echo >&2 "$0: <dir1> <dir2>"
+fi
+for dir in "$@";do
+  if [ ! -e $dir ];then
+    echo >&2 "!-e: $dir"
+    exit 1
+  fi
+  sed -i -e 's#\(failed for \)[0-9]*\(: Operation not permitted\)#\1\2#' $(find $dir -name summary.txt)
+done
+dir1=$(realpath $1)
+dir2=$(realpath $2)
+cd $1
+(
+  for i in $(find -name summary.txt);do
+    echo "$dir1/$i -> $dir2/$i"
+    $diff_u $i $dir2/$i
+  done
+  function prep8
+  {
+    sed <$1 -n 's/^\(Passed\|FAILED\): \(.*\)$/\2: \1/p'|sort
+  }
+  for i in $(find -name "*-test.release.log");do
+    echo "$dir1/$i -> $dir2/$i"
+    $diff_u <(prep8 $i) <(prep8 $dir2/$i)
+  done
+)|if $n;then cat;else grep -v '^+++ '|grep -v '^--- '|grep -v '^[ @]';fi
diff --git a/bin/cherrydownload b/bin/cherrydownload
new file mode 100755 (executable)
index 0000000..90c52e6
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+for arg in $@;do
+  # http://cherry.azulsystems.com:8080/job/zulu17-silver-build/377/
+  arg="$(echo $arg|sed 's#[a-z]\+/\?$##')"
+  build="$(echo $arg|sed -n 's#^.*/\([0-9]\+\)/$#\1#p')"
+  if [ -z "$build" ];then
+    echo >&2 "parse error: $arg"
+    exit 1
+  fi
+  build="build$build"
+  if [ -e "$build" ];then
+    echo >&2 "exists: $build"
+    exit 1
+  fi
+  mkdir $build
+  outcome="$(wget -O - "${arg}artifact/outcome/")"
+  if [ -z "$outcome" ];then
+    echo >&2 "no outcome: $arg"
+    exit 1
+  fi
+  for release in $(echo "$outcome"|perl -ne 'while (m{<a href="([^"]*)/release">}g) { print "$1\n"; }');do
+    # java17, java8
+    ok=false
+    for file in test-results.tar.gz test.release.log;do
+      url="${arg}artifact/outcome/$release/release/$file"
+      if wget --read-timeout=5 -O "$build/$release-$file" "$url";then
+       ok=true
+       break
+      fi
+      rm -f "$build/$release-$file"
+    done
+    if ! $ok;then
+      echo >&2 "wget error"
+      exit 1
+    fi
+  done
+  (cd $build;if compgen -G "*test-results.tar.gz";then exx *test-results.tar.gz;fi)
+done
diff --git a/bin/cherryfilter b/bin/cherryfilter
new file mode 100755 (executable)
index 0000000..681a0e1
--- /dev/null
@@ -0,0 +1,20 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+my $file;
+while (<STDIN>) {
+  if (/^[^+-]/) {
+    $file=$_;
+    next;
+  }
+  my $match;
+  for my $arg (@ARGV) {
+    $match=1 if /\Q$arg\E/;
+  }
+  next if !$match;
+  if (defined $file) {
+    print $file;
+    $file=undef;
+  }
+  print;
+}
index fd308d3..50740c4 100755 (executable)
@@ -26,7 +26,7 @@ mcheck=""
 #mcheck="-lmcheck"
 fast=false
 readline=""
-disablebinutils="--disable-binutils --disable-gas --disable-gold --disable-gprof --disable-ld"
+disablebinutils="--disable-binutils --disable-gas --disable-gold --disable-gprof --disable-ld --disable-gprofng"
 python=""
 
 while true
diff --git a/bin/exx b/bin/exx
index 97f021b..8994599 100755 (executable)
--- a/bin/exx
+++ b/bin/exx
@@ -14,6 +14,8 @@ use constant FORMATS=>{
        "rpm"    =>'rpm2cpio $pathname|cpio -id --quiet',       #-v #FIXME: --sparse doesn't work, why?
        "zip"    =>'unzip -Lq $pathname',
        "jar"    =>'unzip -Lq $pathname',
+       "xpi"    =>'unzip -Lq $pathname',
+       "crx"    =>'unzip -Lq $pathname',
        "a"      =>'ar x $pathname',
        "deb"    =>'ar x $pathname;'
                   .'for i in *.tar.gz;do j=`basename $i .tar.gz`;mkdir -p $j;cd $j;tar xzf ../$i;cd ..;rm -f $i;done',
diff --git a/bin/git-bisect-status b/bin/git-bisect-status
new file mode 100755 (executable)
index 0000000..c49167a
--- /dev/null
@@ -0,0 +1,3 @@
+#! /bin/bash
+# https://stackoverflow.com/a/37668768/2995591
+git bisect visualize --oneline|wc -l|perl -MPOSIX -lne 'print "Bisecting: ".floor(($_-1)/2)." revisions left to test after this (roughly ".(floor(log($_-1)/log(2))-1)." steps)" if $_'
diff --git a/bin/git-show-ls b/bin/git-show-ls
new file mode 100755 (executable)
index 0000000..b502547
--- /dev/null
@@ -0,0 +1,3 @@
+#! /bin/bash
+# https://stackoverflow.com/a/424142/2995591
+exec git show --pretty="" --name-only "$@"
diff --git a/bin/heat b/bin/heat
new file mode 100755 (executable)
index 0000000..0c81139
--- /dev/null
+++ b/bin/heat
@@ -0,0 +1,186 @@
+#! /usr/bin/perl
+# * * * * * nice -n20 /root/bin/heat -s
+use strict;
+use warnings;
+my $schedulefile="/root/heat.schedule";
+my $statefile="/root/heat.state";
+my $resetfile="/root/heat.reset";
+my $usbrelay="/root/bin/usbrelay";
+my $logfile="/var/log/heat.log";
+require POSIX;
+$|=1;
+sub readfile($) {
+  my($fname)=@_;
+  local *F;
+  if (!open F,$fname) {
+    warn "$fname: $!";
+    return undef;
+  }
+  my $F=do { local $/; <F>; };
+  defined $F or die "read $fname: $!";
+  close F or die "read-close $fname: $!";
+  return $F;
+}
+sub writefile($$;$) {
+  my($fname,$content,$mode)=@_;
+  local *F;
+  open F,($mode||">").$fname or die "$fname: $!";
+  print F $content or die "write $fname: $!";
+  close F or die "write-close $fname: $!";
+}
+sub logmsg($) {
+  my($s)=@_;
+  chomp $s;
+  writefile $logfile,POSIX::strftime("%Y-%m-%dT%H:%M:%S",localtime)." $s\n",">>";
+}
+sub spawn($) {
+  my($cmd)=@_;
+  $cmd.=" >&2";
+  system $cmd and die "$cmd: $!";
+}
+my($trash,$min,$hour)=localtime;
+my $minutes=$hour*60+$min;
+if (($ARGV[0]||"")=~/^[@]0?(\d+):0?(\d+)$/) {
+  $minutes=$1*60+$2;
+  shift;
+}
+my $finishline;
+my %schedule;
+my $schedule;
+if (-e $schedulefile) {
+  $schedule=readfile $schedulefile;
+  while ($schedule=~s/^(0?(\d+):0?(\d+)) ([01s])\n//) {
+    my $tm=$2*60+$3;
+    warn "$schedulefile set twice for: $1" if defined $schedule{$tm};
+    $schedule{$tm-24*60}="$1 $4\n";
+    $schedule{$tm      }="$1 $4\n";
+    $schedule{$tm+24*60}="$1 $4\n";
+  }
+  warn "$schedulefile garbage: $schedule" if $schedule ne "";
+  warn "Empty $schedulefile" if !%schedule;
+}
+sub schedulenext($) {
+  return undef if !%schedule;
+  my($now)=@_;
+  my($bestprev,$prev,$bestnext,$next);
+  for my $found (keys(%schedule)) {
+    if ($found<=$now) {
+      next if defined $bestprev&&$bestprev>$found;
+      $bestprev=$found;
+      $prev=$schedule{$found};
+    } else {
+      next if defined $bestnext&&$bestnext<$found;
+      $bestnext=$found;
+      $next=$schedule{$found};
+    }
+  }
+  die "No bestprev" if !defined $bestprev;
+  die "No bestnext" if !defined $bestnext;
+  return [$prev,$next];
+}
+$finishline=schedulenext($minutes);
+$finishline=[$finishline->[1]] if $finishline;
+my $silent=shift if ($ARGV[0]||"") eq "-s";
+logmsg "command: ".join(" ",@ARGV) if !$silent&&@ARGV;
+sub info($) {
+  my($s)=@_;
+  print $s if !$silent;
+}
+sub finish() {
+  info join("",@$finishline) if defined $finishline;
+  exit 0;
+}
+my $reset=readfile $resetfile if -e $resetfile;
+my($resetminutes,$resetstate);
+sub resetread() {
+  $reset=~/^0?(\d+)[:.]0?(\d+) ([01s]|reset)\n$/ or warn "Invalid $resetfile: $reset";
+  $resetminutes=$1*60+$2;
+  $resetstate=($3 eq "reset"?undef:$3);
+}
+sub unlink_resetfile() {
+  unlink $resetfile or die "$resetfile: $!";
+  logmsg "remove reset: $reset";
+  $reset=$resetminutes=undef;
+}
+if (defined $reset) {
+  resetread;
+  unlink_resetfile if $resetminutes==$minutes;
+}
+sub finishlinereset() {
+  $finishline=schedulenext $resetminutes;
+  if ($finishline) {
+    $finishline=[$reset,$finishline->[1]];
+  } else {
+    $finishline=[$reset];
+  }
+}
+finishlinereset if defined $reset;
+my $state=readfile $statefile;
+chomp $state;
+info $state;
+my $newstate=shift;
+if (!defined $newstate&&defined $resetstate&&!defined $resetminutes) {
+  $newstate=$resetstate;
+  logmsg "reset: $newstate";
+}
+sub printminutes($) {
+  my($m)=@_;
+  return sprintf "%02d:%02d",int($m/60),$m%60;
+}
+if ($silent) {
+  if (!defined $newstate&&!defined $reset&&%schedule) {
+    my $prev=schedulenext($minutes);
+    if ($prev) {
+      my $prev=$prev->[0];
+      if ($prev=~m{ (.)\n$}) {
+       $newstate=$1;
+       logmsg "scheduled: $prev";
+      }
+    }
+  }
+  $newstate=$state if !defined $newstate;
+}
+if (!defined $newstate) {
+  info "\n";
+  finish;
+}
+die "state!={s|0|1}" if $newstate!~/^[s01]$/;
+sub setstate() {
+  info "->$newstate";
+  writefile $statefile,"$newstate\n" if $state ne $newstate;
+  logmsg "$state->$newstate" if $state ne $newstate;
+  if ($newstate ne "s") {
+    my $pid=readfile "pidof -x dnf;true|";
+    die "\n".printminutes($minutes)." change $state".($state eq $newstate?"":"->$newstate")." refused: dnf running: $pid" if $pid;
+  }
+  my $both={"s"=>[0,0],"0"=>[1,0],"1"=>[1,1]}->{$newstate};
+  spawn "$usbrelay 1 ".$both->[0];
+  spawn "$usbrelay 2 ".$both->[1];
+  info "\n";
+}
+my $newreset=shift;
+die "Excessive args: ".join(" ",@ARGV) if @ARGV;
+if (!defined $newreset) {
+  setstate;
+  if (!$silent&&defined $reset) {
+    unlink_resetfile;
+    $finishline=schedulenext($minutes);
+    $finishline=[$finishline->[1]] if $finishline;
+  }
+  finish;
+}
+$reset=$newreset;
+if ($reset=~/^\d+$/) {
+  $resetminutes=($reset+$minutes)%(24*60);
+  $reset=printminutes($resetminutes)." reset\n";
+  setstate;
+} else {
+  $reset.=" reset\n";
+  resetread;
+  $reset=printminutes($resetminutes)." $newstate\n";
+  info "\n";
+}
+writefile $resetfile,$reset;
+logmsg "new reset: $reset";
+finishlinereset;
+finish;
diff --git a/bin/hog b/bin/hog
new file mode 100755 (executable)
index 0000000..4dd1017
--- /dev/null
+++ b/bin/hog
@@ -0,0 +1,19 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+my $psu=system($ENV{"HOME"}."/bin/psu")==0;
+local *F;
+open F,"/usr/bin/top -b -n2 -d1|" or die;
+my $top=0;
+while (<F>) {
+  /^top / && $top++;
+  /^$/ && $top++;
+  next if $top!=5;
+  /^(?:\s*\S+){8}\s*(\S+)\s*\S+\s*([^:\s]*):/ or next;
+  next if $1<90;          # CPU %
+  next if $2<($psu?30:1); # minutes it has run
+  tr/'//d;
+  system "DISPLAY=:0 xmessage -timeout 30 -default okay -center '$_' 2>/dev/null";
+  exit 0;
+}
+close F or die;
diff --git a/bin/java_error-findaddr b/bin/java_error-findaddr
new file mode 100755 (executable)
index 0000000..c8f62c6
--- /dev/null
@@ -0,0 +1,50 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+use bigint qw/hex/;
+
+my $opt_f=1,shift if ($ARGV[0]||"") eq "-f";
+
+sub readfile($) {
+  my($fname)=@_;
+  local *F;
+  open F,$fname or die $fname;
+  my $F=do { local $/; <F>; };
+  defined $F or die $fname;
+  close F or die $fname;
+  return $F;
+}
+sub fromhex($) {
+  my($hex)=@_;
+  $hex=~s/^0x//;
+  die "Invalid addr: $hex" if $hex!~/^[0-9a-f]+$/i;
+  return hex $hex;
+}
+
+my $fn=shift;
+my $f=readfile $fn;
+my @f;
+while ($f=~/^(([0-9a-f]+)-([0-9a-f]+) [r-][w-][x-][p-] .*)\r?$/mg) {
+  my $line =$1;
+  my $start=fromhex $2;
+  my $end  =fromhex $3;
+  push @f,[$line,$start,$end];
+}
+if (!@ARGV) {
+  $f=~/^RIP=(0x[^,]*),/m or die "no RIP";
+  push @ARGV,$1;
+  my $stack=($f=~/^Top of Stack:.*?\n(.*?\n)\n/ms)[0] or die "no Top of Stack";
+  $stack=~s/^0x.*: *//mg;
+  push @ARGV,split /\s+/,$stack;
+}
+for my $arg (@ARGV) {
+  my $addr=fromhex $arg;
+  my $found;
+  for my $ft (@f) {
+    my $line =$ft->[0];
+    my $start=$ft->[1];
+    my $end  =$ft->[2];
+    $found=1,print "$arg: $line\n" if $start<=$addr&&$addr<$end&&(!$opt_f||$line=~/[^ ]$/);
+  }
+  print "$arg: -\n" if !$found&&!$opt_f;
+}
diff --git a/bin/jtreg-pass-fail b/bin/jtreg-pass-fail
new file mode 100755 (executable)
index 0000000..aca6978
--- /dev/null
@@ -0,0 +1,14 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+my $f=shift if ($ARGV[0]||"") eq "-f";
+my %p;
+my %f;
+while (<>) {
+  chomp;
+  $p{$_}=1 if s/^Passed: //;
+  $f{$_}=1 if s/^TEST: //;
+}
+die "No PASS" if !%p&&!$f;
+my %a=(%p,%f);
+print(($p{$_}?"PASS":"FAIL")." test/hotspot/jtreg/$_\n") for sort keys(%a);
diff --git a/bin/jtreg-pass-fail2 b/bin/jtreg-pass-fail2
new file mode 100755 (executable)
index 0000000..490d38b
--- /dev/null
@@ -0,0 +1,21 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+my %t;
+my $test;
+while (<>) {
+  chomp;
+  if (/^TEST: (.*)$/) {
+    die $_ if $test;
+    $test=$1;
+    next;
+  }
+  if (/^TEST RESULT: (Passed|Failed)[.]/) {
+    die $_ if !$test;
+    $t{$test}=$1 eq "Passed";
+    $test=undef;
+    next;
+  }
+}
+die if defined $test;
+print(($t{$_}?"PASS":"FAIL")." test/hotspot/jtreg/$_\n") for sort keys(%t);
diff --git a/bin/makeinfo b/bin/makeinfo
deleted file mode 100755 (executable)
index 61e1f05..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /bin/sh
-if [ "$*" = "--version" ];then
-       /usr/bin/makeinfo "$@" | sed 's/texinfo[^0-9]*/&4.40 - orig /'
-else
-       exec /usr/bin/makeinfo "$@"
-fi
diff --git a/bin/nn b/bin/nn
new file mode 100755 (executable)
index 0000000..d8ce7f4
--- /dev/null
+++ b/bin/nn
@@ -0,0 +1,9 @@
+#! /bin/sh
+if [ -z "$*" ];then
+  echo >&2 "No PID"
+elif [ "$1" = "-p" ];then
+  shift
+  exec chrt -i -p 0 "$@"
+else
+  exec chrt -i 0 "$@"
+fi
diff --git a/bin/openjdk-find-ccache-bug b/bin/openjdk-find-ccache-bug
new file mode 100755 (executable)
index 0000000..2c62ff3
--- /dev/null
@@ -0,0 +1,8 @@
+#! /bin/bash
+# 0 * * * * ~/bin/openjdk-find-ccache-bug
+for i in $(find ~ 2>/dev/null -name config.status);do
+  if grep -qw REWRITE_PATHS_RELATIVE $(echo $i|sed 's#/build/[^/]*/configure-support/config.status$##')/make/common/NativeCompilation.gmk 2>/dev/null;then
+    continue
+  fi
+  grep CCACHE_BASEDIR= $i /dev/null
+done
diff --git a/bin/psu b/bin/psu
new file mode 100755 (executable)
index 0000000..c8d9697
--- /dev/null
+++ b/bin/psu
@@ -0,0 +1,4 @@
+#! /bin/bash
+/usr/bin/acpi|&grep -Eq 'Battery 0: Not charging|Battery 0: Charging|No support for device type: power_supply' || exit 1
+/usr/bin/grpcurl -v -plaintext -d '{"get_status":{}}' 192.168.1.1:9000 SpaceX.API.Device.Device/Handle &>/dev/null || exit 1
+exit 0
index c61a6b7..a163307 100755 (executable)
@@ -21,7 +21,9 @@ for base in $l;do
 
        if test ! -f $orig
        then
-               cmp $base $base.rpmnew
+               if [ "$base" = "${base%/java.security}" ];then
+                       cmp $base $base.rpmnew
+               fi
                mv -f $base.rpmnew $base
                continue
        fi
@@ -54,7 +56,7 @@ done
 if $ok
 then
        mkdir -p /root/rpmmerge
-       rpm -qac|perl -ne 'chomp;next if $_ eq "(contains no files)";next if m{^/var/lib/rpm/};next if $_ eq "/usr/lib/locale/locale-archive";next if ! -f $_;$o=$_;$o="/root/rpmmerge/$o";next if -e $o;$od=$o;$od=~s{[^/]*$}{};system "mkdir -p '\''$od'\''" and die "dir $_: $!";system "cp -i -p '\''$_'\'' '\''$o'\''" and die "$_: $!";'
+       rpm -qac|perl -ne 'chomp;next if $_ eq "(contains no files)";next if m{^/var/lib/rpm/};next if $_ eq "/usr/lib/locale/locale-archive";next if $_ eq "/usr/lib/sysimage/rpm/rpmdb.sqlite";next if ! -f $_;$o=$_;$o="/root/rpmmerge/$o";next if -e $o;$od=$o;$od=~s{[^/]*$}{};system "mkdir -p '\''$od'\''" and die "dir $_: $!";system "cp -i -p '\''$_'\'' '\''$o'\''" and die "$_: $!";'
 
        echo OK
 else
diff --git a/bin/shutdown-detect b/bin/shutdown-detect
new file mode 100755 (executable)
index 0000000..53bc406
--- /dev/null
@@ -0,0 +1,4 @@
+#! /bin/bash
+echo "$(date --iso=seconds) shutdown $* boot=$(uptime -s) $(uptime -p)" >>/var/log/shutdown-detect.log
+touch /var/log/shutdown-detect.ok
+sync
diff --git a/bin/shutdown-detect-boot b/bin/shutdown-detect-boot
new file mode 100755 (executable)
index 0000000..7990229
--- /dev/null
@@ -0,0 +1,11 @@
+#! /bin/bash
+boot="boot $* boot=$(uptime -s) $(uptime -p)"
+echo "$(date --iso=seconds) uninit-time $boot" >>/var/log/shutdown-detect.log
+# Raspberry Pi 3B+: 15 sec
+sleep 60
+echo "$(date --iso=seconds)   init-time $boot" >>/var/log/shutdown-detect.log
+if [ -e /var/log/shutdown-detect.ok ];then
+  rm -f /var/log/shutdown-detect.ok
+  exit 0
+fi
+echo "$(date --iso=seconds) unclean shutdown"|tee -a /var/log/shutdown-detect.log|mail -s "unclean shutdown: $(hostname)" jan@jankratochvil.net
diff --git a/bin/shutdown-detect.service b/bin/shutdown-detect.service
new file mode 100644 (file)
index 0000000..88cd5a1
--- /dev/null
@@ -0,0 +1,15 @@
+# jankratochvil:
+# ln -s ../../../root/bin/shutdown-detect.service /etc/systemd/system/shutdown-detect.service; systemctl enable shutdown-detect.service
+# https://unix.stackexchange.com/a/41756/296319
+
+[Unit]
+Description=shutdown-detect
+
+[Service]
+Type=oneshot
+RemainAfterExit=true
+ExecStart=/root/bin/shutdown-detect-boot
+ExecStop=/root/bin/shutdown-detect
+
+[Install]
+WantedBy=multi-user.target
index b58d0e0..fa7906f 100755 (executable)
@@ -1,2 +1,3 @@
 #! /bin/bash
-tail -fn0 `(file $(find /var/log -name ntpstats -prune -o -type f -print)|grep -v 'data$'|sed 's/:.*//'|grep -v '^\(/var/log/squid/store.log\|/var/log/acpid\|/var/log/btmp\|/var/log/maillog\)$';echo "$*"|tr ' ' '\n')|sort -u` # |grep -v ': bio too big device md3 (248 > 240)$'
+ulimit -n 65536
+tail -fn0 `(file $(find /var/log -name ntpstats -prune -o -type f -print)|grep -v 'data$'|sed 's/:.*//'|grep -v '^\(/var/log/squid/store.log\|/var/log/acpid\|/var/log/btmp\|/var/log/journal/.*\|/var/log/maillog\|/var/log/sa/\)$';echo "$*"|tr ' ' '\n')|sort -u` # |grep -v ': bio too big device md3 (248 > 240)$'
diff --git a/bin/upsc-log b/bin/upsc-log
new file mode 100755 (executable)
index 0000000..7298a39
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+renice >/dev/null +19 -p $$
+ionice -c3 -p $$
+t=/tmp/upsc-log.$$
+rm -f $t $t.*
+log=/var/log/upsc.log
+exec >>$log 2>&1
+while sleep 1;do
+  date --iso=seconds >$t.t
+  upsc eaton &>$t
+  if grep -q '^ups.status: OL$' $t && grep -q '^battery.charge: 100$' $t;then
+    rm -f $t.bad $t.bad.t
+    if [ ! -e $t.good ];then
+      cat $t.t $t;echo
+      sync -d $log
+    fi
+    mv -f $t.t $t.good.t
+    mv -f $t   $t.good
+    rm -f /tmp/upsc-smsed
+    continue
+  fi
+  if [ ! -e /tmp/upsc-smsed ];then
+    touch /tmp/upsc-smsed
+    date --iso=seconds|mail -s "upsc-log $(hostname)" jankratochvil@vodafonemail.cz
+  fi
+  if [ -e $t.good ];then
+    cat   $t.good.t $t.good;echo
+    sync -d $log
+    rm -f $t.good.t $t.good
+  fi
+  if [ -e $t.bad ] && cmp -s $t $t.bad;then
+    continue
+  fi
+  cat $t.t $t;echo
+  sync -d $log
+  mv -f $t.t $t.bad.t
+  mv -f $t   $t.bad
+done
diff --git a/bin/upsc-log-diff b/bin/upsc-log-diff
new file mode 100755 (executable)
index 0000000..7ca44e3
--- /dev/null
@@ -0,0 +1,47 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+local *F;
+my $ta="/tmp/upsc-log-diff.".$$."a";
+my $tb="/tmp/upsc-log-diff.".$$."b";
+unlink $ta; unlink $tb;
+END {
+  unlink $ta; unlink $tb;
+}
+my $F="/var/log/upsc.log";
+open F,$F or die "$F: $!";
+my $prev;
+my $this="";
+my $line;
+while (<F>) {
+  $line=$.;
+  chomp;
+  if (!/^$/) {
+    $this.="$_\n";
+    next;
+  }
+  if ($prev) {
+    local *TA;
+    local *TB;
+    open TA,">$ta" or die "$ta: $!";
+    open TB,">$tb" or die "$tb: $!";
+    print TA $prev or die "$ta: $!";
+    print TB $this or die "$tb: $!";
+    close TA or die "$ta: $!";
+    close TB or die "$ta: $!";
+    local *TD;
+    my $td="diff -u $ta $tb|";
+    open TD,$td or die "$td: $!";
+    <TD>=~/^\Q--- / or die "line $line: $_";
+    <TD>=~/^\Q+++ / or die "line $line: $_";
+    while (<TD>) {
+      next if /^[@ ]/;
+      print;
+    }
+    close TD; # or die "$td: $!";
+    print "\n";
+  }
+  $prev=$this;
+  $this="";
+}
+die $this if $this;
diff --git a/bin/usbrelay b/bin/usbrelay
new file mode 100755 (executable)
index 0000000..d45514c
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# -*- encoding: utf-8 -*-
+# https://slomkowski.eu/tutorials/eavesdropping-usb-and-writing-driver-in-python/
+
+import time
+import sys
+
+import usb.core
+import usb.util
+
+VENDOR_ID = 0x16c0
+DEVICE_ID = 0x05df
+
+MANUFACTURER_NAME = "www.dcttech.com"
+PRODUCT_NAME = "USBRelay2"
+
+
+def check_manufacturer_and_product(dev):
+    return dev.manufacturer == MANUFACTURER_NAME and dev.product == PRODUCT_NAME
+
+
+def find_device_handle():
+    return usb.core.find(idVendor=VENDOR_ID, idProduct=DEVICE_ID,
+                         custom_match=check_manufacturer_and_product)
+
+
+dev_handle = find_device_handle()
+
+if dev_handle.is_kernel_driver_active(0):
+    try:
+        dev_handle.detach_kernel_driver(0)
+        print("kernel driver detached")
+    except usb.core.USBError as e:
+        sys.exit("Could not detach kernel driver: %s" % str(e))
+
+requestType = usb.util.build_request_type(usb.util.CTRL_OUT,
+                                          usb.util.CTRL_TYPE_CLASS,
+                                          usb.util.CTRL_RECIPIENT_INTERFACE)
+
+
+def set_relay(relay_number, enabled: bool):
+    b1 = 0xff if enabled else 0xfd
+    dev_handle.ctrl_transfer(requestType, 9, 0x0300, 0, (b1, relay_number, 0, 0, 0, 0, 0, 0))
+
+
+#while True:
+#    set_relay(1, True)
+#    time.sleep(1)
+#    set_relay(1, False)
+#    time.sleep(1)
+#    set_relay(2, True)
+#    time.sleep(1)
+#    set_relay(2, False)
+#    time.sleep(1)
+
+
+if len(sys.argv)!=3 or (sys.argv[1]!="1" and sys.argv[1]!="2") or (sys.argv[2]!="0" and sys.argv[2]!="1"):
+    sys.exit("usbrelay: {1|2} {0|1}")
+set_relay((1 if sys.argv[1]=="1" else 2),(True if sys.argv[2]=="1" else False))
diff --git a/bin/vi-git-both-modified b/bin/vi-git-both-modified
new file mode 100755 (executable)
index 0000000..8f47892
--- /dev/null
@@ -0,0 +1,15 @@
+#! /bin/bash
+fis=""
+for fi in $(git status|sed -n 's/^     both modified: *//p');do
+  if ! grep -q '^<<<<<<< ' $fi;then
+    git add $fi
+    continue
+  fi
+  fis="$fis $fi"
+done
+if [ -z "$fis" ];then
+  echo done
+  exit 0
+fi
+set -x
+exec vim $fis
diff --git a/bin/wcsrc b/bin/wcsrc
new file mode 100755 (executable)
index 0000000..0daba7a
--- /dev/null
+++ b/bin/wcsrc
@@ -0,0 +1,7 @@
+#! /bin/bash
+set -ev
+wc `git ls-files`
+cat `git ls-files`|sort -u|wc
+cat `git ls-files`                                  |perl -pe 's/^\s*//'|sort -u|wc
+git log -p        |perl -ne 'print if s/^[+][^+]//;'|perl -pe 's/^\s*//'|sort -u|wc
+echo done
diff --git a/src/.cvsignore b/src/.cvsignore
deleted file mode 100644 (file)
index 5fff1d9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-pkg
index d23df8b..5625e19 100644 (file)
@@ -4,8 +4,8 @@ CFLAGS+=-Wall -Wstrict-prototypes -ggdb3
 #CFLAGS+=-O9 -fexpensive-optimizations -finline-functions
 #CFLAGS+=-pg
 
-all: yasm2gas
-# unmime flock pipebuf
+all: streamfer-server streamfer-client
+# yasm2gas unmime flock pipebuf
 
 pipebuf: pipebuf.c
        sh $<
@@ -13,3 +13,8 @@ pipebuf: pipebuf.c
 yasm2gas: yasm2gas.c
        gcc -o $@ -Wall -g $<
 
+streamfer-server: streamfer-server.C streamfer.h streamfer.C safeio.C safeio.h socket.C socket.h stringf.h
+       clang++ -o $@ -Wall -g streamfer-server.C streamfer.C safeio.C socket.C
+
+streamfer-client: streamfer-client.C streamfer.h streamfer.C safeio.C safeio.h socket.C socket.h stringf.h
+       clang++ -o $@ -Wall -g streamfer-client.C streamfer.C safeio.C socket.C
diff --git a/src/rpm/.cvsignore b/src/rpm/.cvsignore
deleted file mode 100644 (file)
index 958613d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-BUILD
-RPMS
diff --git a/src/rpm/SOURCES/.cvsignore b/src/rpm/SOURCES/.cvsignore
deleted file mode 100644 (file)
index a95da62..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-cvs-1.11-3:
-cvs-1.10-tmprace.patch
-cvs-1.10.7-fixinfo.patch
-cvs-1.10.7-krb5-1.1.1.patch
-cvs-1.10.8-errno.patch
-cvs-1.10.8-krb4.patch
-cvs-1.10.8-zlib.patch
-cvs-1.11-existence.patch
-cvs-1.11-security.patch
-cvs-1.11.tar.gz
-
-glibc-2.2.2-10:
-glibc-2.2.2.tar.bz2
-glibc-kernel-2.4.patch
-
diff --git a/src/safeio.C b/src/safeio.C
new file mode 100644 (file)
index 0000000..fb76743
--- /dev/null
@@ -0,0 +1,87 @@
+#include "safeio.h"
+#include <sys/uio.h>
+
+const SafeIOError exception_SafeIOError;
+const SafeIOEOF   exception_SafeIOEOF  ;
+
+void read_safe(int fd,void *buf,size_t size) {
+  assert(size>0);
+  while (size>0) {
+    const ssize_t got(read(fd,buf,size));
+    assert(got>=-1);
+    if (got<=0) {
+      warning("read_safe: fd=%d size=%zu: %s",fd,size,(got==0?"EOF":strerror(errno)));
+      // Do not throw ?exc1:exc2 as it would get upcasted.
+      if (got==0||(got==-1&&errno==ECONNRESET))
+       throw exception_SafeIOEOF;
+      throw exception_SafeIOError;
+    }
+    assert(size_t(got)<=size);
+    buf=static_cast<uint8_t *>(buf)+got;
+    size-=got;
+  }
+}
+
+void read_safe(FILE *f,void *buf,size_t size) {
+  assert(size>0);
+  assert(!ferror(f));
+  assert(!feof(f));
+  const size_t got(fread(buf,1/*size*/,size/*nmemb*/,f));
+  if (ferror(f)||feof(f)) {
+    warning("read_safe: fd=%d size=%zu: %s",fileno(f),size,(ferror(f)?"error":"EOF"));
+    // Do not throw ?exc1:exc2 as it would get upcasted.
+    if (ferror(f))
+      throw exception_SafeIOError;
+    throw exception_SafeIOEOF;
+  }
+  // return [...] which is less than nitems only if a read error or end-of-file is encountered.
+  if (size_t(got)!=size) {
+    warning("read_safe: fd=%d size=%zu got=%zu",fileno(f),size,got);
+    throw exception_SafeIOError;
+  }
+}
+
+void write_safe(int fd,const void *buf,size_t size) {
+  assert(size>0);
+  const ssize_t got(write(fd,buf,size));
+  if (got==-1) {
+    warning("Error writing %zu to fd %d: %m",size,fd);
+    throw exception_SafeIOError;
+  }
+  assert(got>0);
+  if (size_t(got)!=size) {
+    warning("Wrote only %zd of %zu to fd %d",got,size,fd);
+    throw exception_SafeIOEOF;
+  }
+}
+
+void write_safe(FILE *f,const void *buf,size_t size) {
+  assert(size>0);
+  assert(!ferror(f));
+  const size_t got(fwrite(buf,1/*size*/,size/*nmemb*/,f));
+  if (ferror(f)) {
+    warning("Error writing %zu to FILE * of fd %d",size,fileno(f));
+    throw exception_SafeIOError;
+  }
+  if (got!=size) {
+    warning("Wrote only %zu of %zu to FILE * of fd %d",got,size,fileno(f));
+    throw exception_SafeIOEOF;
+  }
+}
+
+void writev_safe_(int fd,const struct iovec *iov, int iovcnt) {
+  size_t size=0;
+  for (int ix=0;ix<iovcnt;++ix)
+    size+=iov[ix].iov_len;
+  assert(size>0);
+  const ssize_t got(writev(fd,iov,iovcnt));
+  if (got==-1) {
+    warning("Error writing %zu iovec to fd %d: %m",size,fd);
+    throw exception_SafeIOError;
+  }
+  assert(got>0);
+  if (size_t(got)!=size) {
+    warning("Wrote only %zd of %zu iovec to fd %d",got,size,fd);
+    throw exception_SafeIOEOF;
+  }
+}
diff --git a/src/safeio.h b/src/safeio.h
new file mode 100644 (file)
index 0000000..84a6706
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef LIB_SAFEIO_H
+#define LIB_SAFEIO_H
+
+#include "streamfer.h"
+#include <sys/uio.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <exception>
+#include <vector>
+#include <string>
+#include <array>
+#include <deque>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+
+extern const class SafeIOError:public exception {
+  virtual const char *what() const noexcept override { return "SafeIOError"; }
+} exception_SafeIOError;
+extern const class SafeIOEOF:public SafeIOError {
+  virtual const char *what() const noexcept override { return "SafeIOEOF"; }
+} exception_SafeIOEOF;
+
+void read_safe(int fd ,void *buf,size_t len);
+void read_safe(FILE *f,void *buf,size_t len);
+
+template<class F,class T> void read_safe(F f,       T  &obj) { read_safe(f,&obj,sizeof(obj)); }
+
+template<class F,class T> void read_safe(F f,vector<T> &vec) {
+  size_t size;
+  read_safe(f,size);
+  vec.resize(size);
+  if (size)
+    read_safe(f,vec.data(),vec.size()*sizeof(vec[0]));
+}
+
+template<class F,class T> void read_safe(F f,deque<T> &vec) {
+  size_t size;
+  read_safe(f,size);
+  vec.resize(size);
+  for (auto &elem:vec)
+    read_safe(f,elem);
+}
+
+template<class F> void read_safe(F f,string &str) {
+  uint8_t len8=0; // false GCC warning
+  read_safe(f,len8);
+  str.resize(len8);
+  if (len8)
+    read_safe(f,&str[0],str.length());
+}
+
+template<class F> string read_safe_string(F f) { string str; read_safe(f,str); return str; }
+
+void write_safe(int fd ,const void *buf,size_t count);
+void write_safe(FILE *f,const void *buf,size_t count);
+void writev_safe_(int fd,const struct iovec *iov, int iovcnt);
+constexpr iovec writev_iovec(const void *base,size_t len) { return iovec{const_cast<void *>(base),len}; }
+static inline const/*FIXME:constexpr c_str()*/ iovec iovec_for_string(const string &str) { return iovec{const_cast<char *>(str.c_str()),str.length()}; }
+template<class T> constexpr iovec iovec_for_object(T &object) { return iovec{reinterpret_cast<void *>(const_cast<typename std::remove_const<T>::type *>(&object)),sizeof(object)}; }
+template<size_t iovcnt> void writev_safe(int fd,std::array<iovec,iovcnt> iov) {
+  writev_safe_(fd,iov.data(),iovcnt);
+}
+
+template<class F,class T> void write_safe(F f,const        T  &obj) { write_safe(f,&obj,sizeof(obj)); }
+
+template<class F,class T> void write_safe(F f,const vector<T> &vec) {
+  const size_t size(vec.size());
+  write_safe(f,size);
+  if (size)
+    write_safe(f,vec.data(),size*sizeof(vec[0]));
+}
+
+template<class F,class T> void write_safe(F f,const deque<T> &vec) {
+  const size_t size(vec.size());
+  write_safe(f,size);
+  for (const auto &elem:vec)
+    write_safe(f,elem);
+}
+
+template<class F> void write_safe(F f,const string &str) {
+  const uint8_t len8(str.length());
+  assert(len8==str.length());
+  write_safe(f,len8);
+  if (!str.empty())
+    write_safe(f,str.c_str(),len8);
+}
+
+#endif /* LIB_SAFEIO_H */
diff --git a/src/socket.C b/src/socket.C
new file mode 100644 (file)
index 0000000..2e0e4b8
--- /dev/null
@@ -0,0 +1,120 @@
+#include "socket.h"
+#include "stringf.h"
+#include <netdb.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+int socket_bind(string host_port_str) {
+  const char *cs(strrchr(host_port_str.c_str(),':'));
+  const string node(!cs?"":host_port_str.substr(0,cs-host_port_str.c_str()));
+  const string service(!cs?host_port_str:cs+1);
+  struct addrinfo hints={}; // designated initializer: error: missing initializer for member ... [-Werror=missing-field-initializers]
+  hints.ai_family=AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+  hints.ai_socktype=SOCK_STREAM;
+  hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;    /* For wildcard IP address */
+  struct addrinfo *result;
+  int err(getaddrinfo(node.empty()?NULL:node.c_str(),service.c_str(),&hints,&result));
+  if (err)
+    fatal("<%s>:<%s>: %s",node.c_str(),service.c_str(),gai_strerror(err));
+  int fd=-1;
+  struct addrinfo *rp;
+  for (rp=result;rp;rp=rp->ai_next) {
+    fd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
+    if (fd==-1)
+      continue;
+    static const int int1(1);
+    err=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&int1,sizeof(int1));
+    assert(!err);
+    if (bind(fd,rp->ai_addr,rp->ai_addrlen)==0)
+      break;
+    err=close(fd);
+    assert(!err);
+  }
+  if (rp==NULL)
+    fatal("Cannot bind(): <%s>:<%s>: %m",node.c_str(),service.c_str());
+  assert(fd!=-1);
+  freeaddrinfo(result);
+  err=listen(fd,SOMAXCONN);
+  assert(!err);
+  return fd;
+}
+
+string sockaddr_string(const struct sockaddr *sockaddrp,socklen_t socklen) {
+  char hostname[NI_MAXHOST];
+  char servname[NI_MAXSERV];
+  const int err=getnameinfo(sockaddrp,socklen,hostname,sizeof(hostname),servname,sizeof(servname),NI_NUMERICSERV/*flags*/);
+  assert(!err);
+  return stringf("%s:%s",hostname,servname);
+}
+
+string socket_name(int socket_fd) {
+  struct sockaddr sockaddr;
+  socklen_t socklen(sizeof(sockaddr));
+  const int err(getsockname(socket_fd,&sockaddr,&socklen));
+  assert(!err);
+  return sockaddr_string(&sockaddr,socklen);
+}
+
+static void socket_setopt(int fd) {
+  static const int int1(1);
+  int err;
+  err=setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&int1,sizeof(int1));
+  assert(!err);
+  static const uint8_t tos(IPTOS_LOWDELAY);
+  err=setsockopt(fd,IPPROTO_IP,IP_TOS,&tos,sizeof(tos));
+  assert(!err);
+  err=setsockopt(fd,IPPROTO_IP,TCP_NODELAY,&int1,sizeof(int1));
+  assert(!err);
+}
+
+int socket_accept(int listen_fd,function<void(int client_fd,string addr)> msgfunc) {
+  struct sockaddr sockaddr;
+  socklen_t socklen(sizeof(sockaddr));
+  const int client_fd(accept(listen_fd,&sockaddr,&socklen));
+  assert(client_fd>=0);
+  msgfunc(client_fd,sockaddr_string(&sockaddr,socklen));
+  socket_setopt(client_fd);
+  return client_fd;
+}
+
+int socket_connect(const string &host_port_str,unsigned retries) {
+  const char *cs(strrchr(host_port_str.c_str(),':'));
+  if (!cs)
+    fatal("Error parsing <host>:<port>: %s",host_port_str.c_str());
+  const string node(host_port_str.substr(0,cs-host_port_str.c_str()));
+  const string service(cs+1);
+  struct addrinfo hints={}; // designated initializer: error: missing initializer for member ... [-Werror=missing-field-initializers]
+  hints.ai_family=AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+  hints.ai_socktype=SOCK_STREAM;
+  struct addrinfo *result;
+  int err=getaddrinfo(node.c_str(),service.c_str(),&hints,&result);
+  if (err)
+    fatal("Error parsing node+service: <%s>:<%s>: %s",node.c_str(),service.c_str(),gai_strerror(err));
+  int fd;
+  struct addrinfo *rp;
+  for (unsigned retryno=0;retryno<1+retries;++retryno) {
+    if (retryno) {
+      warning("Sleeping 1 second for connect retry #%u/%u",retryno,retries);
+      err=sleep(1);
+      assert(!err);
+    }
+    for (rp=result;rp;rp=rp->ai_next) {
+      fd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
+      if (fd==-1)
+       continue;
+      if (connect(fd,rp->ai_addr,rp->ai_addrlen)==0)
+       break;
+      err=close(fd);
+      assert(!err);
+    }
+    if (rp==NULL)
+      warning("Could not connect(): <%s>:<%s>: %m",node.c_str(),service.c_str());
+    else
+      break;
+  }
+  freeaddrinfo(result);
+  if (rp==NULL)
+    fatal("Could not connect(), giving up");
+  socket_setopt(fd);
+  return fd;
+}
diff --git a/src/socket.h b/src/socket.h
new file mode 100644 (file)
index 0000000..19570ef
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef LIB_SOCKET_H
+#define LIB_SOCKET_H
+
+#include "streamfer.h"
+#include <unistd.h>
+#include <string>
+#include <functional>
+#include <cstring>
+#include <sys/socket.h>
+
+int socket_bind(string host_port_str);
+string sockaddr_string(const struct sockaddr *sockaddrp,socklen_t socklen);
+string socket_name(int socket_fd);
+int socket_accept(int listen_fd,function<void(int client_fd,string addr)> msgfunc);
+int socket_connect(const string &host_port_str,unsigned retries);
+
+#endif /* LIB_SOCKET_H */
diff --git a/src/streamfer-client.C b/src/streamfer-client.C
new file mode 100644 (file)
index 0000000..b2b9a61
--- /dev/null
@@ -0,0 +1,128 @@
+#include "safeio.h"
+#include "socket.h"
+#include "stringf.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <climits>
+
+static string get_string(FILE *f,const char *fn,const char *what) {
+  char buf[PATH_MAX];
+  char *got=fgets(buf,sizeof(buf),f);
+  if (got!=buf)
+    fatal("Error reading %s from %s: %m",what,fn);
+  char *s(strchr(buf,'\n'));
+  if (!s)
+    fatal("Stored %s in %s is not newline-terminated",what,fn);
+  *s=0;
+  assert(!s[1]);
+  return buf;
+}
+
+static void last_stored_write(const char *last_stored_fn,const string &filename,uint64_t offset) {
+  std::string last_tmp_fn(std::string(last_stored_fn)+".new");
+  int last_tmp_fd(open(last_tmp_fn.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0644));
+  if (last_tmp_fd==-1)
+    fatal("Error storing filename to %s: %m",last_tmp_fn.c_str());
+  write_safe(last_tmp_fd,filename.c_str(),filename.length());
+  write_safe(last_tmp_fd,'\n');
+  string offsetstr(stringf("%zu",(size_t)offset));
+  write_safe(last_tmp_fd,offsetstr.c_str(),offsetstr.length());
+  write_safe(last_tmp_fd,'\n');
+  if (close(last_tmp_fd))
+    fatal("Error closing %s: %m",last_tmp_fn.c_str());
+  if (rename(last_tmp_fn.c_str(),last_stored_fn))
+    fatal("Error renaming %s->%s: %m",last_tmp_fn.c_str(),last_stored_fn);
+}
+
+static string my_basename(const string &path) {
+  size_t slash(path.find_last_of('/'));
+  if (slash!=string::npos)
+    return path.substr(slash+1);
+  return path;
+}
+
+int main(int argc,char **argv) {
+  if (argc!=1+3)
+    fatal("streamfer-client <host>:<port> <pattern> <last storage file>");
+  int server_fd(socket_connect(string(argv[1]),0/*retries*/));
+  string pattern(argv[2]);
+  write_safe(server_fd,pattern);
+  string last_found;
+  const char *last_stored_fn(argv[3]);
+  FILE *last_stored_f(fopen(last_stored_fn,"r"));
+  uint64_t offset(0);
+  if (!last_stored_f) {
+    if (errno!=ENOENT)
+      fatal("Error opening filename from %s: %m",last_stored_fn);
+  } else {
+    last_found=      get_string(last_stored_f,last_stored_fn,"filename") ;
+    string offsetstr(get_string(last_stored_f,last_stored_fn,"offset"  ));
+    char *end;
+    errno=0;
+    unsigned long ul(strtoul(offsetstr.c_str(),&end,0));
+    if (errno||(end&&*end))
+      fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str());
+    offset=ul;
+    int gotint(fgetc(last_stored_f));
+    if (gotint!=EOF)
+      fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str());
+    int err(fclose(last_stored_f));
+    assert(!err);
+  }
+  write_safe(server_fd,last_found);
+  write_safe(server_fd,offset);
+  { struct stat statbuf;
+    string last_found_basename(my_basename(last_found));
+    if (stat(last_found_basename.c_str(),&statbuf)) {
+      static const struct timespec mtim_zero{};
+      write_safe(server_fd,mtim_zero);
+    } else
+      write_safe(server_fd,statbuf.st_mtim);
+  }
+  string last_got;
+  try {
+    read_safe(server_fd,last_got);
+  } catch (SafeIOError) {
+    fatal("SafeIOError reading from the server %s",argv[1]);
+  }
+  if (last_got.empty()) {
+    warning("No more files to transfer");
+    exit(EXIT_SUCCESS);
+  }
+  struct timespec mtim;
+  read_safe(server_fd,mtim);
+  int file_fd;
+  string file_name(my_basename(last_got));;
+  if (last_found==last_got) {
+    file_fd=open(file_name.c_str(),O_WRONLY);
+    if (file_fd==-1)
+      fatal("Error opening for write %s: %m",file_name.c_str());
+    off_t got(lseek(file_fd,offset,SEEK_SET));
+    if ((uint64_t)got!=offset)
+      fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got);
+  } else {
+    offset=0;
+    file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644);
+    if (file_fd==-1)
+      fatal("Error creating %s: %m",file_name.c_str());
+  }
+  last_stored_write(last_stored_fn,last_got,offset);
+  uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str()));
+  if (!transferred) {
+    struct stat statbuf;
+    int err(fstat(file_fd,&statbuf));
+    assert(!err);
+    if (memcmp(&mtim,&statbuf.st_mtim,sizeof(mtim))==0)
+      return EXIT_FAILURE;
+  }
+  offset+=transferred;
+  struct timespec mtim2[2];
+  mtim2[0]=mtim; // atime
+  mtim2[1]=mtim; // mtime
+  if (futimens(file_fd,mtim2))
+    fatal("Error setting timestamp of %s: %m",file_name.c_str());
+  if (close(file_fd))
+    fatal("Error closing %s: %m",file_name.c_str());
+  last_stored_write(last_stored_fn,last_got,offset);
+  return EXIT_SUCCESS;
+}
diff --git a/src/streamfer-server.C b/src/streamfer-server.C
new file mode 100644 (file)
index 0000000..5a0bd8a
--- /dev/null
@@ -0,0 +1,212 @@
+#include "safeio.h"
+#include "socket.h"
+#include "stringf.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <poll.h>
+#include <climits>
+#include <cstdlib>
+#include <csignal>
+
+// https://stackoverflow.com/a/8615450/2995591
+#include <glob.h> // glob(),globfree()
+#include <cstring> // memset()
+#include <vector>
+#include <string>
+
+static std::vector<std::string> cxxglob(const std::string pattern) {
+  glob_t glob_result;
+  memset(&glob_result,0,sizeof(glob_result));
+  int return_value=glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
+  if (return_value)
+    fatal("glob() failed with return_value %s",return_value);
+  vector<string> filenames;
+  filenames.reserve(glob_result.gl_pathc);
+  for (size_t i = 0; i < glob_result.gl_pathc; ++i)
+     filenames.push_back(string(glob_result.gl_pathv[i]));
+  globfree(&glob_result);
+  return filenames;
+}
+
+// FIXME: Use C++17
+static bool fd_is_open(const char *execname,const char *fn) {
+  const char slashproc[]("/proc");
+  DIR *dir(opendir(slashproc));
+  if (!dir)
+    fatal("Cannot opendir %s: %m",slashproc);
+  bool retval(false);
+  for (;;) {
+    errno=0;
+    const struct dirent *de=readdir(dir);
+    if (!de) {
+      if (errno)
+       fatal("Cannot readdir %s: %m",slashproc);
+      break;
+    }
+    if (!isdigit(de->d_name[0]))
+      continue;
+
+    char buf[PATH_MAX];
+    ssize_t got(readlinkat(dirfd(dir),stringf("%s/exe",de->d_name).c_str(),buf,sizeof(buf)));
+    if (got==-1||got==sizeof(buf))
+      continue;
+    buf[got]=0;
+    char *s=strrchr(buf,'/');
+    if (!s)
+      continue;
+    if (strcmp(s+1,execname)!=0)
+      continue;
+
+    string procpidfd(stringf("/proc/%s/fd",de->d_name));
+    DIR *fddir(opendir(procpidfd.c_str()));
+    if (!fddir)
+      fatal("Cannot opendir %s: %m",procpidfd.c_str());
+    for (;;) {
+      errno=0;
+      const struct dirent *de=readdir(fddir);
+      if (!de) {
+       if (errno)
+         fatal("Cannot readdir %s: %m",procpidfd.c_str());
+       break;
+      }
+      if (!isdigit(de->d_name[0]))
+       continue;
+      char buf[PATH_MAX];
+      ssize_t got(readlinkat(dirfd(fddir),de->d_name,buf,sizeof(buf)));
+      if (got==-1||got==sizeof(buf))
+       continue;
+      buf[got]=0;
+      if (strcmp(buf,fn)==0) {
+       retval=true;
+       break;
+      }
+    }
+    if (closedir(fddir))
+      fatal("Cannot closedir %s: %m",procpidfd.c_str());
+    if (retval)
+      break;
+  }
+  if (closedir(dir))
+    fatal("Cannot closedir %s: %m",slashproc);
+  return retval;
+}
+
+int main(int argc,char **argv) {
+  static struct sigaction sigchld;
+  sigchld.sa_handler=SIG_DFL;
+  sigchld.sa_flags=SA_NOCLDWAIT;
+  int err(sigaction(SIGCHLD,&sigchld,nullptr));
+  assert(!err);
+
+  if (argc!=1+2&&argc!=1+3)
+    fatal("streamfer-server [<listen-host>:]<listen-port> <prefix> [follow-fd-of-executable-basename]");
+  string prefix;
+  if (argc>=1+2&&*argv[2])
+    prefix=argv[2];
+  const char *execname(nullptr);
+  if (argc>=1+3)
+    execname=argv[3];
+  int listen_fd(socket_bind(argv[1]));
+  int client_fd;
+  for (;;) {
+    client_fd=socket_accept(listen_fd,[&](int client_fd,string addr) {
+      warning("%d:%s",client_fd,addr.c_str());
+    });
+    int child(fork());
+    assert(child!=-1);
+    if (!child)
+      break;
+    int err(close(client_fd));
+    assert(!err);
+  }
+  err=close(listen_fd);
+  assert(!err);
+
+  string pattern(read_safe_string(client_fd));
+  std::vector<std::string> matched(cxxglob(pattern));
+  for (size_t ix=0;ix<matched.size()-1;++ix) {
+    const std::string &a(matched[ix  ]);
+    const std::string &b(matched[ix+1]);
+    int err(strcmp(a.c_str(),b.c_str()));
+    if (err>=0)
+      fatal("glob: strcmp(\"%s\",\"%s\")=%d",a.c_str(),b.c_str(),err);
+  }
+  string last(read_safe_string(client_fd));
+  size_t lastix(SIZE_MAX);
+  for (size_t ix=0;ix<matched.size();++ix) {
+    const std::string &member(matched[ix]);
+    if (strcmp(last.c_str(),member.c_str())>0)
+      assert(lastix==SIZE_MAX);
+    else if (lastix==SIZE_MAX)
+      lastix=ix;
+  }
+  if (lastix==SIZE_MAX)
+    fatal("Requested too new file");
+  uint64_t offset;
+  read_safe(client_fd,offset);
+  struct timespec mtim;
+  read_safe(client_fd,mtim);
+  const string *fnp;
+  int file_fd=-1;
+  struct stat statbuf;
+  for (;lastix<matched.size();file_fd=-1,++lastix) {
+    fnp=&matched[lastix];
+    const string &fn(*fnp);
+    file_fd=open(fn.c_str(),O_RDONLY);
+    if (file_fd==-1) {
+      if (errno!=ENOENT)
+       fatal("Cannot open %s: %m",fn.c_str());
+      continue;
+    }
+    int err(fstat(file_fd,&statbuf));
+    assert(!err);
+    if (offset<(uint64_t)statbuf.st_size)
+      break;
+    static const struct timespec mtim_zero{};
+    if (memcmp(&mtim,&mtim_zero,sizeof(mtim))!=0&&memcmp(&mtim,&statbuf.st_mtim,sizeof(mtim))!=0)
+      break;
+    if (offset>(uint64_t)statbuf.st_size)
+      warning("File %s has transferred %zu < %zu which is its size",fn.c_str(),(size_t)offset,(size_t)statbuf.st_size);
+    if (lastix==matched.size()-1&&execname)
+      break;
+    err=close(file_fd);
+    assert(!err);
+    offset=0;
+  }
+  if (file_fd==-1) {
+    string empty("");
+    write_safe(client_fd,empty);
+    fatal("No more files to transfer");
+  }
+  const string &fn(*fnp);
+  const char *fn_canon(nullptr);
+  if (!prefix.empty()||execname) {
+    fn_canon=realpath(fn.c_str(),nullptr);
+    if (!fn_canon)
+      fatal("realpath %s: %m",fn.c_str());
+  }
+  if (fn!=last)
+    offset=0;
+  if (!prefix.empty()&&strncmp(prefix.c_str(),fn_canon,prefix.length())!=0)
+    fatal("prefix=\"%s\" realpath=\"%s\"",prefix.c_str(),fn_canon);
+  warning("%s @%zu",fn.c_str(),(size_t)offset);
+  write_safe(client_fd,fn);
+  write_safe(client_fd,statbuf.st_mtim);
+  off_t got(lseek(file_fd,offset,SEEK_SET));
+  assert((uint64_t)got==offset);
+  struct pollfd fds;
+  fds.fd=client_fd;
+  fds.events=POLLIN|POLLPRI|POLLRDHUP;
+  for (;;) {
+    transfer(file_fd,fn.c_str(),client_fd,"client fd");
+    if (!fn_canon||!fd_is_open(execname,fn_canon))
+      break;
+    int err(poll(&fds,1,1000/*ms*/));
+    if (err==-1)
+      fatal("poll client fd: %m");
+    if (err==1) 
+      fatal("poll client fd: revents=0x%x",fds.revents);
+    assert(err==0);
+  }
+}
diff --git a/src/streamfer.C b/src/streamfer.C
new file mode 100644 (file)
index 0000000..7f2e432
--- /dev/null
@@ -0,0 +1,51 @@
+#include "streamfer.h"
+#include <unistd.h>
+
+static void vwarning(const char *msg,va_list ap) {
+  int err=vfprintf(stderr,msg,ap);
+  assert(err>0);
+  fputc('\n',stderr);
+}
+
+void warning(const char *msg,...) {
+  va_list ap;
+  va_start(ap,msg);
+  vwarning(msg,ap);
+  va_end(ap);
+}
+
+void fatal(const char *msg,...) {
+  va_list ap;
+  va_start(ap,msg);
+  vwarning(msg,ap);
+  va_end(ap);
+  exit(EXIT_FAILURE);
+}
+
+uint64_t transfer(int from_fd,const char *from_fn,int to_fd,const char *to_fn) {
+  size_t total(0);
+  uint8_t buffer[0x10000];
+  size_t buffer_filled(0);
+  for (;;) {
+    if (buffer_filled==0) {
+      ssize_t got(read(from_fd,buffer+buffer_filled,sizeof(buffer)-buffer_filled));
+      if (got==-1)
+       fatal("Error reading %s: %m",from_fn);
+      if (got==0)
+       return total;
+      buffer_filled=got;
+      assert(buffer_filled<=sizeof(buffer));
+    }
+    size_t buffer_written(0);
+    while (buffer_written<buffer_filled) {
+      ssize_t got(write(to_fd,buffer+buffer_written,buffer_filled-buffer_written));
+      if (got==-1)
+       fatal("Error writing %s: %m",to_fn);
+      assert(got>0);
+      buffer_written+=got;
+      assert(buffer_written<=buffer_filled);
+    }
+    total+=buffer_filled;
+    buffer_filled=0;
+  }
+}
diff --git a/src/streamfer.h b/src/streamfer.h
new file mode 100644 (file)
index 0000000..d692137
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef STREAMFER_H
+#define STREAMFER_H 1
+
+#include <cstdarg>
+#include <cstdio>
+#include <cassert>
+#include <cstdlib>
+#include <cstdint>
+using namespace std;
+
+#define PRINTF(f,a) __attribute__((format(printf,f,a)))
+#define UNUSED __attribute__((unused))
+
+void warning(const char *msg,...);
+void fatal(const char *msg,...);
+
+uint64_t transfer(int from_fd,const char *from_fn,int to_fd,const char *to_fn);
+
+#endif // STREAMFER_H
diff --git a/src/stringf.h b/src/stringf.h
new file mode 100644 (file)
index 0000000..2e194fc
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef LIB_STRINGF_H
+#define LIB_STRINGF_H
+
+#include "streamfer.h"
+
+// template<class... Args>: format string is not a string literal (potentially insecure) [-Werror,-Wformat-security]
+
+static inline PRINTF(2,0) string vstringf_const(size_t sizemax,const char *fmt,va_list ap) {
+  string str(sizemax+1,0);
+  const int got=vsnprintf(&str[0],str.length(),fmt,ap);
+  if (got!=ssize_t(sizemax))
+    fatal("stringf*(): expected=%zu got=%d fmt=\"%s\" str=\"%s\"",sizemax,got,fmt,str.c_str());
+  str.resize(sizemax);
+  return str;
+}
+
+static inline PRINTF(2,3) string stringf_const(size_t sizemax,const char *fmt,...) {
+  va_list ap;
+  va_start(ap,fmt);
+  const string str(vstringf_const(sizemax,fmt,ap));
+  va_end(ap);
+  return str;
+}
+
+static inline PRINTF(1,2) string stringf(const char *fmt,...) {
+  // 189: for i in activetick.data/;do find $i -name "*.xz";done|xargs xz -dc|perl -lne 'BEGIN{$m=0;}if (length($_)>$m){$m=length $_;print $m;}'
+  static const int sizetry(192-1/*+1->malloc()*/);
+  string str(sizetry,0);
+  va_list ap;
+  va_start(ap,fmt);
+  const int got(vsnprintf(&str[0],sizetry,fmt,ap));
+  va_end(ap);
+  assert(got>=0);
+  if (got>=sizetry) {
+    str.resize(got+1);
+    va_start(ap,fmt);
+    const int got2(vsnprintf(&str[0],str.length(),fmt,ap));
+    va_end(ap);
+    assert(got2==got);
+  }
+  str.resize(got);
+  return str;
+}
+
+#endif /* LIB_STRINGF_H */