Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

export phase 1 on Sat Aug 22 17:48:21 CEST 2009

  • Loading branch information...
commit f6788c2853a11983a16f8d79c66586315bd94159 1 parent 8a94c56
Peter Jakobi authored
Showing with 27,915 additions and 60 deletions.
  1. +58 −0 admin.misc/tracefacility-example.aixtrace.pl
  2. +1 −0  admin.misc/tracefacility-example.aixtrace.pl.name
  3. +64 −0 admin.text/00_ADMIN_TEXT.phtml
  4. +1 −0  admin.text/00_ADMIN_TEXT.phtml.name
  5. 0  admin.text/@freshmeat.skip
  6. +109 −0 admin.text/selectlines
  7. +1 −0  admin.text/selectlines.name
  8. +1,213 −0 admin.text/sfre
  9. +1 −0  admin.text/sfre.name
  10. +58 −0 admin.text/sfre.sfrerc
  11. +400 −0 admin.text/unescape.pl
  12. +1 −0  admin.text/unescape.pl.name
  13. +29 −10 cli.backup/00_BACKUP.phtml
  14. +1 −1  cli.backup/00_BACKUP.phtml.name
  15. +1 −0  cli.backup/@incomplete.skip
  16. +345 −0 cli.backup/expanfind
  17. +1 −0  cli.backup/expanfind.name
  18. +3 −2 cli.conf/00_EXAMPLE_CONFIGURATIONS.phtml
  19. +1 −0  cli.conf/mc.name
  20. +22 −0 cli.conf/mc/bashrc
  21. +827 −0 cli.conf/mc/bindings
  22. +131 −0 cli.conf/mc/ini
  23. +37 −3 cli.firefox/00_FIREFOX.phtml
  24. +300 −0 cli.firefox/firefox
  25. +3 −0  cli.firefox/firefox.-example
  26. +1 −0  cli.firefox/firefox.-example.name
  27. +1 −0  cli.firefox/firefox.name
  28. +123 −0 cli.firefox/firefoxcookies
  29. +1 −0  cli.firefox/firefoxcookies.name
  30. +35 −0 cli.firefox/firefoxdelta
  31. +1 −0  cli.firefox/firefoxdelta.name
  32. +251 −0 cli.firefox/firefoxgrep
  33. +1 −0  cli.firefox/firefoxgrep.name
  34. +111 −0 cli.firefox/firefoxgrephtml
  35. +1 −0  cli.firefox/firefoxgrephtml.name
  36. +248 −0 cli.firefox/firefoxgrepsqlite
  37. +1 −0  cli.firefox/firefoxgrepsqlite.name
  38. +223 −0 cli.firefox/firefoxsessionlist
  39. +1 −0  cli.firefox/firefoxsessionlist.name
  40. +257 −0 cli.firefox/firefoxstdin
  41. +1 −0  cli.firefox/firefoxstdin.name
  42. +125 −0 cli.firefox/userChrome.css
  43. +1 −0  cli.firefox/userChrome.css.name
  44. +10 −5 cli.html/00_HTML-PROCESSING.phtml
  45. +1 −1  cli.html/00_HTML-PROCESSING.phtml.name
  46. +594 −0 cli.html/COLLECT
  47. +1 −0  cli.html/COLLECT.name
  48. +401 −0 cli.html/DIFF
  49. +1 −0  cli.html/DIFF.name
  50. +307 −0 cli.html/DIFFTREE
  51. +1 −0  cli.html/DIFFTREE.name
  52. +195 −0 cli.html/LINKS
  53. +1 −0  cli.html/LINKS.name
  54. +178 −0 cli.html/XLINKS
  55. +1 −0  cli.html/XLINKS.name
  56. +901 −0 cli.html/db_cgi.p
  57. +1 −0  cli.html/db_cgi.p.name
  58. +1 −0  cli.html/dir2html-modern-day-version.name
  59. +56 −0 cli.html/dir2html-modern-day-version/base.epl.raw
  60. +1 −0  cli.html/dir2html-modern-day-version/base.epl.raw.name
  61. +71 −0 cli.html/dir2html-modern-day-version/dir2array.pl
  62. +165 −0 cli.html/dir2html-modern-day-version/exportscript
  63. +1 −0  cli.html/dir2html-modern-day-version/exportscript.name
  64. +60 −0 cli.html/dir2html-modern-day-version/gitfilelist.epl.raw
  65. +1 −0  cli.html/dir2html-modern-day-version/gitfilelist.epl.raw.name
  66. +7 −0 cli.html/dir2html-modern-day-version/gitfilelist.head.epl.raw
  67. +1 −0  cli.html/dir2html-modern-day-version/gitfilelist.head.epl.raw.name
  68. +68 −0 cli.html/dir2html-modern-day-version/index.phtml.raw
  69. +1 −0  cli.html/dir2html-modern-day-version/index.phtml.raw.name
  70. +644 −0 cli.html/dir2html.pl
  71. +1 −0  cli.html/dir2html.pl.name
  72. +248 −0 cli.html/domain.codes.txt
  73. +1 −0  cli.html/domain.codes.txt.name
  74. +35 −0 cli.html/htmlcheck
  75. +1 −0  cli.html/htmlcheck.name
  76. +8 −0 cli.html/htmlcheck.tum.cfg
  77. +908 −0 cli.html/htmlchek.awk
  78. +1,505 −0 cli.html/htmlchek.pl
  79. +55 −0 cli.html/htmlchek.sh
  80. +47 −0 cli.html/htmlchkp.sh
  81. +29 −0 cli.html/remcr
  82. +1 −0  cli.html/remcr.name
  83. +347 −0 cli.html/sub.mime
  84. +1 −0  cli.html/sub.mime.name
  85. +10 −2 cli.list.grep/00_LISTPROCESSING.phtml
  86. 0  cli.list.grep/@freshmeat.skip
  87. +1 −1  cli.list.grep/Compact_pm/Grep.pm.name
  88. +111 −0 cli.list.grep/cat0
  89. +1 −0  cli.list.grep/cat0.name
  90. +307 −0 cli.list.grep/cat0par
  91. +1 −0  cli.list.grep/cat0par.name
  92. +307 −0 cli.list.grep/expansyn
  93. +7 −0 cli.list.grep/expansyn.cfg
  94. +1 −0  cli.list.grep/expansyn.cfg.name
  95. +1 −0  cli.list.grep/expansyn.name
  96. +101 −0 cli.list.grep/hlgrep
  97. +1 −0  cli.list.grep/hlgrep.name
  98. +11 −0 cli.list.grep/nl0
  99. +1 −0  cli.list.grep/nl0.name
  100. +12 −0 cli.list.grep/xargs0
  101. +1 −0  cli.list.grep/xargs0.name
  102. +15 −13 cli.list.rename/00_MASS-RENAME.phtml
  103. 0  cli.list.rename/@freshmeat.skip
  104. +1 −1  cli.list.rename/emv.name
  105. +9 −0 cli.list.rename/emvp
  106. +1 −0  cli.list.rename/emvp.name
  107. +41 −0 cli.list.rename/emvp0
  108. +1 −0  cli.list.rename/emvp0.name
  109. +38 −0 cli.list.rename/emvs
  110. +1 −0  cli.list.rename/emvs.name
  111. +50 −0 cli.list.rename/emvx
  112. +1 −0  cli.list.rename/emvx.name
  113. +1,132 −0 cli.list.rename/myrename
  114. +178 −0 cli.list.rename/myrename.mangle
  115. +1 −0  cli.list.rename/myrename.mangle.name
  116. +1 −0  cli.list.rename/myrename.name
  117. +65 −0 cli.list.rename/myrename.quote
  118. +1 −0  cli.list.rename/myrename.quote.name
  119. +1 −1  cli.list.tagls/00_TAGS_AND_COMMANDLINE.phtml
  120. 0  cli.list.tagls/@freshmeat.skip
  121. +905 −0 cli.list.tagls/tagls
  122. +1 −0  cli.list.tagls/tagls.name
  123. +87 −0 cli.list.tagls/taglsc
  124. +42 −0 cli.list.tagls/taglsc.cfg
  125. +1 −0  cli.list.tagls/taglsc.name
  126. +15 −0 cli.list.tagls/taglsgrep
  127. +1 −0  cli.list.tagls/taglsgrep.name
  128. +2 −5 cli.list.various/00_LISTPROCESSING2.phtml
  129. +92 −0 cli.list.various/USR1tail
  130. +1 −0  cli.list.various/USR1tail.name
  131. +8 −0 cli.list.various/args
  132. +1 −0  cli.list.various/args.name
  133. +99 −0 cli.list.various/busythreadtail.p
  134. +1 −0  cli.list.various/busythreadtail.p.name
  135. +78 −0 cli.list.various/ddnonblock
  136. +1 −0  cli.list.various/ddnonblock.name
  137. +12 −0 cli.list.various/ddsponge
  138. +1 −0  cli.list.various/ddsponge.name
  139. +2 −0  cli.list.various/echo2cat
  140. +1 −0  cli.list.various/echo2cat.name
  141. +132 −0 cli.list.various/line.print
  142. +1 −0  cli.list.various/line.print.name
  143. +179 −0 cli.list.various/line.replace
  144. +1 −0  cli.list.various/line.replace.name
  145. +250 −0 cli.list.various/pfind
  146. +1 −0  cli.list.various/pfind.name
  147. +19 −0 cli.list.various/pipe.filerequest.gtk2
  148. +1 −0  cli.list.various/pipe.filerequest.gtk2.name
  149. +92 −0 cli.list.various/pipe.select.curses
  150. +1 −0  cli.list.various/pipe.select.curses.name
  151. +84 −0 cli.list.various/pipe.select.gtk2
  152. +1 −0  cli.list.various/pipe.select.gtk2.name
  153. +68 −0 cli.list.various/randomcat
  154. +1 −0  cli.list.various/randomcat.name
  155. +2 −0  cli.list.various/randomecho
  156. +1 −0  cli.list.various/randomecho.name
  157. +154 −0 cli.list.various/range
  158. +1 −0  cli.list.various/range.name
  159. +37 −0 cli.list.various/revdatesort
  160. +1 −0  cli.list.various/revdatesort.name
  161. +37 −0 cli.list.various/sort.ip4
  162. +1 −0  cli.list.various/sort.ip4.name
  163. +217 −0 cli.list.various/teefork
  164. +1 −0  cli.list.various/teefork.name
  165. +276 −0 cli.list.various/xchange.pl
  166. +1 −0  cli.list.various/xchange.pl.name
  167. +5 −4 cli.mail/00_MAIL_SUPPORTSCRIPTS.phtml
  168. +3,025 −0 cli.mail/mygrepmail
  169. +1 −0  cli.mail/mygrepmail.name
  170. +9 −7 cli.processes/00_PROCESS+EVENT-HANDLING.phtml
  171. +1 −1  cli.processes/00_PROCESS+EVENT-HANDLING.phtml.name
  172. 0  cli.processes/@freshmeat.skip
  173. +134 −0 cli.processes/fwait
  174. +1 −0  cli.processes/fwait.name
  175. +12 −0 cli.processes/pwait
  176. +447 −0 cli.processes/waitcond
  177. +1 −0  cli.processes/waitcond.name
  178. +358 −0 cli.processes/waitcond.timeout
  179. +1 −0  cli.processes/waitcond.timeout.name
  180. +350 −0 cli.processes/zap
  181. +1 −0  cli.processes/zap.name
  182. +66 −0 cli.shell.functions/shell/00_ksh.func
  183. +127 −0 cli.shell.functions/shell/00_lib.func
  184. +1 −0  cli.shell.functions/shell/00_lib.func.name
  185. +67 −0 cli.shell.functions/shell/01_initialize.func
  186. +29 −0 cli.shell.functions/shell/02_customize.func
  187. +62 −0 cli.shell.functions/shell/LOAD
  188. +1 −0  cli.shell.functions/shell/LOAD.name
  189. +44 −0 cli.shell.functions/shell/auth.func
  190. +303 −0 cli.shell.functions/shell/cd.dyndirstack_bash.func
  191. +1 −0  cli.shell.functions/shell/cd.dyndirstack_bash.func.name
  192. +67 −0 cli.shell.functions/shell/cd.func
  193. +569 −0 cli.shell.functions/shell/cd.nico_cd_for_bash_and_ksh.func
  194. +1 −0  cli.shell.functions/shell/cd.nico_cd_for_bash_and_ksh.func.name
  195. +52 −0 cli.shell.functions/shell/color.func
  196. +81 −0 cli.shell.functions/shell/dict.func
  197. +53 −0 cli.shell.functions/shell/grep.func
  198. +9 −0 cli.shell.functions/shell/mail.func
  199. +29 −0 cli.shell.functions/shell/media.func
  200. +13 −0 cli.shell.functions/shell/misc.func
  201. +61 −0 cli.shell.functions/shell/promptcdtitledisplay.func
  202. +57 −0 cli.shell.functions/shell/text.func
  203. +21 −0 cli.shell.functions/shell/zz_finalize.func
  204. +40 −0 cli.various/00_VARIOUS.phtml
  205. +1 −0  cli.various/00_VARIOUS.phtml.name
  206. +91 −0 cli.various/ALARM
  207. +1 −0  cli.various/ALARM.name
  208. +4 −0 cli.various/ALERTCLR
  209. +1 −0  cli.various/ALERTCLR.name
  210. +47 −0 cli.various/ALERTOSD
  211. +1 −0  cli.various/ALERTOSD.name
  212. +40 −0 cli.various/date.before
  213. +1 −0  cli.various/date.before.name
  214. +11 −0 cli.various/df.minfree
  215. +1 −0  cli.various/df.minfree.name
  216. +54 −0 cli.various/diff.p
  217. +1 −0  cli.various/diff.p.name
  218. +67 −0 cli.various/fdupes/NOTES
  219. +1 −0  cli.various/fdupes/NOTES.name
  220. +30 −0 cli.various/fdupes/fdupes.expand
  221. +27 −0 cli.various/fdupes/fdupes.grepv
  222. +23 −0 cli.various/fdupes/fdupes.head
  223. +35 −0 cli.various/fdupes/fdupes.relink
  224. +15 −0 cli.various/fdupes/fdupes.size
  225. +7 −0 cli.various/fdupes/fdupes.sort.lex
  226. +7 −0 cli.various/fdupes/fdupes.sort.size
  227. +67 −0 cli.various/gunzipmemberextract
  228. +1 −0  cli.various/gunzipmemberextract.name
  229. +92 −0 cli.various/gzip.rfix
  230. +1 −0  cli.various/gzip.rfix.name
  231. +159 −0 cli.various/mt_dir
  232. +12 −0 cli.various/mt_dir.cfg
  233. +1 −0  cli.various/mt_dir.name
  234. +395 −0 cli.various/paradj
  235. +1 −0  cli.various/paradj.name
  236. +51 −0 cli.various/printing/lpr
  237. +1 −0  cli.various/printing/lpr.name
  238. +67 −0 cli.various/printing/myenscript
  239. +1 −0  cli.various/printing/myenscript.name
  240. +110 −0 cli.various/printing/mypsnup
  241. +1 −0  cli.various/printing/mypsnup.name
  242. +47 −0 cli.various/repeat
  243. +1 −0  cli.various/repeat.name
  244. +90 −0 cli.various/sanitize_filenames
  245. +1 −0  cli.various/sanitize_filenames.name
  246. +4 −0 cli.various/sleep.pl
  247. +1 −0  cli.various/sleep.pl.name
  248. +125 −0 cli.various/sleepuntil
  249. +1 −0  cli.various/sleepuntil.name
  250. +2,046 −0 cli.various/term.reset
  251. +1 −0  cli.various/term.reset.name
  252. +247 −0 cli.various/watchfloat
  253. +1 −0  cli.various/watchfloat.name
  254. +195 −0 cli.various/zappend.p
  255. +1 −0  cli.various/zappend.p.name
  256. +96 −0 hw.tablet/rotate.sh
  257. +1 −0  hw.tablet/rotate.sh.name
  258. +3 −2 vim/00_VIM.phtml
  259. +1 −1  vim/00_VIM.phtml.name
  260. +1 −0  vim/@incomplete.skip
  261. +102 −0 vim/pipe.vim
  262. +1 −0  vim/pipe.vim.name
  263. +31 −0 vim/vimless
  264. +208 −0 vim/vimscript
  265. +1 −0  vim/vimscript.name
  266. +13 −0 vim/vimscript.range
58 admin.misc/tracefacility-example.aixtrace.pl
View
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+# trace one command (assuming sufficently large trace buffer, etc)
+# USAGE: $0 [-e PERLREGEX] [-E PERLEXPR] /ABSOLUTE/PATH/TO/COMMAND COMMAND/ARGUMENTS
+
+# PJ20020128, roughly based on AIX Survivalguide p345.
+
+# BUGS:
+# - does not add -f/-F options to automatically select/follow forks
+# - does not do concurrent tracing
+# - does not do -p PID (use /usr/bin/sleep instead and grep for
+# processes of interest)
+
+$|=1;
+args: while (1) {
+ if ($ARGV[0] eq '-') { shift; last args}
+ elsif ($ARGV[0] eq '-E'){ shift; do "$ARGV[0]"; die "$@" if $@; shift}
+ elsif ($ARGV[0] eq '-e'){ shift; $REGEX=$ARGV[0]; eval{/$REGEX/}; die "$@" if $@; shift}
+ elsif ($ARGV[0] eq '-h'){ shift; &help; exit 1}
+ else{ last args; }
+}
+
+ $FILE_HOOKS="107,12E,130,15B,163,19C";
+$SYSCALL_HOOKS="101";
+ $KERNEL_HOOKS="134,135,139";
+ $HOOKS=join(",", $FILE_HOOKS, $SYSCALL_HOOKS, $KERNEL_HOOKS);
+ $REPORT_OPTS="ids=off,exec=on,pid=on,svc=on,pagesize=0,timestamp=3";
+ $REPORT_FLAGS="-v -x";
+
+# default, unless -e: look for lines starting with basename of program
+$REGEX="^".quotemeta($1) if not $REGEX and $ARGV[0]=~m@.*/(.*)@;
+
+$date=`date +%Y%m%d%H%M%S`; chomp $date;
+$TMP="/tmp/tracelog.$date";
+print "LOG: $TMP\n";
+print "\n";
+
+system("trace -a -d -j $HOOKS; trcon");
+system @ARGV; # tough, I am just too lazy to requote the args we got...
+system("trcstop");
+system("echo; trcrpt $REPORT_FLAGS -O $REPORT_OPTS > $TMP");
+
+system("head -12 $TMP");
+open(FH, "<$TMP") or die "no trace file";
+while(<FH>) {
+ next if /^trace/o;
+ next if /^trcstop/o;
+ next if $REGEX and not /$REGEX/o;
+ print
+}
+close FH;
+
+exit 0;
+
+########################################
+sub help { system "grep US"."AGE $0"; exit 1}
+
+
1  admin.misc/tracefacility-example.aixtrace.pl.name
View
@@ -0,0 +1 @@
+experimental - restricting aix tracefacility (AIX5+: use strace instead)
64 admin.text/00_ADMIN_TEXT.phtml
View
@@ -0,0 +1,64 @@
+[*
+ $ENV{__EMB_EXTRATITLE}="System-Administration / Editing";
+ $ENV{__EMB_COUNT}++;
+ if (1==$ENV{__EMB_COUNT}) {
+ Execute('base.epl');
+ } else {
+*]
+
+<p>Most of the stuff is too specific. Otherwise the stuff's waiting
+for a rewrite before being again (while hoping to stay retired, when a
+suitable group-maintained pendant is can be found).</p>
+
+<p>sfre is a slightly different case, it's an itch from previous
+projects, where being unable to ask (night-time, holidays, whatever)
+lead to time-consuming double-checks. Now wouldn't it be nice to find
+such information about already execute tasks, changes and
+modifications in a single location on the system in question? Note
+that sfre substitutes for a versioning system or a tool like cfengine
+only in the most trivial of cases, it's real purpose is to complement
+proper system management tools, and in addition provide a fail-safe
+for those dusty corners where use of proper tools like cfengine hasn't
+been implemented yet.</p>
+
+<p><i>I'm currently using sfre as $EDITOR, $VISUAL
+and as a replacement for vi (albeit with a call to the real vi later
+on); it's quite painful to be asked all the time for sane log messages
+when opening a vi without files or doing a git commit. This translates
+into a growing list of changes like implementing a command blacklist
+for file locking and journaling. For now it's still a bit too annoying
+to be used for accounts other than root.</i></p>
+
+
+<H3>Commands</H3>
+
+<ul>
+ <li><b>selectlines</b> , a version of the classic to select/deselect lines according
+ to embedded directives, while copying the sourcefile. Useful for trivial cases,
+ provided you switch to more suitable tools for heavier lifting.
+ <li><B>systemflightrecorderedit (sfre)</B> - sfre tries to sequence file
+ edits and augment group memory in concurrent file change scenarios like
+ multiple root users plus cron jobs. For each edit (or more generally
+ any arbitrary command given with the -e option) sfre logs a description
+ to a journal, f-locks the plain file arguments of the command and uses
+ changetrack to backup changes. It also supports lookup/diff of files
+ from a changetrack backupdir by date, revision number or symbolic name
+ allowing all options of the cat or diff commands (e.g. all GNU diff
+ options instead of just the small subset of options available via rcsdiff).<BR>
+ Dependencies: changetrack (changetrack.sf.net) and lock.pl/Flock.pm.
+ <li><B>unescape.pl</b> strips some of the line noise from script typescripts.
+ Modified version with prompt guessing, tries to remove e.g. vim session.
+ Using a tool like this may make typescripts somewhat more usable as a base
+ for documentation.
+</ul>
+
+<p>See also:</p>
+<ul>
+ <li>vimsh as helper for system documentation / documentation creation taken
+ from session transcripts: see the vimsh script from vim.org for a non-typescript
+ approach (non-interactive commands only, unless you use some kind
+ of pty splitter in combination with say urxvt -pty or screen; I've a demo
+ of both, but first I want to patch some more comfort into vimsh for this kind of usage).
+</ul>
+
+[*}*][# MUST BE AT EOF! #]
1  admin.text/00_ADMIN_TEXT.phtml.name
View
@@ -0,0 +1 @@
+introduction to sfre and other tools for config file 'editing'
0  admin.text/@freshmeat.skip
View
No changes.
109 admin.text/selectlines
View
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+
+# 1999XXXX PJ 0.1 jakobi@acm.org initial version
+# 20090729 PJ 0.2
+#
+# copyright: (c) 1999-2009 jakobi@acm.org, placed under GPL v3 or later
+my $version="0.2";
+
+# just a quick hack to select lines acc. to argument settings,
+# e.g. for extracting a specific configuration from a multi
+# configuration master file.
+
+# example data file X
+# alpha
+# beta ##SELECT!charly:$delta:echo
+# gamma
+# bravo ##SELECT:a ##select b
+# example usage:
+# ./selectlines -e '$delta="foxtrott"' charly X # deselects and does not print beta
+# ./selectlines -e '$delta="foxtrott"' not_matched X # does not print beta
+# ./selectlines -e '$delta="foxtrott"' foxtrott X # selects and does print beta
+# ./selectlines 'PATTERN|a|b' X # selects bravo (both a and b must be
+# # a part of selectlines argument)
+#
+# this line is true unless gamma ##SELECT!gamma:ALWAYS: - always is REQUIRED
+# whitespace is kept if SELECT ends with a :, otherwise trailing or leading whitespace is removed
+
+$VARCHAR='[a-z0-9A-Z_{}]'; # lower AND uppercase, allow {} to access ENV
+
+args: while (1) {
+ if ($ARGV[0] eq '-') { shift; last args; }
+ elsif ($ARGV[0] eq '-e'){ shift; $_=shift; eval($_); warn $@ if $@; }
+ elsif ($ARGV[0] eq '-c'){ shift; $_=shift; do $_; warn $@ if $@;}
+ elsif ($ARGV[0] eq '-v'){ shift; $verbose++; }
+ elsif ($ARGV[0] eq '-h'){ shift; &help; exit 1; }
+ elsif ($ARGV[0] eq '-o'){ shift; $outputfile=shift; }
+ else { last args; }
+}
+$select=shift;
+die "$0: no pattern??" if not $select=~/\S/;
+
+while(<>) {
+ # expand variables outside of the tags themselves.
+ while(s@##SELECTVAR:(\$$VARCHAR+)##@$1@gmee){;}
+ # expand any variables in SELECT TAGS
+ while(s@(##SELECT(?:(?:[!:]$VARCHAR*)*))([!:])\$($VARCHAR+)((?:(?:[!:][$]?$VARCHAR*)*)(?:$|\s))@do{"$1$2${$3}$4"}@gme){;}
+ $output.="# // $_" if $verbose;
+ # and print the line
+ while(s@(##SELECT(?:(?:[!:]$VARCHAR*)*))@@) {
+ $m=$1;
+ (not $m=~m@##SELECT((?:[!:]$VARCHAR*)*?)+($|\s)@m or # 1. print line if no selector
+ ( $m=~m@##SELECT((?:[!:]$VARCHAR*)*?):(?:$select)(:|!|\s|$)@m or # 2a. line is selected and
+ $m=~m@##SELECT((?:[!:]$VARCHAR*)*?):ALWAYS(:|!|\s|$)@m
+ ) and not
+ ( $m=~m@##SELECT((?:[!:]$VARCHAR*)*?)!(?:$select)(:|!|\s|$)@m or # 2b. selected line from 2a
+ $m=~m@##SELECT((?:[!:]$VARCHAR*)*?):NEVER(:|!|\s|$)@m # is not deselected
+ ) # success
+ ) or $_=""; # or failure
+ }
+ $output.=$_;
+}
+
+if ($outputfile) {
+ if ($output!~/\S/) {
+ die "$0: no output??"
+ } else {
+ open(FH, ">$outputfile") || die "$0: cannot open $outputfile";
+ print FH $output;
+ }
+ exit 0;
+} else {
+ print $output;
+ exit 0;
+}
+
+sub help {
+ print <<'EOF';
+selectlines [OPTIONS] PATTERN FILE ... > FILE
+
+selectlines copies input to output while selecting or deselecting
+lines containing a special PATTERN of the form ##SELECT([:!]STRING)+,
+where ! deselects a line if !PATTERN matches !STRING: for example
+:STRING1!STRING3!STRING4:STRING2 corresponds to (:STRING1 OR :STRING2)
+AND NOT !STRING3 AND NOT !STRING4) - we select the line if one of
+strings 1 or 2 occurs but neither strings 3 nor 4.
+
+:ALWAYS is always selected unless deselected. :NEVER is always
+deselected, regardless of other values. A line with multiple
+##SELECTs is only printed if all select. WARNING: negation is NOT
+boolean but rather a final grep -v! (see e.g. resolv.conf.master.pj)
+
+STRING must be a (possibly empty) alpha-numeric string or a
+alpha-numeric variable name beginning with $ ($ENV{LOGNAME} et al is
+also ok).
+
+Perl variables can also be used outside of the SELECT tags: e.g.
+##SELECTVAR:$ENV{LOGNAME}##.
+
+[does this need to be expanded to be able to invoke shell commands,
+too??]
+
+Options:
+ -c F load config file F
+ -e E eval expression E
+ -h help
+ -o F output to file F instead of STDOUT
+ -v verbosity
+EOF
+}
1  admin.text/selectlines.name
View
@@ -0,0 +1 @@
+copy config files from templates, with line(de)selection
1,213 admin.text/sfre
View
@@ -0,0 +1,1213 @@
+#!/usr/bin/perl
+
+# systemflightrecordereditor
+# multi-file (non-) interactive command or editor execution with
+# - logging to a journal,
+# - file locking, and
+# - backup and basic revision control of changed files
+
+# 20090729 PJ 0.1 jakobi@acm.org -- initial version
+#
+# copyright: GPL v3 or later
+our $version="0.1-still-lacks-a-bit-testing,but-safe-even-if-aliased-to-vi";
+our $debug=0;
+
+
+# Required dependencies:
+# - locking (both available at http://jakobi.github.com)
+# - lock.pl
+# - Compact_pm/Flock.pm (used by lock.pl)
+# - changetrack (e.g. from the debian package or directly
+# from changetrack.sf.net)
+
+
+# locking: use o_nolock to skip locking the command,
+# change lockcmd to '' to avoid locking for
+# journal and backup access.
+our $o_nolock=0; # set to one to skip flock
+# quiet (stripped if verbose); timeout for command less than 1h
+# retry interval 5s, report fact of being delayed ONCE, quiet otherwise
+our $lockcmd='lock.pl -1 -r 5 -q -t 3300';
+our $changetrackcmd='changetrack';
+
+
+# Bugs:
+#
+# - Q: what shall be the default when confronted with sudo? currently we
+# changetrack those files. but maybe it's saner to do have the user
+# do sudo 'sfre...' instead of doing sfre -e 'sudo...' and tracking
+# files from/for another user's backupdir?
+# at least the backup permissions are saner that way.
+# ==? turn this into an example note?==
+#
+# To be implemented/changed:
+#
+# - allow explicit filelist file to include in @files if existing
+# - DO add contents to filesN, this list could be e.g. generated by
+# a wrapper or say known modified files when e.g. invoking ypmake
+# or similar stuff. options --add --addlist
+# - rejection lock/desc/journal command list
+# (esp. for things like blank vi or emv or from within GIT please...)
+# - rejection filename lists for inclusion into changetrackable files
+# - DO implement it as a skip hash (&action style), but check it
+# early during filesN selection.
+# - DO add an option to load a \n pattern list into %skip
+# --skip, --skiplist, --skiplist-regex
+# - reduce backups and questions for non-changable files
+# avoid changetracking any non-changable files
+# - DO: options --mode/--modeappend
+# (no)tty # previously o_notty
+# (no)ask # o_ask
+# (no)journal # o_nojournal
+# ---
+# (no)lock, # o_nolock
+# (no)version # o_noversion
+# (no)(version|lock|)unchange?able
+# allfilesifsudo' in editor and args
+# if encountered and true wrt editor/@ARGV:
+# set lock, version and reloop.
+# --> for word in mode: turn into hash, and for each mode unset
+# opposites then finally turn back into string
+# - DO: if command does not contain \bsu(do)?\b [$sudopat] AND
+# not (-w or -W dirof(file)) and not (-w or -W file),
+# [DONT: AND file has NOT CHANGED timestamps; this would require
+# a hidden copy to stash away... - DO NOT DO THIS], then
+# avoid including file into file1 / file3 / file4.
+# have a flag being part of mode to turn this on.
+# == above all files if sudo mode flag
+# - DO: have the deselected stuff as %rfilesN/@rfilesN,
+# to still be included in the journal with a RFILE: prefix.
+# - == these three points should do the trick.
+# avoid questions when only non-changable files
+# can we move-down the description query (but we use domain/user
+# from that for loading the config [not possible, adding a skip
+# on detecting vi -R however might work, or not, see configuration]!)?
+# [DO skip query before loading config, and place a o_message only
+# after loading the configs, see next point:]
+# - DO: adding an option to assert o_user / o_domain to be unchanged
+# might be the ticket. If set, skip generating o_user/o_domain
+# question, and thus also skip asking about o_message on o_writableonly
+# DO make this option -U
+# - == both points should do the trick.
+# - special handling of vi -R and empty vi invocations
+# - DO ask no message, maybe also avoid journaling if no o_message
+#
+# Ultimate wishlist:
+#
+# - actually modified files...
+# - now wouldn't it be nice to check all written files ptrace-style and
+# consider that the apps working file set... . More realistic: if there's
+# a wrapper logging the accessed files (but that's too late). We'd need
+# a dtrace-style trace that suspends the process, changetracks the
+# about-to-be-written/-deleted to file, then resumes. There's a published
+# example for solaris dtrace doing some kind of
+# safe-delete-allowing-for-undelete.
+# I don't know of a suitable ptrace example [beyond early basics like
+# fakeroot-ng; a FUSE style trick might also be a ticket to do something
+# similar; wasn't there a syscall tracer like that?]
+# - and contrary to strace, it should never hang and be utterly transparent
+# to the file (unless maybe setuid, in which case a switch should be
+# available to turn off things, and a second more restrictive sudopat
+# to do this automatically if we 'smell' a sudo 'hint' as the string su
+# or \bsudo\b being part of an argument or filename).
+
+
+# Notes:
+#
+# - ' #' in the command can suppress arguments and logging command output
+# (lack of command screening is intentional, see -examples).
+# - use of symlink as the script name also changes the implicit
+# filenames for the configuration files. Intended for customization.
+# - does not parse changetrack configurations (Q: which/how to guess)
+# to extract emails for notification; that's still the task for the
+# nightly cronjob.
+# If you want email notification also for manually changed and
+# changetracked files, use -d to use a different changetrack
+# repository; however by default, we use the standard location for
+# debian/ubuntu and skip email notification instead.
+
+# changetrack's use of RCS imports a few RCS peculiarities:
+# - changetrack and RCS is a bit painful due to the renaming to :-pathes
+# and RCS inistence to never cat a version directly from the repo
+# even rcsdiff cannot and insists on an existing base file. Worse,
+# rcsdiff goes out of its way to disallow gnudiffs -N to work...
+# looks like I need to do a sfrecat that tries to cat older versions...
+# incl. locking and stuff. See the --diff/--cat workaround
+# to feel the pain.
+# - symlinks --> treated as if it were a plain file
+# - dirs --> ignored by changetrack
+# (use --addversion "dir/*" for a single level if necessary)
+
+
+# -------------------------------------------------------------
+
+use strict;
+use vars;
+use warnings;
+
+$|=1;
+our $Me="sfre";
+our $start=time;
+
+our($o_verbose, $o_changetrack, $o_journaledit, $o_addoutput)=(0,0,0,0);
+our($o_nobackup, $o_notty, $o_nojournal)=(0,0,0);
+our($o_configfile, $o_message, $o_user, $o_domain, $o_ask)=("","","","",0);
+our($o_journalgrep, $o_journalecho)=("",0);
+our($o_backupdiff, $o_backupcat, $o_backupecho)=(undef,undef,0);
+our($o_echohistory,$o_echodir,$o_echoyesterday)=(0,0,0);
+
+# $lockcmd and $changetrackcmd are just the commands,
+# while $editorcmd may also be a command with arguments
+our($shellcmd, $editorcmd,$log)=("","","");
+
+our($o_hostname, $o_fqdn, $o_whoami);
+chomp($o_hostname=qx!hostname!);
+ $o_hostname=$ENV{HOSTNAME} if not $o_hostname;
+chomp($o_fqdn=qx!hostname -f!);
+ $o_fqdn=$o_hostname if not $o_fqdn;
+chomp($o_whoami=qx!whoami!);
+ $o_whoami=$ENV{LOGNAME} if not $o_whoami;
+
+$o_user=$ENV{SUDO_USER} if not $o_user;
+$o_user=$ENV{LOGNAME} if not $o_user;
+chomp($o_user=qx!whoami!) if not $o_user;
+$o_user=$< if not $o_user;
+$o_user=$ENV{SFREUSER} if $ENV{SFREUSER};
+$o_configfile=$ENV{SFRERC} if $ENV{SFRERC};
+$o_domain=$ENV{SFREDOMAIN} if $ENV{SFREDOMAIN};
+$o_domain=$ENV{HOSTNAME} if not $o_domain;
+$o_domain=$o_hostname if not $o_domain;
+$o_message=$ENV{SFREMESSAGE} if $ENV{SFREMESSAGE};
+$editorcmd=$ENV{SFREEDITOR} if $ENV{SFREEDITOR};
+
+$shellcmd="/bin/bash" if not $shellcmd or not -x $shellcmd;
+$shellcmd="/bin/ksh" if not $shellcmd or not -x $shellcmd;
+$shellcmd="/usr/bin/bash" if not $shellcmd or not -x $shellcmd;
+$shellcmd="/usr/bin/ksh" if not $shellcmd or not -x $shellcmd;
+$shellcmd=$ENV{SFRESHELL} if $ENV{SFRESHELL};
+die "# $Me: aborting - no shell - please set \$SFRESHELL\n" if not $shellcmd;
+
+
+our($backupdir, $journal)=("",""); # backupdir and system journal
+our(@journal)=(); # extra files to consider with journaledit (ignored otherwise)
+our(@o_addlock,@o_addversion)=(); # extra files ... for locking and versioning (ignored otherwise)
+our(@ARGVOPT)=();
+
+# hook variables for evaluation
+our(%action1,%action2,%action3,%action4)=(); # file specific actions: pattern => perlscrap
+our($preaction,$postaction)=("",""); # configuration defined global actions
+our(@files1,%files1,@files3,%files3,@files4,%files4)=(); # currently existing plainfiles + c/mtimes
+
+our($rc,$rc0,$rc1,$rc2,$rc3,$rc4)=(0,0,0,0,0,0); # collect to allow exit with the sum of rc
+
+
+# lock options and break delays
+our $lockoptjournal="-b 60 -r 1";
+our $lockoptbackup= "-b 300 -r 1";
+our $lockoptcommand="";
+our $changetrackopt="-q -u";
+
+
+sub examples {
+
+&checkrequirements;
+
+ warn <<EOF;
+
+On required programs to run sfre:
+
+This script requires changetrack and lock.pm (if you don't see a
+warning above, you're probably fine, unless lock.pl -f cannot find its
+own Compact_pm/Flock.pm implementation).
+
+
+Usage scenario / comparing sfre to revision control:
+
+It isn't. Or rather it is. A basic single-branch, non-release,
+no-checkout, no-revision-locking versioning setup not allowing for
+off-site/offline remote use. Offering far less features than the RCS
+sfre uses.
+
+sfre's main idea is keeping a journal of e.g. administrators' actions
+in a multi-administrator group to allow quickly to pinpoint recent
+changes to a system involving specific files or services, with more
+interesting revision control (or versioning packaging of components)
+being done outside of sfre, with sfre just noticing and archiving
+newly installed file versions.
+
+That said, if you stash away a backup with a special suffix instead of
+naming release revisions, in a pinch, it should substitute a real
+version control system well enough for e.g. SOHO or single user
+development with mostly independent files.
+
+
+Usage scenario 2 / on security
+
+sfre is NOT a security tool. It is merely a tool to augment group
+memory. As such, having a command contain a '#' in the wrong place can
+disallow the command to see part of its arguments, but it will also
+suppress appending the command output to the journal (by commenting
+out the tee to the log). On the other hand, this very placement may be
+extactly the correct position while debugging some problem. Therefore,
+sfre does not make any effort to screen commands.
+
+Consider a sudo-cum-script whitelist approach if you require a secure
+and restricted environment. Given sfre's use of multiple configuration
+files, you probably do not wish to run sfre in this environment;
+also given the restriction on possible actions and the available logs,
+there's little need for sfre.
+
+
+On using sfre to wrap all interactive editor invocations
+
+The following example executes sfre instead of vi, but passes all
+options and arguments on to vi (Bourne-shell and descendants, assuming
+/usr/bin/vi as location).
+
+ if [ -n "\$PS1" ]; then
+ alias vim=vi
+ alias vi='sfre -e /usr/bin/vi --'
+ EDITOR=sfre
+ VISUAL=sfre
+ fi
+
+
+On cron, changetrack, sfre (also skipped changetrack email):
+
+If you want changetrack to send email notifications from e.g. a
+cronjob NOT using sfre, then keep sfre and changetrack backupdirs
+separate (e.g. SFREOPT="-d /var/lib/sfre").
+
+This currently is only an issue for root, as both changetrack hourly
+cronjobs and sfre both use /var/lib/changetrack as backupdir, leading
+to sfre updating the backups before the cronjob sees the change, thus
+resulting in skipped email notifications.
+
+Note that for this shared use, the changetrack cronjobs need to be
+modified to honor locking with procmail's lockfile:
+ f=/var/lib/changetrack/.locked; lockfile -s10 -l600 \$f
+ <other statements>
+ rc=\$?; rm \$f
+or with lock.pl
+ f=/var/lib/changetrack/.locked; <PATH>/lock.pl -t 3300 -r 10 -b 600
+ <other statements>
+ rc=\$?; rmdir \$f
+
+
+On locking (-> defaults can be changed by configurations):
+ lockcmd $lockcmd
+ lockoptbackup $lockoptbackup
+ lockoptcommand $lockoptcommand
+ lockoptjournal $lockoptjournal
+
+Locking aborts the attempted command by default after waiting for 3300
+sec (\$lockcmd), by default without attempting to break locks (type
+flock). For journal access (type flock), logging INTENT and COMMIT
+each breaks earlier locks after 120 seconds. For backup (type mkdir),
+the lock is broken after 300 seconds. Lock.pl defaults to non-greedy
+locking, add -g to retain all claimed locks when waiting for the yet
+unclaimed ones.
+
+
+Accessing the journal records with -jedit/-jgrep:
+
+The journal consists of paragraphs delimited by newlines. Each entry
+consists of an email-style header, followed by "File:" entries giving
+any currently existing file from the arguments, and optionally by the
+quoted command output.
+
+ Type: COMMIT
+ From: jakobi
+ Date: 2009-08-16 18:13:55
+ Domain: anuurn
+ Command: ls
+ Command-Args: -ld /etc/hosts
+ Start-Date: 2009-08-16 18:12:54
+ Message-ID: <COMMIT:jakobi.1000.:20090816181355\@anuurn.compact>
+ Message: /etc/hosts
+ File: /etc/hosts
+ Output: captured -e command output follows.
+ > ls:
+ > -rw-r--r-- 1 root root 754 2009-08-13 09:52 /etc/hosts
+
+To search the journal get a report by date, use the --grep option;
+however that is also just
+ perl -00ne 'print if /REGEX/' JOURNALFILE
+
+Do lengthy journal edits outside of the locking: -jedit and save a
+copy, clear the file, save and quit), edit at leisure, then -jedit and
+insert the edited version back at the beginning of the journal, save
+and quit.
+
+
+On configuration and hook variables for file-specific actions
+
+At the end of the script, you find a commented example configuration
+file, that also includes some actions. Look at the hash keys and
+invoke sfre with files matching those keys and experiment.
+
+Note that invoking the script using a symlink, you also change some of
+the names of the implicit configuration files we try to load:
+ - user request with -c, otherwise
+ - one or more of
+ /etc/.sfrerc
+ \$0.sfrerc
+ \$0.sfrerc.{\$o_domain,\$o_user}
+ \$0.sfrerc.{\$o_domain.\$o_user,\$o_user.\$o_domain}
+ ~/.sfrerc
+
+This should allow easy configuration of task specific (symlink) or
+host/user specific modifications, without requiring too much perl
+within the configuration files.
+
+The script also honors exported shell variables as initial default
+values (unless null):
+ - SFREDOMAIN -- --domain (default hostname)
+ - SFREMESSAGE -- -m (no default session description)
+ - SFREUSER -- -u (default \$LOGNAME)
+
+ - SFREEDITOR -- EDITOR to use, e.g. vi (also -e)
+ - SFREOPT -- prepended to args after splitting on (?<!\\\\)\\s
+ (avoid quotes except to denote the empty string)
+ - SFRERC -- configuration file to use (also -c)
+ - SFRESHELL -- /bin/bash or /bin/ksh (should support pipefail)
+
+
+Alternatives to sfre:
+
+ - a real version control system, if you can guarantee all group
+ members to always checkin/checkout the running version on each
+ and every change even if changing only a single line for a
+ quick test. Which includes indirect calls like crontab -e, or
+ scripted changes of e.g. configuration files which ideally should
+ also be tracked or wrapped with sfre.
+
+ - sudo with script and maybe strace -efile or some other syscall
+ tracer. In contrast to sfre, this approach also allows for
+ shared restricted or even jailed root setups. Depending on the
+ repository locations, sfre might be usable as part of such a
+ concept. Otherwise a different way must be devised to obtain
+ command and change file set for backup and journaling. With
+ a strongly nailed down whitelisted sudo and security concept,
+ the number of involved files per accepted task however should
+ small enough to not require a kitchensink-approach like sfre.
+
+
+EOF
+}
+
+sub usage {
+
+ &checkrequirements;
+ warn <<EOF;
+
+systemflightrecordereditor - a tool to support group memory and
+ to avoid stepping on each other's
+ and everybody's cronjobs' toes
+
+ - by forcing a meaningful description
+ - by logging into a journal
+ - by f-locking mentioned files
+ - by archiving changed files
+
+$Me [OPTIONS] -- <EDITOR-ARGUMENTS AND FILES>
+
+version: $version.
+
+This script wraps a command (\$EDITOR by default) with locking and
+versioning for plain files (changetrack/RCS is the only option for
+now). In addition it updates journal with a short session description
+by the user (including the set of files involved and optionally also
+the command output).
+
+If part of the description is missing and a tty is available, the
+script gives the user a change to provide a complete description of
+his action. By providing a configuration file, sfre can be asked to
+automatically perform pre and post action depending on files or
+session description.
+
+The implementation is safe for concurrent use by multiple users and
+cron-jobs incl. a multiple root scenario. sfre can be used to wrap
+your default editor in interactive shells, see --examples. It can also
+wrap arbitrary commands like cronjobs, protect them by locking and
+journal/backup their changes.
+
+By default, sfre will abort the command after waiting for nearly an
+hour, without trying to break locks (see \$lockcmd).
+
+For more information, examples incl. configuration examples:
+ - say --examples
+ - take a peek at the comments in the source or
+ the configuration file example at the end of the script
+
+
+Options:
+ -h --help -- help; further information with --examples
+ -v --verbose -- verbosity
+ -- -- last option
+
+The type for the second argument for some of the following options
+is one of F(ile), D(irector), R(egex), or (S)strings.
+
+Options for configuration and commands
+ -c F --cfg -- load config file F
+ -d D --dir -- directory to place backups in
+ -e S --command -- run command S instead of \$EDITOR
+ -j F --journal -- journal to log to (:-separated)
+
+Options for the journal
+ -J --addoutput -- include command output in journal
+ -m S --message -- session description for the journal
+ -u S --user -- username for the journal
+ --ask -- always ask the user to confirm the description
+ --domain S -- set 'logical domain' (default is hostname)
+ --notty -- do not ask for missing description
+
+Ooptions for locking and versioning
+ -C --changetrack -- use changetrack
+ --addlock F -- also lock file F before running command
+ --addversion F -- also track this file (also allows "dir/*")
+ --nolock -- no flocking of file arguments
+ --noversion -- no backups
+
+Accessing journal and versions (if pathes are set within config
+files, consider providing -u and --domain as well)
+ --cat [S] -- both commands use RCS co with S being either
+ --diff [S] -d<DATE> or -r<revision number or name>,
+ with all arguments being passed to the
+ system's cat or diff command, the last
+ being the file of interest. Without S,
+ both default to the most recent version
+ in changetrack's RCS backup.
+ --echo -- print the RCS file name (not checked out)
+ --echod(ir) -- print changetrack's backupdir
+ --echoh(istory) -- print changetrack's .history filename
+ --echoy(esterday) -- print changetrack's .yesterday filename
+ --jecho -- print journal path
+ --jedit -- edit the journal (you've 60s before
+ other instances may break the lock)
+ --jgrep R -- grep the journal (perl expression for
+ a simple perl -00ne 'print if /R/')
+ --nojournal -- no journal (also set by --jedit/--jgrep
+ and --cat/--diff)
+
+EOF
+}
+
+
+if ($ENV{SFREOPT}) {
+ my @tmp=split /(?<!\\)\s+/, $ENV{SFREOPT};
+ for(reverse @tmp) {
+ s/^(["'])\1$//;
+ unshift @ARGV, $_;
+ }
+}
+while($_=$ARGV[0],defined $_){
+ my($t);
+ /^-(c|-?cfg)$/o and do{shift; $o_configfile=shift; push @ARGVOPT, $_, $o_configfile; next};
+ /^-(C|-?changetrack)$/o and do{shift; $o_changetrack=1; push @ARGVOPT, $_; next};
+# /^-(-?diff)$/o and do{shift; $o_diff=0; $o_diff=shift if $ARGV[0]!~/^-/; next};
+ /^-(e|-?command)$/o and do{shift; $editorcmd=shift; push @ARGVOPT, $_, $editorcmd; next};
+ /^-(d|-?dir)$/o and do{shift; $backupdir=shift; push @ARGVOPT, $_, $backupdir; next};
+ /^-(j|-?journal)$/o and do{shift; $journal=shift; push @ARGVOPT, $_, $journal; next};
+ /^-(-?jedit)$/o and do{shift; $o_journaledit=1; next};
+ /^-(-?jgrep)$/o and do{shift; $o_journalgrep=shift; next};
+ /^-(-?jecho)$/o and do{shift; $o_journalecho=1; next};
+ /^--?echod(ir)?$/o and do{shift; $o_echodir=1; next};
+ /^--?echoh(istory)?$/o and do{shift; $o_echohistory=1; next};
+ /^--?echoy(esterday)?$/o and do{shift; $o_echoyesterday=1; next};
+ /^-(-?echo)$/o and do{shift; $o_backupecho=1; next};
+ /^-(-?cat)$/o and do{shift; $o_backupcat=0; $o_backupcat=shift if $ARGV[0]!~/^-/; next};
+ /^-(-?diff)$/o and do{shift; $o_backupdiff=0; $o_backupdiff=shift if $ARGV[0]!~/^-/; next};
+ /^-(-?ask)$/o and do{shift; $o_ask=1; push @ARGVOPT, $_; next};
+ /^-(m|-?message)$/o and do{shift; $o_message=shift; push @ARGVOPT, $_, $o_message; next};
+ /^-(u|-?user)$/o and do{shift; $o_user=shift; push @ARGVOPT, $_, $o_user; next};
+ /^-?-addoutput$/o and do{shift; $o_addoutput=1; push @ARGVOPT, $_; next};
+ /^-?-notty$/o and do{shift; $o_notty=1; push @ARGVOPT, $_; next};
+ /^-?-nolock$/o and do{shift; $o_nolock=1; push @ARGVOPT, $_; next};
+ /^-?-nojournal$/o and do{shift; $o_nojournal=1; push @ARGVOPT, $_; next};
+ /^-?-nobackup$/o and do{shift; $o_nobackup=1; push @ARGVOPT, $_; next};
+ /^-?-domain$/o and do{shift; $o_domain=shift; push @ARGVOPT, $_, $o_domain; next};
+ /^-?-addlock$/o and do{shift; push @o_addlock,$t=shift; push @ARGVOPT, $_, $t; next};
+ /^-?-addversion$/o and do{shift; push @o_addversion,$t=shift;push @ARGVOPT, $_, $t; next};
+ /^-(h|-?help|\?)$/o and do{&usage; exit 1};
+ /^--?examples?$/o and do{&examples; exit 1};
+ /^-(v|-?verbose)$/o and do{shift; $o_verbose++; push @ARGVOPT, $_; next};
+ /^--?$/o and do{shift; last};
+ last;
+}
+for(@ARGVOPT) { $_="" if not defined $_; }
+@o_addlock=grep {defined $_ and /./} @o_addlock;
+@o_addversion=grep {defined $_ and /./} @o_addversion;
+
+
+
+# stage 0-get info+cfg ---------------------------------------
+
+# where to log and diff to?
+$journal ="$ENV{HOME}/.JOURNAL" if not $journal and $> == $< and $>;
+$journal ="/etc/JOURNAL" if not $journal;
+
+$backupdir="$ENV{HOME}/.sfre_backup" if not $backupdir and $> == $< and $>;
+$backupdir="/var/lib/changetrack" if not $backupdir and $o_changetrack;
+$backupdir="/var/lib/sfre_backup" if not $backupdir;
+
+
+# force user to complete journal message (unless -notty)
+if ($o_nojournal or
+ $o_journaledit or $o_journalgrep or $o_journalecho or
+ defined $o_backupcat or defined $o_backupdiff or $o_backupecho or
+ $o_echohistory or $o_echoyesterday or $o_echodir) {
+ # we skip journaling in this invocation, so don't ask the user
+ # if we need user/domain to have some config file setting the
+ # correct backup location, then better hope for the user providing
+ # -u / --domain on his own. Or -j / -d instead, which are the only
+ # values we're really interested with above options, besides maybe
+ # @journal.
+} else {
+ if ( not $o_user or
+ not $o_domain or
+ not $o_message) {
+ if (not $o_notty) {
+ ($o_user,$o_domain,$o_message)=getanswer(
+ "Please enter/confirm: ", sprintf("Username: %-20s",$o_user), $o_user,
+ sprintf("Domain: %-20s",$o_domain), $o_domain,
+ sprintf("Message: %-20s",$o_message), $o_message);
+ }
+ }
+}
+
+chomp($o_user=qx!whoami!) if not $o_user;
+$o_user=$< if not $o_user;
+
+$o_domain="nowhere" if not $o_domain;
+$o_message="noplan" if not $o_message;
+$o_user="noone" if not $o_user;
+
+
+# read configuration, overlaying defaults if no user request
+# to avoid loading a config, just say -c /dev/null
+if (not $o_configfile or not -f $o_configfile) {
+ $o_configfile="";
+ for("/etc/.sfrerc", "$0.sfrerc", "$0.sfrerc.$o_domain",
+ "$0.sfrerc.$o_user", "$0.sfrerc.$o_domain.$o_user", "$0.sfrerc.$o_user.$o_domain",
+ "$ENV{HOME}/.sfrerc"){
+ do $_ or die "# $Me: problem with config file $_ $@" if -f $_;
+ }
+} elsif ($o_configfile and $o_configfile ne "/dev/null") {
+ do $o_configfile or die "# $Me: problem with config file $_ $@";
+} else {
+ warn "# $Me: skipping configuration as requested\n" if $o_verbose;
+}
+
+# try to avoid accidental recursion
+$editorcmd=$ENV{EDITOR} if not $editorcmd and $ENV{EDITOR}!~/sfre|systemflightrecorder/;
+$editorcmd=$ENV{VISUAL} if not $editorcmd and $ENV{VISUAL}!~/sfre|systemflightrecorder/;
+$editorcmd="/bin/vi" if not $editorcmd and -x "/bin/vi";
+$editorcmd="/usr/bin/vi" if not $editorcmd and -x "/usr/bin/vi";
+$editorcmd="/usr/bin/nano" if not $editorcmd and -x "/usr/bin/nano";
+if (not $editorcmd) {
+ my($tmp);
+ chomp($tmp=`which vi`);
+ $ENV{file}=$tmp;
+ $ENV{file1}=$Me;
+ if ($tmp and not -B $tmp) {
+ system "grep -e '$Me' -e sfre -e systemflightrecorder \$file >/dev/null 2>&1 ||".
+ "fgrep -e \$file1 \$file >/dev/null 2>&1";
+ $editorcmd="$tmp" if $?; # otherwise probably recursion
+ } else {
+ $editorcmd="$tmp" if $tmp;
+ }
+}
+if (not $editorcmd) {
+ die "# !!! $Me: cannot determine sane editor (that isn't $Me)\n";
+}
+
+
+if ($backupdir eq "/var/lib/changetrack" and -r "/etc/cron.hourly/changetrack") {
+ system "fgrep .locked /etc/cron.hourly/changetrack 2>&1 >dev/null";
+ if ($?) {
+ $backupdir="/var/lib/sfre";
+ warn "\n** $Me: forced change of backupdir as the hourly changetrack cron\n".
+ "** seems to lack locking. Using /var/lib/sfre instead of\n".
+ "** /var/lib/changetrack. See note in --examples for more info!\n".
+ "** Consider separate repositories if you require changetrack cron\n".
+ "** to send notification emails for _ALL_ changes, including\n".
+ "** those done with sfre (tradeoff disk-space vs. email-notification).\n\n";
+ }
+}
+
+
+&checkrequirements; # give user a hint in case of missing lock.pl/changetrack
+
+
+# stage 0-commands-and-exit -------------------------------
+# commands that short-circuit processing
+# (or redefine too many options at once like -jedit :))
+
+if ($o_journaledit) {
+ # reinvoke happens w/o -jedit option (I want to lock the journal...)
+ push @ARGV, $journal, @journal;
+ @ARGVOPT=grep {not /^-addoutput$/} @ARGVOPT;
+ push @ARGVOPT,"--nojournal";
+} elsif ($o_journalgrep) {
+ $ENV{match}=$o_journalgrep;
+ exec "perl","-00ne",'print if /$ENV{match}/',$journal,@journal;
+} elsif ($o_journalecho) {
+ print "$journal\n"; exit 0;
+} elsif (defined $o_backupcat or defined $o_backupdiff or
+ $o_backupecho or $o_echohistory or $o_echoyesterday or $o_echodir) {
+ my $file=pop @ARGV;
+ my $ans=&getrcsfile($file, @ARGV);
+ exit $ans if $ans=~/^\d+$/;
+ print $ans;
+ exit 0;
+}
+
+
+
+# stage 1-lock-and-reinvoke -----------------------------------
+&actions(\%action1,@ARGV); # allow for preparing non-existing files
+ # !!! can be invoked TWICE, once by sfre,
+ # !!! and once by the sfre child of lock.pl
+
+# get the list of already existing files for locking
+for(@ARGV) {
+ my @stat=stat $_;
+ if (-f _) {
+ push @files1, $_;
+ $files1{$_}=$stat[9].":".$stat[10];
+ }
+}
+
+# and lock by reinvoking ourselves AT MOST ONCE,
+# that way unlocking is automatic
+if (not $ENV{SFRELOCKED} and $lockcmd and not $o_nolock and @files1) {
+ my @cmd;
+ $lockoptcommand.="-v -v " if $o_verbose>1;
+ warn "# $Me: reinvoking $0 with $lockcmd\n" if $o_verbose>1;
+ $ENV{SFRELOCKED}=1;
+ push @ARGVOPT, "-u", $o_user, "--domain", $o_domain, "-m", $o_message, "--";
+ @cmd=($shellcmd, "-c", $lockcmd." -f ".$lockoptcommand.' ${1:+"$@"}', "${Me}lock",
+ @files1, @o_addlock, "--", $0, @ARGVOPT, @ARGV);
+ warn "# $Me: reinvoking as ", join(" ",map({"'".sq($_)."'"} @cmd)), "'\n" if $o_verbose>1;
+ exec @cmd;
+}
+
+# moved down, as the -e command is allowed to fail,
+# which is already checked for.
+$lockcmd=~s/ -q / / if ($o_verbose or $debug) and $lockcmd=~/lock\.pl/;
+
+if($o_verbose) {
+ print "# -j journal: $journal\n";
+ print "# -d backupdir: $backupdir\n";
+}
+
+
+
+# stage 2-preprocess ---------------------------------------
+# from now on, logging is permitted, but we're possibly
+# a-child-process-of-lock instead of being the original running
+# sfre instance.
+
+$preaction and do{eval $preaction; warn "# !! $Me: \$preaction failed: $@" if $@};
+&actions(\%action2,@files1,@ARGV); # allow for preparing non-existing files
+&journal("INTENT", $start, 0, @files1); # this may block for upto 5 minutes, so go early
+&versioning(@files1,@o_addversion);
+warn "# sfre preprocessing problems: $rc1\n" if $rc1;
+
+
+
+# stage 2-command -----------------------------------------
+my($tmpfile,$cmd);
+# the script command either sticks around interactively incl. ignoring exit
+# or makes a mess of quoting CMD options by only allowing -c CMD
+# -> NO GO.
+$cmd=$editorcmd . ' ${1:+"$@"} ';
+if ($o_addoutput) {
+ chomp($tmpfile=`tempfile -d /tmp -p sfre.`);
+ $cmd="set -o pipefail; { ".$editorcmd . ' ${1:+"$@"} ;} 2>&1 | tee -a ' . $tmpfile if $tmpfile;
+}
+
+#&debug_status;
+
+warn "# $Me: command ", join(" ", $shellcmd, "-c", $cmd, "${Me}cmd", @ARGV), "\n" if $o_verbose>1;
+system $shellcmd, "-c", $cmd, "${Me}cmd", @ARGV;
+$rc2=$?>>8;
+$log=""; $log=`cat $tmpfile;rm $tmpfile` if $o_addoutput;
+warn "# $Me: command returns $rc2\n" if $rc2 and $o_verbose;
+
+&debug_status;
+#die "# enough for now\n";
+
+
+
+# stage 3-postprocess modified files -----------------------
+for(@ARGV) { # get the lists for stages 3 and 4
+ my @stat=stat $_;
+ if (-f _) {
+ push @files4, $_;
+ $files4{$_}=$stat[9].":".$stat[10];
+ if (not $files1{$_} or $files4{$_} ne $files1{$_}) {
+ push @files3, $_;
+ $files3{$_}=$files4{$_};
+ }
+ }
+}
+&actions(\%action3,@files3);
+
+# allow changetrack to do its own job and check ALL files in stage 4
+
+
+
+# stage 4-postprocess --------------------------------------
+&actions(\%action4,@files4);
+&journal("COMMIT", $start, time, @files4);
+&versioning(@files4,@o_addversion);
+warn "# sfre postprocessing problems: $rc3\n" if $rc3;
+
+$rc=$rc1+$rc2+$rc3+$rc4;
+$postaction and do{eval $postaction; warn "# !! $Me: \$postaction failed: $rc): $@" if $@};
+
+&debug_status(0);
+#die "# enough for now\n";
+
+# return error or status of command
+exit $rc;
+
+
+
+# #####################################################################
+
+# ---------------------------------------------------------------------
+# sub routines
+# ---------------------------------------------------------------------
+
+
+sub actions { # arrayref, files...
+ my($perlscrap,%files)=();
+ my($actref, @files)=@_;
+ @files=grep {not $files{$_} and do{$files{$_}=1}} @files; # dedupe
+
+ # for each re, check all fails against the re, with $_ being the file
+ for my $re (sort keys %$actref){
+ $perlscrap=$actref->{$re};
+ for my $file (@files){
+ $_=$file;
+ if (/$re/) {
+ warn "# $Me: action match ($re : $file)\n" if $o_verbose>1;
+ eval($perlscrap);
+ warn "$ ! Me: action configuration error ($re : $file) $@" if $@;
+ $@="";
+ }
+ }
+ }
+}
+
+
+sub versioning {
+ # currently the great versioning system switch ...
+ # ... does know only changetrack, which may be for the
+ # best given that we'd also need switching
+ # --diff / --cat / --*echo* implementations as well
+ &versioning_changetrack;
+}
+sub versioning_changetrack { # changetrack
+
+ my(@files)=@_;
+ return if $o_nobackup or not @files;
+
+ # this however is sufficient reason to be finally allowed to panic...
+ die "!!! $Me: backupdir invalid\n" if not $backupdir or not -d $backupdir and not mkdir $backupdir;
+
+ my($lock,$rc,$t,$conf,$log);
+ $lock="$backupdir/.locked";
+ $log="$backupdir/.sfre.log";
+ $ENV{lock}=$lock;
+ $conf="$backupdir/.sfre.conf_ct.$$.$o_hostname";
+ $rc=0;
+
+ if ($lockcmd) {
+ system("$lockcmd $lockoptbackup \$lock");
+ $rc=$?>>8;
+ warn "# $Me: cannot lock backup - continue\n" if $rc;
+ #$lock=undef;
+ } else {
+ $lock=undef;
+ }
+
+ # QQ: parse some yet to detect changetrack config
+ # to determine email addresses to add to config?
+ # Skip - if we use 2 repos, the emails will happen
+ # at the cost of some disk space
+
+ # create configuration filelist
+ open(FH, ">", $conf) and
+ print FH join("\n",map({absname($_)} @files),"") and
+ close FH or
+ do{$rc=1; warn "# $Me: cannot write changetrack config\n"};
+
+ # run changetrack
+ $t="$o_user\@$o_domain: $o_message"; $t=~s/\n/ /g;
+ not $rc and do {
+ $ENV{log}=$log;
+ my @cmd=($shellcmd, "-c", $changetrackcmd." >\$log 2>&1 ".$changetrackopt." ".
+ "-c '". sq($conf)."' ".
+ "-d '". sq($backupdir)."' ".
+ "-m '". sq($t)."' ".
+ '${1:+"$@"}', "${Me}version_ct");
+ open (FH, ">>", $log);
+ print FH "# Me: changetracking on ", gettime(time), ": ", join(" ", @cmd,"\n");
+ print FH join("\n",map({absname($_)} @files),"\n");
+ close FH;
+ system(@cmd);
+ $rc=$?>>8;
+ };
+ warn "# $Me: changetrack error $rc - continue\n" if $rc;
+ unlink $conf;
+
+ rmdir $lock if $lock;
+ return $rc;
+}
+
+
+sub journal {
+ # log a message of "type" suitably dated to the journal, incl. the
+ # affected files. Multi-line $o_message descriptings are ok, subsequent
+ # lines will have prepended whitespace in the log. Each log entry is a paragraph.
+ return 0 if ($o_nojournal);
+ my($type, $start, $now, @files)=@_;
+ my($log0)=("");
+ $type="" if not defined $type;
+ $start=time if not $start;
+ $now=$start if not $now;
+
+ my $msgid=uc "$type"; $msgid=~s/\s.*//g;
+ $msgid.=":$o_whoami.$<.". ( $ENV{SUDO_USER} ? $ENV{SUDO_USER} : "" ) . ":".gettimeshort($now)."\@$o_fqdn";
+
+ my($ok)=(0);
+ $ENV{journal}=$journal;
+ ( $lockcmd ?
+ open(FH,"| $lockcmd --flock $lockoptjournal \$journal -- cat >> \$journal") :
+ open(FH,">>", $journal)
+ ) and
+ ++$ok or
+ warn "# $Me: cannot append to journal $journal\n";
+
+ # add an excess \n in case somebody adds notes to the journal by hand
+ if ($log and $o_addoutput) {
+ $log0=$log;
+ $log0=~s/(?<!\n)\z/\n/;
+ $log0=~s/^/ > /mg;
+ $log0="Output: captured -e command output follows.\n".$log0;
+ }
+ print FH "\n" .
+ headerize("Type: $type\n") .
+ headerize("From: $o_user\n") .
+ headerize("Date: ". gettime($now) ."\n") .
+ headerize("Domain: $o_domain\n") .
+ headerize("Command: $editorcmd\n") .
+ headerize("Command-Args: " . join(" ", map({"'".sq($_)."'"} @ARGV)) . "\n") .
+ headerize("Start-Date: ". gettime($start) ."\n") .
+ headerize("Message-ID: <$msgid>\n") .
+ headerize("Message: $o_message\n") .
+ join( "File: ", "", map({my $s=$_; $s=~s/\n/?/g; "$s\n"} @files)) .
+ $log0 .
+ "\n";
+ close FH and ++$ok or warn "# $Me: cannot append to journal $journal\n";
+
+ return 2==$ok ? 1 : 0;
+}
+
+
+# @ = getanswer($firstprompt, $prompt1, $default1, ...)
+#
+# guess usable tty and ask user for input in case of missing description items
+# if the user replies <CR>, just return the caller-provided 'current value'
+# Tries to short circuit in case of only one missing (null) value. The prompt
+# is assumed to already contain the current value if required as information
+# for the user.
+sub getanswer {
+ my($firstprompt)=shift;
+ $firstprompt="" if not defined $firstprompt;
+ $firstprompt.="\n" if $firstprompt and $firstprompt!~/\n\z/;
+
+ my($IN,$OUT,$INclose,$OUTclose);
+ if (-t main::STDOUT){
+ $OUT=\*main::STDOUT;
+ } elsif (-t main::STDERR) {
+ $OUT=\*main::STDERR;
+ } else {
+ open($OUT,">/dev/tty"); $OUTclose=1;
+ }
+ if (-t main::STDIN){
+ $IN=\*main::STDIN;
+ } else {
+ open($IN,"</dev/tty"); $INclose=1;
+ }
+
+ my($missing,$i,@prompt,@current);
+ ($missing,$i)=(-1,-1);
+ print $OUT $firstprompt if (@_);
+ while(@_){
+ $i++;
+ $_=shift; $_="" if not defined $_;
+ push @prompt,$_;
+ $_=shift; $_="" if not defined $_;
+ push @current,$_;
+ if ($_!~/\S/) {
+ $missing = -1==$missing ? $i : 99;
+ }
+ }
+
+ my($ans,@ans);
+ if(99>$missing and -1!=$missing and not $o_ask) {
+ # we have only one missing item, so
+ # attempt to just ask for this item,
+ # but also allow the user to check all
+ # items.
+ $ans=$prompt[$missing]; $ans=~s/\s*$//;
+ print $OUT "$ans [<STRING>/";
+ print $OUT "<E>dit all/" if $#prompt>0;
+ print $OUT "<CR>] ? ";
+ $ans=<$IN>; chomp($ans);
+ if ($ans=~/^E$/i) {
+ @ans=();
+ $missing=99;
+ } elsif ($ans!~/\S/) {
+ @ans=@current;
+ $missing=-1;
+ } else {
+ @ans=@current;
+ $ans[$missing]=$ans;
+ $missing=-1;
+ }
+ } else {
+ $missing=99;
+ }
+
+ if (99==$missing) {
+ my($prompt,$current);
+ for $i (0..$#prompt){
+ ($prompt,$current)=($prompt[$i],$current[$i]);
+ $prompt=$prompt."? " if $prompt!~/[\n:\.\?]\s{0,1}\z/;
+ print $OUT $prompt;
+ $ans=<$IN>; chomp($ans);
+ $ans=$current if $ans!~/[^\s\r]/;
+ push @ans, $ans;
+ }
+ }
+ close $IN if $INclose;
+ close $OUT if $OUTclose;
+ return @ans;
+}
+
+
+
+# small helpers -----------------------------------------------------
+
+sub sq{ # escape hack for single-quoting
+ my($tmp)=@_;
+ $tmp=~s/'/'"'"'/g;
+ return($tmp);
+}
+
+sub gettimeshort {
+ my @time=localtime($_[0]);
+ my $time=sprintf "%4d%02d%02d%02d%02d%02d", 1900+$time[5],1+$time[4],$time[3],@time[2,1,0];
+ return $time;
+}
+
+sub gettime {
+ my @time=localtime($_[0]);
+ my $time=sprintf "%4d-%02d-%02d %02d:%02d:%02d", 1900+$time[5],1+$time[4],$time[3],@time[2,1,0];
+ return $time;
+}
+
+sub headerize { # add blanks to additional lines, like rfc822 mail headers
+ local($_);
+ ($_)=(@_);
+ s/\n+\z//g;
+ s/(\n)(?=\S)/$1 /g;
+ return $_ . "\n";
+}
+
+sub absname {
+ local($_)=@_;
+ $_=$ENV{PWD}."/".$_ if not m!^/!;
+ s!(/.|/+)(?=/)!!g;
+ return $_;
+}
+
+
+
+# insanity checks and debug help --------------------------
+
+sub checkrequirements {
+ my $tmp;
+ $tmp=$changetrackcmd; $tmp=~s/ .*//g;
+ -x $tmp or chomp($tmp=`which $tmp`) and -x $tmp or $rc=1;
+ $tmp=$lockcmd; $tmp=~s/ .*//g;
+ -x $tmp or chomp($tmp=`which $tmp`) and -x $tmp or $rc=1;
+ warn "\n** PLEASE CHECK FOR POSSIBLY MISSING DEPENDENCIES:\n".
+ "** - changetrack (debian package or changetrack.sf.net)\n".
+ "** - lock.pl and Compact_pm/Flock.pm (jakobi.github.com)\n".
+ "** (if this is just 'which' acting up, please edit\n".
+ "** \$lockcmd and \$changetrackcmd to use absolute paths):\n".
+ "** \n".
+ "** lockcmd: $lockcmd\n".
+ "** changetrackcmd: $changetrackcmd\n".
+ "\n" if $rc;
+}
+
+
+sub debug_status {
+ return if not $debug;
+ my($mode);
+ $mode=shift; $mode="" if not defined $mode;
+ warn join " ",@_,"\n" if @_;
+ if ($mode) {
+ my $tmp="";
+ for(qw/editorcmd shellcmd lockcmd o_configfile o_user o_domain o_message/) {
+ $tmp.=qq!printf main::STDERR "# D %-20s: %s\\n", "$_", \$$_; !;
+ }
+ eval($tmp);
+ warn "# D ARGV: ". join(" ",@ARGV) . "\n";
+ warn "# D ARGVOPT: ". join(" ",@ARGVOPT)."\n";
+ }
+ warn "# D files1: ". join(" ",@files1) ."\n" if @files1;
+ warn "# D files3: ". join(" ",@files3) ."\n" if @files3;
+}
+
+
+
+# RCS -----------------------------------------------------------------
+
+sub changetrackpath {
+ local($_)=@_;
+ $_=absname($_);
+ s!/!:!g;
+ s!^:!!;
+ return $_;
+}
+
+
+sub getrcsfile {
+ my($file, @ARGV)=@_;
+ my($rc)=0;
+ # RCS annoyances, thus changetrack annoyances:
+ # - rcsdiff refuses to return diffs against missing sources
+ # - rcsdiff refuses to pass -N to gnudiff (which would allows this)
+ # - there is no rcscat... so to get a file, I've to do an
+ # explicit checkout or create a mess of dummies for rcsdiff
+ # - while co has a -dDATE option, rdiff doesn't
+ # - but like rcsdiff, co alsways has to have to create the file, not
+ # cat for you... - at least it allows date - ranges, which rcsdiff
+ # does not.
+
+ # this work around requires write access to the repo
+ # (if this is a problem, we can omit the backdir/RCS
+ # prefix and place the RCS/*,v file in question in a
+ # tmpdir, hopefully in the same fs with ln or cp otherwise;
+ # cd to tmpdir and invoke co locally)
+
+ my $absfile=absname($file);
+ my $ctname =changetrackpath($file);
+ my $ctnametmp ="$o_hostname.$$.::".$ctname;
+ my $ctfile ="$backupdir/".$ctname;
+ my $ctfiletmp ="$backupdir/$ctnametmp";
+ my $ctfilercstmp="$backupdir/RCS/$ctnametmp,v";
+ my $ctfilercs ="$backupdir/RCS/$ctname,v";
+
+ do{return "$ctfile\n"} if $o_backupecho;
+ do{return "$ctfile.history\n"} if $o_echohistory;
+ do{return "$ctfile.yesterday\n"} if $o_echoyesterday;
+ do{return "$backupdir\n"} if $o_echodir;
+
+ link $ctfilercs,$ctfilercstmp and do{
+ my $opt=$o_backupdiff; $opt=$o_backupcat if $o_backupcat;
+ if ($opt) {
+ if($opt=~/[ :]|^\d+[:-]/) { # assume -ddate
+ $opt="-d$opt";
+ } else { # assume -rrev number or symbolic name
+ $opt="-r$opt";
+ }
+ $opt="'".sq($opt)."'";
+ } else {
+ $opt="";
+ }
+ system "cd '".sq($backupdir)."'; co $opt -T '".sq($ctnametmp)."'";
+ };
+ my $cmd;
+ $cmd="diff"; $cmd="cat" if defined $o_backupcat;
+ $cmd="cd '".sq($backupdir)."'; $cmd";
+ for(@ARGV) { $cmd.=" '".sq($_)."'"; }
+ $cmd.=" '".sq($absfile)."'" if defined $o_backupdiff and not defined $o_backupcat;
+ $cmd.=" '".sq($ctnametmp)."'";
+
+ system $shellcmd, "-c", "set -o pipefail; ".$cmd;
+ $rc=$?>>8;
+ unlink $ctfilercstmp, $ctfiletmp;
+ return $rc;
+}
+
+
+__END__
+
+
+warn "# loading config sfre.sfrerc\n";
+
+# in a configuration file, you can set/unset option variables,
+# define additional files to lock, file-specific action, ...
+
+## global variables of interest:
+# $journal the journal
+# $backupdir the changetrack repository
+# the description variables:
+# - $o_user
+# - $o_domain
+# - $o_message
+
+
+## hooks
+
+# hook variables are available for evaluation by sfre.
+# these can be e.g. checking out a not-yet existing file
+# in action1 to e.g. an action3 to send an email on change
+# or invoke a make/ypmake/newaliases on change.
+
+# variables of interest:
+# - $_ is the argument string or filename
+
+# available hooks in temporal order:
+
+# 1) before locking
+# against argument array (possibly not filenames, but command args).
+# check before locking, can be checked a second time in a different
+# process instance after locking. can create files to be included
+# in the locking.
+$action1{"/etc/passwd"}='{warn "A1 triggered /etc/passwd\n"}';
+
+# 2g) global hook
+#$preaction
+
+# 2) before execution
+# checked before journal and versioning, existing plain files only
+$action2{"/etc/passwd"}='{warn "A2 triggered /etc/passwd\n"}';
+
+# 3) after execution, changed files
+# checked after command execution, just existing plain files
+# with changed # mtime/ctime timestamps
+$action3{'/\.bashrc'}= '{warn "A3 triggered on a bashrc: $_\n"}';
+
+# 4) after execution
+# checked after command execution, all existing plain files only
+$action4{"/etc/passwd"}='{warn "A4 triggered /etc/passwd\n"}';
+$action4{"/etc/group"}= '{warn "A4 triggered /etc/group\n"}';
+
+# 4g) global hook
+# $postaction
+
+# don't forget to signal the success of reading your config with true :)
+1;
+
1  admin.text/sfre.name
View
@@ -0,0 +1 @@
+systemflightrecordereditor, or just sfre (REQ cli.lock/*, changetrack.sf.net)
58 admin.text/sfre.sfrerc
View
@@ -0,0 +1,58 @@
+warn "# loading config sfre.sfrerc\n" if $o_verbose >1;
+
+# # in a configuration file, you can set/unset option variables,
+# # define additional files to lock, file-specific action, ...
+#
+# ## global variables of interest:
+# # $journal the journal
+# # $backupdir the changetrack repository
+# # the description variables:
+# # - $o_user
+# # - $o_domain
+# # - $o_message
+#
+#
+# ## hooks
+#
+# # hook variables are available for evaluation by sfre.
+# # these can be e.g. checking out a not-yet existing file
+# # in action1 to e.g. an action3 to send an email on change
+# # or invoke a make/ypmake/newaliases on change.
+#
+# # variables of interest:
+# # - $_ is the argument string or filename
+#
+# # available hooks in temporal order:
+#
+# # 1) before locking
+# # against argument array (possibly not filenames, but command args).
+# # check before locking, can be checked a second time in a different
+# # process instance after locking. can create files to be included
+# # in the locking.
+# $action1{"/etc/passwd"}='{warn "A1 triggered /etc/passwd\n"}';
+#
+# # 2g) global hook
+# #$preaction
+#
+# # 2) before execution
+# # checked before journal and versioning, existing plain files only
+# $action2{"/etc/passwd"}='{warn "A2 triggered /etc/passwd\n"}';
+#
+# # 3) after execution, changed files
+# # checked after command execution, just existing plain files
+# # with changed # mtime/ctime timestamps
+# $action3{'/\.bashrc'}= '{warn "A3 triggered on a bashrc: $_\n"}';
+#
+# # 4) after execution
+# # checked after command execution, all existing plain files only
+# $action4{"/etc/passwd"}='{warn "A4 triggered /etc/passwd\n"}';
+# $action4{"/etc/group"}= '{warn "A4 triggered /etc/group\n"}';
+#
+# # 4g) global hook
+# # $postaction
+
+
+
+# don't forget to signal the success of reading your config with true :)
+1;
+
400 admin.text/unescape.pl
View
@@ -0,0 +1,400 @@
+#!/usr/bin/perl -w
+
+# created GR 2007 Guide De Rosa
+# last change PJ 20090615
+# copyright: see below.
+
+# unescape.pl
+# Copyright (C) 2007 Guido De Rosa <guido_derosa*libero.it>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+
+
+# See script(1), @BUGS
+# This program aims to properly handle escape character and sequences
+# giving something more human-readable than typescript logs ..
+# Usage: unescape.pl < typescript > typescript.out
+
+
+# further information
+# -> script, scriptreplay, teseq
+# -> http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+
+
+#pj 20090601 jakobi@acm.org
+#
+# hints for improved usability of typescript logs
+# - to improve usability, define a easy-to-guess prompt
+# - have your shell print the command after editing/before execution
+# - it helps to change the window title, as the OSC sequences
+# usually do define reasonable points to consider ending/starting
+# a block or line-edit-simulation.
+# - you need to shorten/edit/rename the transcript anyway ...
+
+# not yet implemented
+# - there should be an option to guess prompt strings and
+# add white space around them. [if PS1 is messing with
+# the window title, this already happens]
+# - should we really try to outguess \r and $COLUMNS instead
+# of giving up very early and report partial line edits
+# via line()'s $history?
+# - there should be some way to mark or optionally remove stretches
+# with just mc / mutt / vi / less / more output, as currently
+# the only way to shorten the log is editing it.
+#
+# In case VIM itself sets the default window title, removing
+# most of the edit sessions is as simple as mangling the
+# region start points and then deleting from each start
+# to the next OSC 2 command (which might fail if
+# a subshell is opened within the vim session)
+# sed 's/^: 2;.*VIM.*/:A/g; /^:A/,/^: 2;/d'
+#
+# vi/less/man ... session detection may also be done as catching
+# suspicious commands as the first word after prompts,
+# and ending on the next prompt (give or take suspend
+# headaches).
+#
+# for friendly-territory admin usage, wrapping vi/vim with a
+# script giving out a unified diff at the end (plus a
+# dated backups) on changes might be nice.
+# note that script isn't really suitable and complete for
+# semi-trusted foreign root admin shells... - sudo changed to
+# keep a copy of the executed script might help as long as
+# both shells and any scripts are disallowed. hmmm. usually
+# way to painful and slow for non-trivial work.
+
+use strict;
+my $VERSION = "0.01pj";
+print STDERR "$0 ver $VERSION\n\n";
+my $DEBUG = 0;
+
+sub line($);
+sub history(@);
+sub debug();
+
+
+
+# set these for some best effort promt guessing
+my $promptre='jakobi\@anuurn.*?[\$\>\#]';
+
+my $rmvim=1; # try stripping vim sessions, it might even work sanely
+
+my $rmmc=0; # try stripping mc sessions, NOT YET IMPLEMENTED
+ # and the mc subshell by default doesn't chnage the
+ # title. Thus above windows title trick fails just as
+ # it would for more, less or original vi.
+
+
+
+# global variables used in functions
+my(@outchars, $inptr, $outptr, $history, $lineprefix, $contprefix);
+my($prefixre,$text,$p1,$p2);
+
+# 4 char limit hardcoded in e.g. osc flagging
+$lineprefix=" "; # prefix for new lines
+$contprefix="> "; # prefix for reporting partial line buffers
+$prefixre='(?:\A|\n)[>= ] '; # regex for skipping above
+
+
+
+$text="";
+while(<>) {
+ $text.=line($_);
+}
+$_=$text;
+
+
+
+# some global cleanup for readability
+s/\x0d\x0a/\x0a/go; s/\x0d/\x0a/go; s/\x0a\x0a+/\x0a/go; # crlf
+# BUG: terminals ain't lineprinters, so this probably won't be required
+s/_\x08//go; s/\x08_//go; # backspace and _;
+while(s/[^\x0a\x08]\x08//go){;}; s/\x08//go; # backspace
+
+# ok, this is officially weak, but this just happens to add newlines
+# before the prompt in case PS1 does suitably mangle the terminal title
+s/${prefixre}OSC: (.*)/\n: $1/go; # mark OSC
+s/(\A|\n)([^:\n][^\S\n]*\S.*\n): /$1$2\n: /go; # and add \n before
+s/(\A|\n): (.*)\n(?!:)/$1: $2\n\n/gi; # and after one or more OSC
+
+
+
+s/^ [ \t]*$//mgo; # shouldn't be required
+
+
+
+# optionally try turning matches into separated paragraphs
+if ($promptre) {
+ # force things looking like promptre to start on a new line
+ s/((?!\n )[\s\S]{5})($promptre)/$1\n$lineprefix$2/go;
+ # if missing, add an empty line in front for all prompt matches
+ s/(\A|[^\n])(\n )($promptre)/$1\n$2$3/go;
+}
+
+
+
+# optionally try stripping vim sessions from the log,
+# assuming vim sets a window title until it is reset
+# roughly similar to e.g.
+# sed 's/^: 2;.*VIM.*/:A/g; /^:A/,/^: 2;/d'
+if ($rmvim) {
+ s/^: 2;.*VIM.*/:A/mgo; # OSC 2 windows title set by vim
+ while(/^:A\n/mgo) { # region start - title set by VIM
+ $p1=pos; $p1-=3;
+ $p2=pos if /^: 2;.*/mgo;
+ if ($p2) {
+ # region end: title restored/changed w/o VIM string
+ substr($_,$p1,$p2-$p1)=""; # strip region
+ pos=$p1;
+ }
+ }
+}
+
+
+
+# mc and mutt might be also be somewhat strippable with customizations
+#
+# NOT YET IMPLEMENTED, similar to above; maybe generalize above to
+# :A, :B, :C, ...
+
+
+
+# BUG there are e.g. some printable ALT-GR chars removed by this...
+s/[^[:print:]\s]//g;
+
+print;
+
+
+#####################################################################33
+
+sub debug() {
+return unless ($DEBUG);
+print STDERR @outchars, " inptr=$inptr outptr=$outptr\n"; #DEBUG
+}
+
+# maintain history of partial buffer contents for current
+# readline simulation (so basically provide partial buffers
+# to the user if fully simulating the terminal gets too hard
+# to guess)
+sub history(@) {
+ my($mode)=shift @_;
+ my($tmp)=join("",@_);
+
+ if ($tmp=~/\S/o) {
+
+ if ($history=~/\n\Z/o) {
+ if ($mode eq "n") {
+ $history.="\n"
+ }
+ $history.="$lineprefix"
+ } elsif ($history) {
+ if ($mode eq "c") {
+ $history.="\n$contprefix"
+ } else {
+ $history.="\n$lineprefix"
+ }
+ } else {
+ $history=$lineprefix
+ }
+
+ $history.=$tmp;
+
+ if ($mode eq "n") {
+ $history.="\n";
+ }
+
+ } else { # $tmp empty or WS
+ if ($mode eq "n") {
+ $history.="\n"
+ } elsif ($mode eq "") {
+ $history.="\n" if not $history=~/\n\Z/o;
+ }
+ }
+
+ return($history);
+}
+
+# simulate and perform in-line editing enough for guessing
+# command lines
+#
+# BUGS:
+# - we ignore anything but the most common CSI single char moves used
+# by e.g. bash readline line editing for _short_ single-terminal
+# line lines)
+# - mere appends to end of line are seen trigger \n and thus are
+# seen only at the next invocation of this function, i.e. we miss
+# the fact that multiple invocations might actually be part of
+# e.g. the same readline session
+# - thinking of ksh editing longish lines in a single screen line
+# makes me shudder
+# - we do NOT try to guess COLUMNS for computing the new $outptr
+# position on \e[A up and \r, instead we just collect and print
+# partial buffers we find upto these situations ($history).
+# - should be rewritten in native perl to use pos()
+# - handle non-printables, from bell to xon ...
+# - heuristic guesswork like vi ~-empty lines really shouldn't be
+# place into line()...
+# - outchars may still contain unparsed csi/osc/... sequences
+# which throw of positioning (even if those tend to be rare
+# in e.g. bash readline). So the regex to strip most csi/osc
+# should be run first, while only keeping the known good csi
+# sequences for readline.
+#
+# [An occurance of \e[A implies a line longer than $COLUMNS;
+# considering <\e1P|\b> \e[A <string with \b,\e[C, maybe one
+# changed char and a _really_printable_ substring already
+# part of $history.join("",@outchars)>, we might indeed be
+# able to guess $COLUMNS.
+#
+# Another way to guess $COLUMNS might be the _really_printable_
+# position of the first \r encountered.
+#
+# (While bash e.g. seems to reprint strings to end of screenline,
+# it still seems to _SHIFT_ chars after that, incl. even shifting
+# chars with \e[1@<CHAR> onto the next screen line, w/o reprinting!?)]
+#
+sub line($) {
+ my($inline,$inlen,$scanptr,$flag,@inchars,$tmp);
+ $inline=$_[0];
+ chomp $inline;
+
+ $inline=~s/\x9b/\e\[/go; # translate 8bit CSI control sequence introducer
+ $inline=~s/\x9c/\e\\/go; # translate 8bit ST string terminator
+ $inline=~s/\x9d/\e\]/go; # translate 8bit OSC operating system command
+ $inline=~s/\x9e/\e\^/go; # translate 8bit PM privacy message
+ $inline=~s/\x9f/\e\_/go; # translate 8bit APC application program command
+
+ #$inline=~s/\e\].{0,120}?(\e\\|\a)//go; # strip all OSC sequences until bell or ST
+ # hopefully, there's no escaping mechanism to allow
+ # \e occuring in an OSC sequence...
+ $inline=~s/\e\](.{0,120}?)(\e\\|\a)/\nOSC: $1\n/go; # or report OSC COMMAND
+
+ # strip most of stretches of empty lines with ~-mark from vim output
+ $inline=~s/\e\[\d+;\d+H~ +(\e\[\d+;\d+H~ +)+//go;
+
+ push @inchars, ( " ", " ", " " ); # add some blanks to allow for CSI length ptr moves
+
+ $inlen=length($inline);
+ @inchars = split(//,$inline);
+
+ $history="";
+ @outchars = ();
+ $outptr=0;
+ for ($inptr=0;$inptr<$inlen;$inptr++) {
+ if ($inchars[$inptr] eq "\n" ) { # newline
+ $outptr = 0;
+ history("n",@outchars);
+ @outchars=();
+ } elsif ($inchars[$inptr] eq "\a") { # skip non printable alarm bell
+ ;
+ } elsif ($inchars[$inptr] eq "\r") { # carriage return:->beginning of line
+ # strangely, when a bash line becomes multiple screen lines
+ # I only see a single (useless?) \r in typescript as indicator
+ $outptr = 0;
+ history("c",@outchars);
+ @outchars=();
+ } elsif ($inchars[$inptr] eq "\b") { # backspace (with no deletion)
+ $outptr--;
+ $outptr = 0 if ($outptr < 0);
+
+ } elsif ($inchars[$inptr] eq "\e") { # escape character (^[)
+ $inptr++;
+ if ($inchars[$inptr] eq "\\") { # skip non-printable ST - oops?
+ ;
+ } elsif ($inchars[$inptr] eq "[") {
+ $inptr++;
+ if ($inchars[$inptr] eq "C") { # ^[[C right
+ $outptr++;
+ # move right but char not yet seen in line:
+ $outchars[$outptr-1]=" " if $outptr>$#outchars+1;
+ } elsif ($inchars[$inptr] eq "A") { #^[[A up
+ # e.g. bash readline multiple-screen-line-editing
+ # for now just emit to history instead of trying to guess
+ # $COLUMNS and new outptr
+ $outptr = 0;
+ history("c",@outchars);
+ @outchars=();
+ } elsif ($inchars[$inptr] eq "1") {
+ $inptr++;
+ if ($inchars[$inptr] eq "P") { # ^[[1P delete
+ splice @outchars, $outptr, 1;
+ debug();
+ } elsif ($inchars[$inptr] eq "@") { # ^[[1@ insert
+ $inptr++;
+ splice @outchars, $outptr, 0, $inchars[$inptr] ;
+ $outptr++;
+ debug();
+ } else {
+ # skip to end of sequence or emit unknown sequence
+ for ($flag=0,$scanptr=$inptr;$scanptr<$inlen;$scanptr++) {
+ if ($inchars[$scanptr]=~/[a-z\@\{\|]/oi) {
+ $flag=1; last
+ }
+ }
+ if ($flag) {
+ $inptr=$scanptr
+ } else {
+ $outchars[$outptr++] = "\e";
+ $outchars[$outptr++] = "\[";
+ $outchars[$outptr++] = "1";
+ $outchars[$outptr++] = $inchars[$inptr];
+ }
+ debug();
+ }
+ } elsif ($inchars[$inptr] eq "K") { # ^[K delete
+ splice @outchars, $outptr, 1;
+ debug();
+ } else {
+ # skip to end of sequence or emit unknown sequence
+ for ($flag=0,$scanptr=$inptr;$scanptr<$inlen;$scanptr++) {
+ if ($inchars[$scanptr]=~/[a-z\@\{\|]/oi) {
+ $flag=1; last
+ }
+ }
+ if ($flag) {
+ $inptr=$scanptr
+ } else {
+ $outchars[$outptr++] = "\e";
+ $outchars[$outptr++] = "\[";
+ $outchars[$outptr++] = $inchars[$inptr];
+ }
+ debug();
+ }
+
+ } else {
+ if ($inchars[$inptr]=~/[#\%\(\)\*\+]/o and $inchars[$inptr+1]=~/[^\]\[\s]/o) {
+ # skip \eXX
+ $inptr+=1;
+ } elsif ($inchars[$inptr]=~/[^\]\[\s]/o) {
+ # skip \eX
+ } else {
+ # unknown partial sequence, emit it and hope for the best
+ $outchars[$outptr++] = "\e";
+ $outchars[$outptr++] = $inchars[$inptr];
+ }
+ debug();
+ }
+ } else {
+ $outchars[$outptr++] = $inchars[$inptr];
+ debug();
+ }
+ }
+ return(history("n",@outchars));
+}
1  admin.text/unescape.pl.name
View
@@ -0,0 +1 @@
+remove some line noise from typescripts (script output)
39 cli.backup/00_BACKUP.phtml
View
@@ -6,20 +6,39 @@
} else {
*]
-<p>Some backup support commands.</p>
-
<p>Some scripts for backups from single-instance tarballs to
incrementals to rsync-style hardlinked snapshots with support scripts
like xfer_backup.diff to diff, echo or ls a file in the backup
-relative to $PWD or the specified file. The scripts need a rewrite and
-some effort to generalize before release; then again, they've been
-working reliable for years now and I'm loath to change them without
-some additional 'benefit' or interesting major feature (like e.g.
-root-less backup - but that way's more effort than interest...).</p>
+relative to $PWD or the specified file.</p>
+
+<p> The scripts need a rewrite and some effort to generalize before
+release; then again, they've been working reliable for years now and
+I'm loath to change them without some additional 'benefit' or
+interesting major feature (like e.g. root-less backup - but that way's
+more effort than interest...).</p>
+
+<hr>
+
+<p>If you also use a filelist based backup, <b>expanfind</b> may be of
+interest, as you can pipe find results to expanfind, have expanfind
+grep that list for interesting files, and then <i>expand</i> the grep
+results by adding any result-<i>related</i> file to the list. The name
+is a play on expansyn, as expanfind does indeed something like synonym
+expansion for filenames (the script still contains some profiling
+comments comparing various code variants - with some interesting
+conclusions wrt performance).
+
+<p>Example: find / | expanfind -tag rpmsave will return the list of
+rpmsave and all files that 'share' a similar prefix. Also useful for
+backups of modified configuration files (just copy FILE the original
+to FILE.TRACKED, and expanfind -tag TRACKED now 'tracks' FILE*).</p>
+
+<hr>
-<p>Recently I've read about ccollect, which might be a suitable
+<p>Recently I've read about <b>ccollect</b>, which might be a suitable
substitute for my own filelist-based rsync snapshot script (which is
-the major non-glue portion), as it also seems to be list-based - at
-the very least it supports exclusion.</p>
+the major non-glue portion of code within the backup scripts), as it
+also seems to be list-based - at the very least it supports
+exclusion.</p>
[*}*][# MUST BE AT EOF! #]
2  cli.backup/00_BACKUP.phtml.name
View
@@ -1 +1 @@
-backup (placeholder)
+backup
1  cli.backup/@incomplete.skip
View
@@ -0,0 +1 @@
+
345 cli.backup/expanfind
View
@@ -0,0 +1,345 @@
+#!/usr/bin/perl
+use strict; use vars;
+
+# expanfind - grep a filelist and return grep + related - skipped
+# expanfind -v - grep a filelist and return all - grep - related - skipped
+#
+# to extend a partial list (plus sort -u?): find | tee >(expanfind) | cat
+# N