home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / rescue / bin / dd_rhelp < prev    next >
Text File  |  2006-11-29  |  31KB  |  1,242 lines

  1. #!/bin/sh
  2. # Begin dd_rhelp
  3. # Copyright (C) 2004 LAB Valentin <vaab@free.fr>
  4. #  
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #  
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #  
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. #  
  19. # TODO : 
  20. #   x Add a feature that if a log file is not found and the dest file has data throws an error or a warning... or something
  21. #   x - close parts should be joined by testing gaps between them...
  22. #   x - BUG : show_bar doesn't correctly draw end of bar when a correct EOF is
  23. #  found. (done ?)
  24. #   x - found which is the correct EOF in log. (done ?)
  25. #   x - Tests and ensures that nb_err is greater than 1.
  26. #   x - First time it is launched, checks right and left limits of chunks.
  27. #   x - Make a estimation time of end. Worse and good.
  28. #   x - Test if dd_rescue is the good version : the one that makes a summary 
  29. #  even in maxerr mode (partly done...)
  30. #   x - less but clearer output if possible (ansi color ?)
  31. #   x - Real handling of options... for max_err min_bs max_bs, the log_file 
  32. #  source... etc ...
  33. #   x - Much cleaner code.
  34. #   x - Better internal Map, with dd_rescued byte, error bytes, clean bytes...
  35. #
  36. # === Do not touch these vars, they are compiled at 'configure' time.
  37. #
  38.  
  39.  
  40. # THIS CODE IS OBSOLETE : PROGRAMS PATHS ARE COMPUTED AT RUNTIME.
  41. # ---
  42. # Where to find programs
  43.  
  44. #SED="@SED@"
  45. #GREP="@GREP@"
  46. #CAT="@CAT@"
  47. #CUT="@CUT@"
  48. #TR="@TR@"
  49. #HEAD="@HEAD@"
  50. #TAIL="@TAIL@"
  51. #TOUCH="@TOUCH@"
  52. #WC="@WC@"
  53. #BC="@BC@"
  54. # ---
  55.  
  56.  
  57. # Some info
  58.  
  59. email="vaab@free.fr"
  60. version="0.0.6"
  61. state="beta"
  62. author="LAB Valentin"
  63.  
  64. # Including 'libcolor.sh'
  65.  
  66. # If COLUMNS hasn't been set yet (bash sets it but not when called as
  67. # sh), do it ourself
  68.  
  69. if [ -z "$COLUMNS" ]
  70. then
  71.     # Get the console device if we don't have it already
  72.     # This is ok by the FHS as there is a fallback if
  73.     # /usr/bin/tty isn't available, for example at bootup.
  74.  
  75.     test -x /usr/bin/tty && CONSOLE=`/usr/bin/tty`
  76.     test -z "$CONSOLE" && CONSOLE=/dev/console
  77.  
  78.     # Get the console size (rows columns)
  79.  
  80.     stty size > /dev/null 2>&1
  81.     if [ "$?" == 0 ]
  82.     then
  83.     [ "$CONSOLE" == "/dev/console" ] && SIZE=$(stty size < $CONSOLE) \
  84.                                          || SIZE=$(stty size)
  85.  
  86.         # Strip off the rows leaving the columns
  87.  
  88.         COLUMNS=${SIZE#*\ }
  89.     else
  90.     COLUMNS=80
  91.     fi
  92.  
  93. fi
  94.  
  95. COL=$[$COLUMNS - 10]
  96. WCOL=$[$COLUMNS - 30]
  97. SCOL=$[$COLUMNS - 4]
  98. LCOL=$[$COLUMNS - 1]
  99.  
  100.  
  101.  
  102. SET_COL=$(echo -en "\\033[${COL}G")
  103. SET_SCOL=$(echo -en "\\033[${SCOL}G")
  104. SET_WCOL=$(echo -en "\\033[${WCOL}G")
  105. SET_LCOL=$(echo -en "\\033[${LCOL}G")
  106.  
  107. SET_BEGINCOL=$(echo -en "\\033[0G")
  108.  
  109.  
  110.  
  111. NORMAL=$(echo -en "\\033[0;37m")
  112. RED=$(echo -en "\\033[1;31m")
  113. GREEN=$(echo -en "\\033[1;32m")
  114. YELLOW=$(echo -en "\\033[1;33m")
  115. BLUE=$(echo -en "\\033[1;34m")
  116. GRAY=$(echo -en "\\033[1;30m")
  117. WHITE=$(echo -en "\\033[1;37m")
  118.  
  119. SUCCESS=$GREEN
  120. WARNING=$YELLOW
  121. FAILURE=$RED
  122. NOOP=$BLUE
  123. ON=$SUCCESS
  124. OFF=$FAILURE
  125. ERROR=$FAILURE
  126.  
  127. # Including 'libcommon.sh'
  128.  
  129. # DEPEND on libcolor
  130.  
  131. [ -n "$exname" ] || exname=$(basename $0)
  132.  
  133. function print_exit()
  134. {
  135.     echo $@;
  136.     exit 1;
  137. };
  138.  
  139. function print_syntax_error()
  140. {
  141.     [ "$*" ] ||    print_syntax_error "$FUNCNAME: no arguments"
  142.     print_exit "${ERROR}script error:${NORMAL} $@";
  143. };
  144.  
  145. function print_syntax_warning()
  146. {
  147.     [ "$*" ] || print_syntax_error "$FUNCNAME: no arguments.";
  148.     [ "$exname" ] || print_syntax_error "$FUNCNAME: 'exname' var is null or not defined.";
  149.     echo "$exname: ${WARNING}script warning:${NORMAL} $@";
  150. };
  151.  
  152. function print_error()
  153. {
  154.     [ "$*" ] || print_syntax_warning "$FUNCNAME: no arguments.";
  155.     [ "$exname" ] || print_exit "$FUNCNAME: 'exname' var is null or not defined.";
  156.     print_exit "$exname: ${ERROR}error:${NORMAL} $@"
  157. };
  158.  
  159. function print_warning()
  160. {
  161.     [ "$*" ] || print_syntax_warning "$FUNCNAME: no arguments.";
  162.     [ "$exname" ] || print_syntax_error "$FUNCNAME: 'exname' var is null or not defined.";
  163.     echo "$exname: ${WARNING}warning:${WARNING} $@"
  164. };
  165.  
  166. function print_usage()
  167. {
  168.     [ "$usage" ] || print_error "$FUNCNAME: 'usage' variable is not set or empty."
  169.     echo "usage: $usage"
  170.  
  171. #    if [ "$_options" != "" ]
  172. #    then    
  173.     
  174.  
  175. #    fi
  176. }
  177.  
  178. function invert_list()
  179. {
  180.     newlist=" "
  181.     for i in $*
  182.     do
  183.       newlist=" $i${newlist}"
  184.     done
  185.     echo $newlist;
  186. };
  187.  
  188. function depends()
  189. {
  190.     for i in $@
  191.     do
  192.     if ! type $i > /dev/null 2>&1
  193.     then
  194.        print_error "dependency check : couldn't find '$i' command."
  195.     fi
  196.     done
  197. }
  198.  
  199. function require()
  200. {
  201.     for i in $@
  202.     do
  203.     if ! type $i > /dev/null 2>&1
  204.     then
  205.        return 1;
  206.     fi
  207.     done
  208. }
  209.  
  210. function print_octets ()
  211. {
  212.     [ "$*" ] || print_syntax_error "$FUNCNAME: no arguments.";
  213.     [ "$2" ] && print_syntax_error "$FUNCNAME: too much arguments.";
  214.  
  215.     [ "$( echo "$1 < 1024" | bc )" == "1" ] && { echo -n "$1 octets"; return 0;}
  216.  
  217.     kbytes=$(echo "$1 / 1024" | bc );
  218.     [ "$( echo "$kbytes < 1024" | bc)" == "1" ] && { echo -n "$kbytes Ko" ; return 0; }
  219.  
  220.     mbytes=$(echo "$kbytes / 1024" | bc );
  221.     [ "$( echo "$mbytes < 1024" | bc)" == "1" ] && { echo -n "$mbytes Mo" ; return 0; }
  222.     gbytes=$(echo "$mbytes / 1024" | bc );
  223.     [ "$( echo "$gbytes < 1024" | bc )" == "1" ] && { echo -n "$gbytes Go" ; return 0; }
  224.     tbytes=$(echo "$gbytes / 1024" | bc );
  225.     echo -n "$gbytes To"
  226.  
  227. }
  228.  
  229. function checkfile ()
  230. {
  231.     [ "$*" ] || print_syntax_error "$FUNCNAME: no arguments.";
  232.     [ "$3" ] && print_syntax_error "$FUNCNAME: too much arguments.";
  233.  
  234.  
  235.     for i in $(echo $1 | sed 's/\(.\)/ \1/g')
  236.     do
  237.     case "$i" in
  238.         "")
  239.             :
  240.         ;;
  241.                 "e")
  242.                         if ! [ -e "$2" ]
  243.             then 
  244.                             echo "'$2' is not found."
  245.                             return 1
  246.             fi;;
  247.         "f")
  248.             if ! [ -f "$2" ]
  249.             then
  250.                 echo "'$2' is not a regular file."
  251.                 return 1
  252.             fi;;
  253.         "d")
  254.             if ! [ -d "$2" ]
  255.             then
  256.                 echo "'$2' is not a directory."
  257.                 return 1
  258.             fi;;
  259.         "r")
  260.                     if ! [ -r "$2" ]
  261.             then
  262.                             echo "'$2' is not readable."
  263.                             return 1
  264.             fi;;
  265.                 "w")
  266.             if ! [ -w "$2" ]
  267.             then
  268.                             echo "'$2' is not writable."
  269.                             return 1
  270.             fi;;
  271.                 "x")
  272.                         if ! [ -x "$2" ]
  273.             then
  274.                             echo "'$2' is not executable/openable."
  275.                             return 1
  276.             fi;;
  277.         "l")
  278.             if ! [ -L "$2" ]
  279.             then
  280.                 echo "'$2' is not a symbolic link."
  281.                 return 1
  282.             fi;;
  283.     esac
  284.     done
  285.  
  286.     return 0;
  287. };
  288.  
  289.  
  290.  
  291. # === VARS :
  292. #
  293. # Feel free to change them.
  294.  
  295. max_err=5     # number of error to start a new chunk
  296.               # DO NOT SET TO "1"...
  297. min_bs=512    # min block size (see dd_rescue's help)
  298. max_bs=16384  # max block size (see dd_rescue's help)
  299. bar_lines=15  # nb of lines for bar drawing.
  300.  
  301.  
  302. # === CODE : 
  303. #
  304. #
  305.  
  306.  
  307. function get_path
  308. {
  309.     echo $(type -p $1) 
  310. }
  311.  
  312. function require_exe 
  313. {
  314.     path=$(get_path $1)
  315.  
  316.     if test -z "$path" ; then
  317.     print_error "requires '$1' to run... And it couldn't find it." >&2
  318.     exit 1
  319.     else
  320.     echo $path
  321.     fi
  322. }
  323.  
  324. # checks each required program and get their path.
  325. SED=$(require_exe sed) || exit 1
  326. GREP=$(require_exe grep) || exit 1
  327. CAT=$(require_exe cat) || exit 1
  328. CUT=$(require_exe cut) || exit 1
  329. TR=$(require_exe tr) || exit 1
  330. HEAD=$(require_exe head) || exit 1
  331. TAIL=$(require_exe tail) || exit 1
  332. TOUCH=$(require_exe touch) || exit 1
  333. WC=$(require_exe wc) || exit 1
  334. BC=$(require_exe bc) || exit 1
  335. tr=$(require_exe tr) || exit 1
  336.  
  337. # usage string :
  338. usage="$exname {filename|device} {output-file} [{info}]
  339.     or $exname --help
  340.     or $exname --version"
  341.  
  342. #
  343. # === Argument checking...
  344. #
  345.  
  346. if [ "$#" == "1" ]; then
  347.  
  348.     if [ "$1" == "--help" ]; then
  349.     print_usage
  350.     "${CAT}" <<EOF
  351.  
  352. Options:
  353.   --help            Print this message
  354.   --version         Print version information
  355.   {filename|device} The source file (it can be a block device)
  356.   {output-file}     The destination file
  357.   info              Specifying "info" as third argument will display 
  358.                       summary informations on ongoing recovery and
  359.               exit without taking any actions.
  360.  
  361. Note:
  362.   A log file will be created, and named '<output-file>.log'. This is a
  363. dd_rescue log file (which is human readable). This log file is important
  364. as dd_rhelp feeds itself with its contents to manage correctly dd_rescue.
  365.  
  366. Send bug reports, or comments to $email.
  367. Sorry for the shitty programming style.
  368. EOF
  369.     exit
  370.     fi
  371.  
  372.     if [ "$1" == "--version" ] ; then
  373.     "${CAT}" <<EOF
  374. $exname $version ($state)
  375. EOF
  376.     exit
  377.     fi
  378.  
  379.     echo "Need 2 arguments..."
  380.     print_usage
  381.     exit 1
  382. fi
  383.  
  384.  
  385.  
  386. if [ "$1" == "" ] 
  387. then
  388.     echo "Need 2 arguments..."
  389.     print_usage
  390.     exit 1;
  391. fi
  392.  
  393. if [ "$3" != "" ] && [ "$3" != "info" ]
  394. then
  395.     shift;shift
  396.     echo "too much argument... : '$*' is beyond limit. "
  397.     print_usage
  398.     exit 1;
  399. fi
  400.  
  401. if [ "$3" == "info" ]
  402. then
  403.  opt="info"
  404. fi
  405.  
  406. infile="$1"
  407. outfile="$2"
  408. logfile="$2.log"
  409.  
  410. #
  411. # === Files checking
  412. #
  413.  
  414.  
  415. if ! checkfile er "$infile"
  416. then
  417.     print_error "'$infile' is not accessible..."
  418.     exit 1;
  419. fi
  420.  
  421. if ! checkfile erw "$outfile" > /dev/null 2>&1
  422. then
  423.  
  424.     if ! "${TOUCH}" "$outfile" > /dev/null 2>&1
  425.     then
  426.     print_error "'$outfile' is not accessible/could not be created..."
  427.     else
  428.     [ "$DEBUG" == "on" ] && 
  429.        echo "- file '$outfile' was successfully touched..."
  430.     fi
  431. fi
  432.  
  433. if ! checkfile erw "$logfile" > /dev/null 2>&1
  434. then
  435.  
  436.     if [ "$opt" == "info" ] && ! [ -r "$logfile" ]
  437.     then
  438.     "${CAT}" <<EOF
  439. No info available since there's no readable '$logfile'.
  440.  
  441. 'info' option outputs information on current rescuing state by parsing this 
  442. log file that would have been created by a precedent use of dd_rhelp or 
  443. dd_rescue. Since there's no log file, it has nothing to display.
  444.  
  445. This happens if you haven't launched a dd_rhelp before.
  446. EOF
  447.     exit 0 ;
  448.     fi
  449.   
  450.  
  451.  
  452.     if ! "${TOUCH}" "$logfile" > /dev/null 2>&1
  453.     then
  454.     print_error "'$logfile' is not accessible/could not be created..."
  455.     else
  456.     [ "$DEBUG" == "on" ] && 
  457.        echo "- file '$logfile' was successfully touched..."
  458.     fi
  459. else
  460.     if [ "$opt" == "info" ] && [ "$(${CAT} "$logfile" | grep -v ^\$ )" == "" ] ; then
  461.     "${CAT}" <<EOF
  462. No info available in '$logfile' : it is empty !
  463.  
  464. 'info' option outputs information on current rescuing state by parsing this 
  465. log file that would have been created by a precedent use of dd_rhelp or 
  466. dd_rescue. Since there's no content in log file, it has nothing to display.
  467.  
  468. This could happens if you haven't launched a dd_rhelp before.
  469. EOF
  470.     exit 0 ;
  471.     fi
  472.  
  473. fi
  474.  
  475.  
  476. #
  477. # === Some vars, do not touch unless you know what you are doing...
  478. #
  479.  
  480. # regexp for parsing the log file.
  481.  
  482. string="^Summary for $infile -> $outfile:"
  483. infoline="^dd_rescue: (info):"
  484. eofstring="$infoline $infile ([0-9]\+\.[0-9]k): EOF\$"
  485. nb_stars=$[ $bar_lines * $COLUMNS ] # nb of char to display progress bar...
  486.  
  487. #
  488. # === Functions
  489. #
  490.  
  491. # Variable that holds chunks info in a list of
  492. # lines of nb1-nb2...
  493. chunk=""
  494.  
  495.  
  496.  
  497. function get_valid_dd_rescue
  498. {
  499.  
  500.     [ "$DEBUG" == "on" ] && echo "Finding dd_rescue binary" >&2
  501.  
  502.     path=$(get_path dd_rescue)
  503.     
  504.     version=$("$path" -V 2>&1 | grep "dd_rescue Version" | cut -f 3 -d " " |
  505.     cut -f 1 -d ",")
  506.     
  507.     [ "$DEBUG" == "on" ] && echo -n "Trying '$path' : gives this version : '$version'..." >&2
  508.  
  509.     if is_num "$version" && [ "$(bc_calc 2 "$version < 1.03")" == "0" ]
  510.     then
  511.         [ "$DEBUG" == "on" ] && echo "OK !" >&2
  512.     if [ "$(bc_calc 2 "$version < 1.12")" == "0" ]
  513.         then
  514.         echo "$path -y0"
  515.         else
  516.         echo "$path"
  517.     fi
  518.     return 0
  519.     else
  520.     [ "$DEBUG" == "on" ] && echo "BAD !" >&2
  521.     fi
  522.     
  523.     path="$(dirname $(type -ap "$0" | "${TAIL}" -n 1))/dd_rescue"
  524.     
  525.     if [ -x "$path" ] ;then
  526.     version=$("$path" -V 2>&1 | grep "dd_rescue Version" | cut -f 3 -d " " |
  527.         cut -f 1 -d ",")
  528.     [ "$DEBUG" == "on" ] && echo -n "Trying '$path' : gives this version : '$version'..." >&2
  529.     if is_num "$version" && [ "$(bc_calc 2 "$version < 1.03")" == "0" ];then
  530.         [ "$DEBUG" == "on" ] && echo "OK !" >&2
  531.         echo "$path"
  532.         return 0
  533.     else
  534.         [ "$DEBUG" == "on" ] && echo "BAD !" >&2
  535.     fi
  536.     
  537.     fi
  538.     
  539.     echo "Bad version of dd_rescue ! you must have >= 1.03">&2
  540.     exit 1    
  541. };
  542.  
  543. # *** check whether $1 is num...
  544. # args : 
  545. #   $1 is string to be tested as num
  546. # returns errorlevel 0/1 depending on "$1"
  547. # std_out : nothing
  548. function is_num ()
  549. {
  550.     rest=$(echo $1 | sed 's/^\([0-9]\+\)\?\(\.\([0-9]\+\)\)\?$/X/g')
  551.     
  552.     if [ -z "$(echo $1 | grep '^\([0-9]\+\)\?\(\.\([0-9]\+\)\)\?$')" ] ;then
  553.     return 1
  554.     else
  555.     return 0
  556.     fi
  557. }
  558.  
  559. # *** Handles BC conveniently
  560. # args :
  561. #   $1  is scale (number of digit in result after '.')
  562. #   $2+ is expression to be parsed by bc
  563. # returns : errorlevel 0 if no error occured
  564. # std_out : output of bc 
  565. # quits program with errorlevel 1 if error occured
  566. #  and output a debug string.
  567. function bc_calc ()
  568. {
  569.     scale=$1;
  570.  
  571.     if ! is_num "$scale" && test "$scale" != "no"; then
  572.     echo "*** bc_calc: Wrong first argument " >&2
  573.     echo "*** '$scale' is not a number." >&2
  574.     exit 1
  575.     fi
  576.  
  577.     test "$scale" == "no" && scale=""
  578.  
  579.     shift
  580.     exp=$(test "$scale" && echo "scale=$scale;";
  581.     test "$*" && echo "$*" || "${CAT}" - )
  582.     
  583.     ans=$(echo "$exp" | "${BC}" 2>&1)
  584.  
  585.     if ! is_num "$ans"; then
  586.     echo "*** BC failed on this expression : " >&2
  587.     echo "$exp" >&2
  588.     echo "*** BC returned :" >&2
  589.     echo "$ans" >&2
  590.     exit 1;
  591.     else
  592.     echo "$ans"
  593.     return 0
  594.     fi    
  595. }
  596.  
  597. # Go fetch EOF information in log to get a good approximation
  598. # no args
  599. # Depends on content of logfile.
  600. # errorlevel allways 0
  601. # return nothing
  602. # changes $eof to "nothing" if no EOF is found,
  603. #              or 'nb' where nb is best EOF found
  604. function get_eof()
  605. {
  606.  
  607.   eoflines="$("${CAT}" "$logfile" | "$tr" -d "\\r" | "$GREP" "$eofstring" | "$SED" 's/^dd_rescue: (info): .* (\([0-9\.]\+\)k): EOF$/\1/g')"
  608.  
  609.   eof="nothing"
  610.  
  611.   for i in $eoflines
  612.   do
  613.     if [ "$eof" == "nothing" ]; then
  614.     eof=$i
  615.     continue
  616.     fi
  617.  
  618.     if [ "$(bc_calc 1 "$eof > $i")" == "1" ];then
  619.     eof=$i
  620.     continue
  621.     fi
  622.   done
  623. }
  624.  
  625.  
  626. # Will mark chunk as beiing completed
  627. # Depends on '$chunk', $chunk MUST be correctly generated !!
  628. # args :
  629. #   "nb1-nb2" with nb1<nb2
  630. # Modifies '$chunk'.
  631. function add_chunk()
  632. {
  633.     arg_start=$(echo "$1" | "${CUT}" -f 1 -d "-")
  634.     arg_stop=$(echo "$1" | "${CUT}" -f 2 -d "-")
  635.  
  636.     if ! is_num "$arg_start" ||
  637.     ! is_num "$arg_stop"; then
  638.     print_error "*** add_chunk : invalid argument '$1' (is not correctly formatted as number:number)"
  639.     fi
  640.  
  641.     if test "$(bc_calc 1 "$arg_start < $arg_stop")" == "0"; then
  642.     print_error "*** add_chunk : invalid argument '$1' (these are not logical values)"
  643.     fi
  644.  
  645.     overlap="no"
  646.  
  647.     goodchunk=""
  648.     parsechunk="$chunk"
  649.  
  650.     while test "$parsechunk"
  651.     do
  652.  
  653.       # get first chunk already marked.
  654.       i="$(echo "$parsechunk" | "${HEAD}" -n 1 )"
  655.  
  656.       # pull the two bounds
  657.       i_start="$(echo "$i" | "${CUT}" -f 1 -d "-")"
  658.       i_stop="$(echo "$i" | "${CUT}" -f 2 -d "-")"
  659.  
  660.  
  661.       # new chunk begins after current chunk end ?
  662.       as_gt_ie="$(bc_calc 1 "$arg_start > $i_stop" )"
  663.  
  664.       if [ "$as_gt_ie" == "1" ]
  665.       then
  666.           # new chunk doesn't overlap with current chunk
  667.       # Iterate, put current chunk in $goodchunk.
  668.       goodchunk="$(echo -en "$goodchunk\n$i")"
  669.       parsechunk="$(echo "$parsechunk" | "${TAIL}" -n +2)"
  670.       continue
  671.       fi
  672.  
  673.       # new chunk ends before current chunk start ?
  674.       ae_gt_is="$(bc_calc 1 "$arg_stop < $i_start")"
  675.  
  676.       
  677.       if [ "$ae_gt_is" == "1" ]
  678.       then
  679.           # new chunk doesn't overlap with current chunk but is before
  680.       # we have found where to put our chunk
  681.  
  682.       break; # we can break because chunk are sorted
  683.       fi
  684.       
  685.       # if we come here, that means that new chunk overlap with current.
  686.       
  687.       # have we new chunk's start located IN current chunk ?
  688.       as_int="$(bc_calc 1 "$arg_start >= $i_start && $arg_start <= $i_stop")"
  689.  
  690.       # have we new chunk's end located IN current chunk ?
  691.       ae_int="$(bc_calc 1 "$arg_stop >= $i_start && $arg_stop <= $i_stop ")"
  692.  
  693.       # new chunk is contained entirely in current chunk
  694.       if [ "$as_int" == "1" ] && [ "$ae_int" == "1" ]
  695.       then
  696.       # no need to do anything
  697.       overlap="yes"
  698.       break;
  699.       fi
  700.  
  701.       # new chunk contains entirely current chunk
  702.       if [ "$as_int" == "0" ] && [ "$ae_int" == "0" ]
  703.       then
  704.       # we forget about current chunk, and iterate.
  705.       parsechunk=$(echo "$parsechunk" | "${TAIL}" -n +2)
  706.       continue
  707.       fi
  708.       
  709.       # new chunk overlap on its end with beginning of current chunk
  710.       if [ "$as_int" == "0" ] && [ "$ae_int" == "1" ]
  711.       then
  712.       # grow new chunk to englobe current chunk.
  713.       arg_stop=$i_stop
  714.       parsechunk=$(echo "$parsechunk" | "${TAIL}" -n +2)
  715.  
  716.       break; # we can break because chunk are sorted.
  717.       fi
  718.  
  719.       # new chunk overlap on its beginning with end of current chunk
  720.       if [ "$as_int" == "1" ] && [ "$ae_int" == "0" ]
  721.       then
  722.       # grow new chunk to englobe current chunk.
  723.       arg_start=$i_start
  724.       parsechunk=$(echo "$parsechunk" | "${TAIL}" -n +2)
  725.       continue; # new chunk might overlap more chunks
  726.       fi
  727.  
  728.     done
  729.  
  730.     # Overlapping occurs only if new chunk is contained in already marked
  731.     # chunk. In this case, we musn't change $chunk.
  732.     if [ "$overlap" == "no" ]
  733.     then
  734.     chunk="$(echo -en "$goodchunk\n$arg_start-$arg_stop\n$parsechunk" |
  735.      "$GREP" -v ^\$)"
  736.     fi
  737. }
  738.  
  739.  
  740. # get_next_pos will found the next offset to jump at to launch dd_rescue
  741. # No args
  742. # depends on $eof
  743. # returns offset:long  (offset in start location, long is how much bytes
  744. #                         to retrieve from location both reverse and forth)
  745. function get_next_pos()
  746. {
  747.    if [ "$eof" == "nothing" ] || test -z "$eof" 
  748.    then
  749.  
  750.     # finding last's chunk end.
  751.  
  752.     if test "$chunk" ;then
  753.        last_chunk=$(echo "$chunk" | "${TAIL}" -n 1 )
  754.        max_stop=$(echo "$last_chunk" | "${CUT}" -f 2 -d "-")
  755.         else           
  756.            max_stop=0
  757.     fi
  758.     
  759.     echo "$(bc_calc 1 "($max_stop * 2)"):$max_stop";
  760.        
  761.     else
  762.  
  763.     # find biggest hole.
  764.     pos=0
  765.     size=0
  766.     cursize=0
  767.     start=0
  768.     next=0
  769.  
  770.     # Get biggest hole between chunks
  771.     for i in $chunk "$eof-$eof"
  772.         do
  773.  
  774.       # collect start of chunk
  775.       next=$(echo "$i" | "${CUT}" -f 1 -d "-")
  776.  
  777.       if [ "$next" != "$start" ]
  778.       then
  779.           cursize="$(bc_calc 1 "($next - $start)")"
  780.           if [ "$(bc_calc 1 "($size < $cursize)")" == "1" ]
  781.           then
  782.               size=$cursize
  783.               pos=$start
  784.           fi
  785.       fi
  786.       start=$(echo "$i" | "${CUT}" -f 2 -d "-")
  787.     done
  788.  
  789.     size="$(bc_calc 0 "(($size + 1) / 2)")"
  790.     echo "$(bc_calc 1 "($pos + $size)"):$size"
  791.     fi
  792. }
  793.  
  794.  
  795. # Get info with last summary produced by dd_rescue call.
  796. # no args
  797. # depends on content of log file
  798. # changes $logcontent, $chunk, $eof 
  799. function swallow_last_summary()
  800. {
  801.   # last summary of log (4 lines output by printreport())
  802.   last_logcontent=$("${CAT}" "$logfile" | "$tr" -d "\\r" | "$GREP" "$string" -A 3 | "${TAIL}" -n 4)
  803.   process_log "$last_logcontent"
  804.  
  805.   get_eof
  806.  
  807.   save_log
  808.   
  809. }
  810.  
  811.  
  812.  
  813. function get_last_chunk()
  814. {
  815.     if test "$chunk"; then
  816.     last_chunk="$(echo "$chunk" | "${TAIL}" -n 1 )"
  817.     echo "$last_chunk" | "${CUT}" -f 2 -d "-"
  818.     else
  819.     echo 0
  820.     fi
  821. };
  822.  
  823.  
  824. # Display a neat bar in ascii art(?!) which shows completion of dd_rescue.
  825. #
  826. #
  827. function show_bar()
  828. {
  829.  
  830.     echo "=== BAR === [ 'x' dd_rescued, '*' next jump point, '|' '.' not dd_rescued ]"
  831.  
  832.     if [ "$eof" == "nothing" ] || test -z "$eof"
  833.     then
  834.     eof_limit="$(get_last_chunk)"
  835.     next_pos="$(get_next_pos | "${CUT}" -f 1 -d ":")"
  836.     if [ "$(bc_calc 1 "$eof_limit < $next_pos")" == "1" ]; then
  837.         eof_limit=$next_pos
  838.     fi
  839.     else
  840.     eof_limit="$eof";
  841.     fi
  842.  
  843.     if ! is_num "$nb_stars";then
  844.     nb_stars=80
  845.     fi
  846.     
  847.  
  848.     if [ "$eof_limit" != "0" ]
  849.     then
  850.     
  851.     # c_res is nb of Kb represented by one char.
  852.     c_res="$(bc_calc 10 "$eof_limit / $nb_stars")";
  853.  
  854.     # next_pos is place of next jump in chars.
  855.     next_pos="$(bc_calc 0 "$(get_next_pos | "${CUT}" -f 1 -d ":") / $c_res")";    
  856.         
  857.  
  858. #    echo -n "[";
  859.  
  860.     curchar=0
  861.     start=0
  862.     next=0
  863.     ct=0
  864.  
  865.     for i in $chunk $eof_limit-$eof_limit
  866.       do
  867.  
  868.       start=$(echo "$i" | "${CUT}" -f 1 -d "-")
  869.       
  870.       if [ "$next" != "$start" ]
  871.       then
  872.  
  873.           # This is start of hole
  874.           startchar="$(bc_calc 0 "$next / $c_res")"
  875.     
  876.           # This is end of hole
  877.           curchar="$(bc_calc 0 "$start / $c_res")"
  878.                     
  879.           # draw completed chars up to start of hole.
  880.           while [ "$ct" -lt "$startchar" ] ;do
  881.         echo -n "x"
  882.         ct=$[$ct+1] ;
  883.           done
  884.  
  885.           # our current tracker ($ct) is now at : $ct==$startchar
  886.           # OR is $ct = $startchar + 1 ONLY if precedent hole finished
  887.           #    in the same char this hole begins !!
  888.           
  889.           # as rounding occurs, we might have $startchar == $curchar
  890.           # but original hole is not null ! We must show that there's
  891.           # a hole in this char.
  892.  
  893.               # hole is bigger than 1 char 
  894.           if [ "$startchar" -lt "$curchar" ] ; then
  895.           # if current drawing position ($ct) is on startchar
  896.           if [ "$ct" == "$startchar" ] ;then
  897.               # draw the beginning of hole.
  898.               [ "$ct" != "$next_pos" ] && echo -n "|" || echo -n "*"
  899.               ct=$[$ct + 1] 
  900.           fi
  901.           
  902.           # mark char between startchar and curchar as hole.
  903.           while [ "$ct" -lt "$curchar" ] ; do
  904.             [ "$ct" != "$next_pos" ] && echo -n "." || echo -n "*"
  905.             ct=$[$ct + 1]
  906.           done
  907.  
  908.           # current tracker is now equal to curchar.
  909.  
  910.           # draw the end of hole.
  911.           if [ "$nb_stars" -gt "$curchar" ] ; then
  912.               [ "$ct" != "$next_pos" ] && echo -n "|" || echo -n "*"
  913.               ct=$[$ct + 1]
  914.           fi
  915.  
  916.           else
  917.           
  918.           # the only remaining possibility is that $startchar=$curchar
  919.           # this is the rounding possibility.
  920.  
  921.           # if [ "$startchar" == "$curchar" ] ; then
  922.               if [ "$nb_stars" -gt "$curchar" ] ; then
  923.               if [ "$ct" == "$next_pos" ] ; then
  924.                   [ "$ct" != "$next_pos" ] && echo -n "|" || 
  925.                   echo -n "*"
  926.                   ct=$[$ct + 1]
  927.               fi
  928.               fi
  929.           # fi
  930.               
  931.           fi
  932.       else
  933.           if [ "$start" == "$eof_limit" ]; then
  934.           while [ "$ct" -lt "$nb_stars" ] ;do
  935.               echo -n "x"
  936.               ct=$[$ct+1] ;
  937.           done
  938.           fi
  939.       fi
  940.       next=$(echo "$i" | "${CUT}" -f 2 -d "-")
  941.       
  942.     done
  943.  
  944.     else
  945.     echo -n "[ No Bar available the first launch ]"
  946.     fi
  947.  
  948.     last_chunk="$(get_last_chunk)"
  949.  
  950.     if [ "$eof_limit" != "$last_chunk" ]
  951.     then
  952.     echo "=== Bar was drawn from 0 to hypothetic end : $eof_limit"
  953.     else
  954.     echo "=== Bar was drawn from 0 to $eof_limit"
  955.     fi
  956. }
  957.  
  958.  
  959.  
  960. function show_info()
  961. {
  962.     echo "=== dd_rhelp INFO -" $(echo "$chunk" | "${WC}" -l) "chunks...";
  963.     jump=$(get_next_pos | "${CUT}" -f 1 -d ":")
  964.     [ "$jump" != "0" ] && echo -n "- Jump pos : $(get_next_pos | "${CUT}" -f 1 -d ":") "
  965.     if [ "$eof" == "nothing" ]
  966.     then
  967.       echo "- max file size : no limit found"
  968.     else
  969.       echo "- max file size : $eof"
  970.  
  971.       echo -en "- Biggest hole size : " "$(bc_calc 1 "$(get_next_pos | "${CUT}" -f 2 -d ":") * 2")" "k "
  972.     fi
  973.  
  974.   parsing="$logcontent"
  975.   total_errxfer="0";
  976.   total_succxfer="0";
  977.   total_xferd="0";
  978.  
  979.   while test "$parsing" 
  980.   do
  981.     firstline="$(echo "$parsing" | "${HEAD}" -n 1)"
  982.     parsing="$(echo "$parsing" | "${TAIL}" -n +2)"
  983.     
  984.     xferd="$(echo "$firstline" | "${CUT}" -f 2 -d ":" | "${CUT}" -f 2 -d "=")"
  985.     errxfer="$(echo "$firstline" | "${CUT}" -f 4 -d ":" | "${CUT}" -f 2 -d "=")"
  986.     succxfer="$(echo "$firstline" | "${CUT}" -f 5 -d ":" | "${CUT}" -f 2 -d "=")"
  987.  
  988.     total_errxfer="$(bc_calc 1 "$total_errxfer + $errxfer")"
  989.     total_succxfer="$(bc_calc 1 "$total_succxfer + $succxfer")"
  990.     total_xferd="$(bc_calc 1 "$total_xferd + $xferd")"
  991.  
  992.   done
  993.  
  994.  
  995.     size=0
  996.     cursize=0
  997.     start=0
  998.     next=0
  999.     for i in $chunk
  1000.     do
  1001.       next=$(echo "$i" | "${CUT}" -f 1 -d "-")
  1002.       if [ "$next" != "$start" ]
  1003.       then
  1004.       cursize="$(bc_calc 1 "$next - $start")"
  1005.       size="$(bc_calc 1 "$size + $cursize")"
  1006.       fi
  1007.       start=$(echo "$i" | "${CUT}" -f 2 -d "-")
  1008.     done
  1009.     echo -e "- total holes : ${size}k"
  1010.  
  1011.     echo -e "- xferd(succ/err) : ${total_xferd}k(${total_succxfer}k/${total_errxfer}k)"
  1012.  
  1013.     eof_limit=$(get_last_chunk)
  1014.  
  1015.     echo -en "- EOF "
  1016.  
  1017.     if  [ "$eof" != "nothing" ] && 
  1018.      [ "$eof" == "$eof_limit" ];then
  1019.     echo "is found and is at ${eof}k."
  1020.     else
  1021.     if [ "$eof" != "nothing" ]; then
  1022.         echo "is not found, but between ${eof_limit}k and ${eof}k."
  1023.     else
  1024.         echo "is not found, but greater than ${eof_limit}k"
  1025.     fi
  1026.     fi
  1027.  
  1028.     if [ "$size" == "0" ] && [ "$eof" != "nothing" ] && 
  1029.     [ "$eof" == "$eof_limit" ];then
  1030.     return 0
  1031.     else
  1032.     return 1
  1033.     fi
  1034. }
  1035.  
  1036.  
  1037. function process_log()
  1038. {
  1039.     data="$1"
  1040.  
  1041.     test -z data && return 0
  1042.     
  1043.     [ "$DEBUG" == "on" ] && echo -n "- cleaning data ["
  1044.     data=$(echo "$data" | "$GREP" -v "xferd: \+0.0k$")
  1045.     [ "$DEBUG" == "on" ] && echo -n "."
  1046.     data=$(echo "$data" | "$GREP" "$infoline" -A 1 | "${CUT}" -c 12-)
  1047.     [ "$DEBUG" == "on" ] && echo -n "."
  1048.     data=$(echo "$data" | "$SED" 's/^(info): ipos: \+//g')
  1049.     [ "$DEBUG" == "on" ] && echo -n "."
  1050.     data=$(echo "$data" | "$SED" 's/^ \+errs: \+/NR:/g')
  1051.     [ "$DEBUG" == "on" ] && echo -n "."
  1052.     data=$(echo "$data" | "$SED" 's/^ \+- \+errs: \+/RE:/g')
  1053.     [ "$DEBUG" == "on" ] && echo -n "."
  1054.     data=$(echo "$data" | "$SED" 's/^\([0-9\.]\+\)k, opos:.\+xferd: \+\([0-9\.]\+\)k$/ipos=\1:xferd=\2:/g')
  1055.     [ "$DEBUG" == "on" ] && echo -n "."
  1056.     data=$(echo "$data" | "$SED" 's/^\(RE\|NR\):[0-9]\+, errxfer: \+\([0-9\.]\+\)k, succxfer: \+\([0-9\.]\+\)k$/\1:errxfer=\2:succxfer=\3;/g')
  1057.     [ "$DEBUG" == "on" ] && echo -n "."
  1058.     data=$(echo "$data" | "${TR}" -d "\n")
  1059.     [ "$DEBUG" == "on" ] && echo -n "."
  1060.     data=$(echo "$data" | "${TR}" ";" "\n")
  1061.     [ "$DEBUG" == "on" ] && echo ".]"
  1062.     
  1063.     # All info now take one line per entry, and field are separated by ":"
  1064.  
  1065.     [ "$DEBUG" == "on" ] && echo -n "- processing data ["
  1066.     
  1067.   # finding start of chunks
  1068.     
  1069.     parsing="$data"
  1070.     # chunk=""
  1071.     
  1072.     while test "$parsing" ;do
  1073.     firstline="$(echo "$parsing" | "${HEAD}" -n 1)"
  1074.     parsing="$(echo "$parsing" | "${TAIL}" -n +2)"
  1075.     
  1076.     ipos="$(echo $firstline | "${CUT}" -f 1 -d ":" | "${CUT}" -f 2 -d "=")"
  1077.     xferd="$(echo $firstline | "${CUT}" -f 2 -d ":" | "${CUT}" -f 2 -d "=")"
  1078.     rev="$(echo $firstline | "${CUT}" -f 3 -d ":")"
  1079.     errxfer="$(echo $firstline | "${CUT}" -f 4 -d ":" | "${CUT}" -f 2 -d "=")"
  1080.     succxfer="$(echo $firstline | "${CUT}" -f 5 -d ":" | "${CUT}" -f 2 -d "=")"
  1081.     
  1082.     if [ "$rev" == "RE" ] ; then
  1083.         start="$ipos"
  1084.         stop="$(bc_calc 1 "$ipos + $xferd")"
  1085.     else
  1086.         start="$(bc_calc 1 "$ipos - $xferd")"
  1087.         stop="$ipos"
  1088.     fi
  1089.  
  1090.     chunkline="$start-$stop"
  1091.     add_chunk $chunkline
  1092.  
  1093.     [ "$DEBUG" == "on" ] && echo -n "."
  1094.     done
  1095.  
  1096.     [ "$DEBUG" == "on" ] && echo "]";
  1097.  
  1098.     if test "$logcontent";then
  1099.     logcontent="$(echo -en "$logcontent\n$data")"
  1100.     else
  1101.     logcontent="$data";
  1102.     fi
  1103. }
  1104.  
  1105. function load_log()
  1106. {
  1107. #
  1108. # loading into memory Summary info found in log file...
  1109. #
  1110.     # line number of last save_log entry...
  1111.     lnb_save=$("${CAT}" -n "$logfile" | "$tr" -d "\\r" | grep "chunk:" -A 2 | "${TAIL}" -n 3)
  1112.     
  1113.  
  1114.     if test "$lnb_save" ;then
  1115.     lnb_save=$(echo $lnb_save | "${HEAD}" -n 1 | cut -f 1 -d " ")
  1116.  
  1117.     end_log="$(cat "$logfile" | "$tr" -d "\\r" | "${TAIL}" -n "+$lnb_save")"
  1118.  
  1119.     last_lines=$(echo "$end_log" | grep "chunk:" -A 2 | "${TAIL}" -n 3)
  1120.     
  1121.     log=$(echo "$last_lines" | "$GREP" "chunk" | "${TAIL}" -n 1 )
  1122.     log1=$(echo "$last_lines" | "$GREP" "logcontent" | "${TAIL}" -n 1 )
  1123.     log2=$(echo "$last_lines" | "$GREP" "eof" | "${TAIL}" -n 1 )
  1124.     
  1125.     if test "$log" && test "$log1" && test "$log2" ;then
  1126.         chunk="$(echo "$log" | "${CUT}" -f 2- -d ":" | "${TR}" ":" "\n")"
  1127.  
  1128.         logcontent="$(echo "$log1" | "${CUT}" -f 2- -d : | "${TR}" ";" "\n")"
  1129.         eof="$(echo "$log2" | "${CUT}" -f 2 -d ":")"
  1130.         
  1131.         log=$(echo "$end_log" | "$GREP" "$string" -A 3 )
  1132.         
  1133.         process_log "$log"
  1134.         return 0
  1135.     else
  1136.         echo "Bad log format !!! Fallback to slow mode..."
  1137.     fi
  1138.  
  1139.     fi
  1140.     
  1141.     # select all summary info of dd_rescue
  1142.     log=$("${CAT}" "$logfile" | "$tr" -d "\\r" | "$GREP" "$string" -A 3 )
  1143.  
  1144.     # Set EOF with log.
  1145.     get_eof
  1146.  
  1147.     # Sets logcontent AND chunk
  1148.     process_log "$log"
  1149.  
  1150. }
  1151.  
  1152. function save_log()
  1153. {
  1154.     echo "=== COMPUTED VERSION OF LOG :" >> "$logfile"
  1155.     echo "chunk:$(echo -n "$chunk" | "${TR}" "\n" : )" >> "$logfile"
  1156.     echo "logcontent:$(echo -n "$logcontent" | "${TR}" "\n" ";" )" >> "$logfile"
  1157.     echo "eof:$eof" >> "$logfile"
  1158. }
  1159.  
  1160.  
  1161.  
  1162. # === beginning of real code
  1163.  
  1164. load_log
  1165.  
  1166. # Save computed version of log for next time.
  1167. save_log
  1168.  
  1169. if [ "$opt" == "info" ] && test -z "$logcontent"
  1170. then
  1171.     echo "No Info found in log..."
  1172.     exit 0;
  1173. fi
  1174.  
  1175. if [ "$opt" != "info" ];then
  1176.     DD_RESCUE="$(get_valid_dd_rescue)"
  1177.     [ "$?" != "0" ] && exit 1
  1178. else
  1179.     echo "$(show_info)"
  1180.     if [ "$?" == "0" ] ; then 
  1181.        echo "ALL your data has been dd_rescued !!"
  1182.     else 
  1183.        show_bar
  1184.     fi
  1185.     exit 0
  1186. fi
  1187.  
  1188. while [ "$(echo "$chunk" | "${WC}" -l)" != "1" ] ||
  1189.       [ "$(get_last_chunk)" != "$eof" ] ||
  1190.       [ "$eof" == "nothing" ]
  1191. do
  1192.  
  1193.   info="$(show_info)" 
  1194.   if [ "$?" == "0" ] ; then 
  1195.       echo "$info"
  1196.       echo "ALL your data has been dd_rescued !!"
  1197.       # show_bar
  1198.       exit 0
  1199.   fi
  1200.   
  1201.   if [ "$logcontent" != "" ] ;then
  1202.       echo "$info";
  1203.       show_bar
  1204.   fi
  1205.  
  1206.  
  1207.   [ "$DEBUG" == "on" ] && [ "$opt" == "info" ] && [ "$chunk" != "" ] && echo -en "Chunks that were dd_rescued (in k):\
  1208.  \n$chunk\n"
  1209.   [ "$opt" == "info" ] && exit 1;
  1210.  
  1211.   next_pos="$(get_next_pos | "${CUT}" -f 1 -d ":")k"
  1212.   count="$(get_next_pos | "${CUT}" -f 2 -d ":")k"
  1213.  
  1214.  
  1215.   if [ "$next_pos" != "0k" ]
  1216.   then
  1217.       echo "=== launched via '$exname' at $next_pos, $count <<< ===" >> "$logfile"
  1218.       echo "=== launched via '$exname' at $next_pos, $count <<< ==="
  1219.       ${DD_RESCUE} -r -s "$next_pos" -l "$logfile" -e "$max_err" -B "$min_bs" -b "$max_bs" -m "$count" "$infile" "$outfile"
  1220.       swallow_last_summary
  1221.   fi
  1222.  
  1223.   if [ "$next_pos" != "${eof}k" ]
  1224.   then
  1225.       if [ "$eof" == "nothing" ]
  1226.       then
  1227.       count=0;
  1228.       fi
  1229.  
  1230.       echo "=== launched via '$exname' at $next_pos, $count >>> ===" >> "$logfile"
  1231.       echo "=== launched via '$exname' at $next_pos, $count >>> ==="
  1232.       ${DD_RESCUE} -s "$next_pos" -l "$logfile" -e "$max_err" -B "$min_bs" -b "$max_bs" -m "$count" "$infile" "$outfile"
  1233.       swallow_last_summary
  1234.   fi
  1235.  
  1236. done
  1237. # End dd_rhelp
  1238.