?? dd-ex.sh
字號:
#!/bin/sh# this is a line editor using only /bin/sh, /bin/dd and /bin/rm# /bin/rm is not really required, but it is nice to clean up temporary filesPATH=dd=/bin/ddrm=/bin/rm# temporary files we might needtmp=/tmp/silly.$$ed=/tmp/ed.$$trap "$rm -f $tmp $tmp.1 $tmp.2 $tmp.3 $tmp.4 $tmp.5 $tmp.6 $ed.a $ed.b $ed.c; exit" 0 1 2 3# from now on, no more rm - the above trap is enoughunset rm# we do interesting things with IFS, but better save it...saveIFS="$IFS"# in case "echo" is not a shell builtin...Echo () {case "$1" in -n) shift $dd of=$tmp 2>/dev/null <<EOF $@EOF IFS="+" set `$dd if=$tmp bs=1 of=/dev/null skip=1 2>&1` IFS="$saveIFS" $dd if=$tmp bs=1 count=$1 2>/dev/null ;; *) $dd 2>/dev/null <<EOF $@EOF ;;esac}# this is used to generate garbage filestrue () { return 0}false () { return 1}zero () { ( trap 'go=false' 13 go=true while $go do $dd "if=$0" case "$?" in 0) ;; *) go=false ;; esac done ) 2>/dev/null}# arithmetic using dd!# add variable n1 n2 n3...# assigns n1+n2+n3+... to variableadd () { result="$1" shift $dd if=/dev/null of=$tmp bs=1 2>/dev/null for n in "$@" do case "$n" in 0) ;; *) zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null ( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null $dd if=$tmp.2 of=$tmp 2>/dev/null ;; esac done IFS="+" set `$dd if=$tmp bs=1 of=/dev/null 2>&1` IFS="$saveIFS" eval $result='$1'}# subtract variable n1 n2# subtracts n2 from n1, assigns result to variablesubtract () { result="$1" zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null IFS="+" set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1` IFS="$saveIFS" case "$1" in dd*) set 0 ;; esac eval $result='$1'}# multiply variable n1 n2# variable = n1 * n2multiply () { result="$1" zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null IFS="+" set `$dd if=$tmp bs=1 of=/dev/null 2>&1` IFS="$saveIFS" eval $result='$1'}# divide variable n1 n2# variable = int( n1 / n2 )divide () { result="$1" zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null IFS="+" set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1` IFS="$saveIFS" eval $result='$1'}# compare variable n1 n2 sets variable to lt if n1<n2, gt if n1>n2, eq if n1==n2compare () { res="$1" n1="$2" n2="$3" subtract somename "$n1" "$n2" case "$somename" in 0) ;; *) eval $res=gt; return; esac subtract somename "$n2" "$n1" case "$somename" in 0) ;; *) eval $res=lt; return; esac eval $res=eq}# lt n1 n2 returns true if n1 < n2lt () { n1="$1" n2="$2" subtract somename "$n2" "$n1" case "$somename" in 0) return 1 ;; esac return 0}# le n1 n2 returns true if n1 <= n2le () { n1="$1" n2="$2" subtract somename "$n1" "$n2" case "$somename" in 0) return 0 ;; esac return 1}# gt n1 n2 returns true if n1 > n2gt () { n1="$1" n2="$2" subtract somename "$n1" "$n2" case "$somename" in 0) return 1 ;; esac return 0}# ge n1 n2 returns true if n1 >= n2ge () { n1="$1" n2="$2" subtract somename "$n2" "$n1" case "$somename" in 0) return 0 ;; esac return 1}# useful functions for the line editor# open a file - copy it to the buffersopen () { file="$1" set `$dd "if=$file" of=/dev/null 2>&1` case "$1" in dd*) return 1 esac # copy the first line to $ed.c go=true len=0 while $go do case "`$dd "if=$file" bs=1 skip=$len count=1 2>/dev/null`" in ?*) go=true ;; *) go=false ;; esac add len 1 $len done # now $len is the length of the first line (including newline) $dd "if=$file" bs=1 count=$len of=$ed.c 2>/dev/null $dd "if=$file" bs=1 skip=$len of=$ed.b 2>/dev/null $dd if=/dev/null of=$ed.a 2>/dev/null lineno=1}# save a file - copy the buffers to the filesave () { # make a backup copy of the original $dd "if=$1" "of=$1.bak" 2>/dev/null # and save ( $dd if=$ed.a; $dd if=$ed.c; $dd if=$ed.b ) > "$1" 2>/dev/null}# replace n1 n2 bla replaces n2 chars of current line, starting n1-threplace () { $dd if=$ed.c of=$tmp.1 bs=1 "count=$1" 2>/dev/null ( $dd if=$ed.c "skip=$1" bs=1 | $dd of=$tmp.2 bs=1 "skip=$2" ) 2>/dev/null shift shift ( $dd if=$tmp.1; Echo -n "$@"; $dd if=$tmp.2 ) > $tmp.3 2>/dev/null $dd if=$tmp.3 of=$ed.c 2>/dev/null}# rstring n s bla# replace the n-th occurence of s with blarstring () { n="$1" shift; # first we have to find it - this is fun! # we have $tmp.4 => text before string, $tmp.5 => text after $dd if=/dev/null of=$tmp.4 2>/dev/null $dd if=$ed.c of=$tmp.5 2>/dev/null string="$1" shift $dd of=$tmp.6 2>/dev/null <<EOF$@EOF while : do case "`$dd if=$tmp.5 2>/dev/null`" in $string*) if lt $n 2 then # now we want to replace the string Echo -n "$@" > $tmp.2 Echo -n "$string" > $tmp.1 IFS="+" set `$dd bs=1 if=$tmp.1 of=/dev/null 2>&1` IFS="$saveIFS" slen=$1 IFS="+" ( $dd if=$tmp.4; $dd if=$tmp.2; $dd if=$tmp.5 bs=1 skip=$slen ) \ 2>/dev/null > $tmp $dd if=$tmp of=$ed.c 2>/dev/null return 0 else subtract n $n 1 ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null $dd if=$tmp of=$tmp.4 2>/dev/null # and remove it from $tmp.5 $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null $dd if=$tmp of=$tmp.5 2>/dev/null fi ;; ?*) # add one more byte... ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null $dd if=$tmp of=$tmp.4 2>/dev/null # and remove it from $tmp.5 $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null $dd if=$tmp of=$tmp.5 2>/dev/null ;; *) # not found return 1 ;; esac done}# skip to next linenext () { add l $lineno 1 ( $dd if=$ed.a; $dd if=$ed.c ) 2>/dev/null > $tmp.3 $dd if=$ed.b of=$tmp.4 2>/dev/null open $tmp.4 $dd if=$tmp.3 of=$ed.a 2>/dev/null lineno=$l}# delete current linedelete () { l=$lineno $dd if=$ed.a 2>/dev/null > $tmp.1 $dd if=$ed.b of=$tmp.2 2>/dev/null open $tmp.2 $dd if=$tmp.1 of=$ed.a 2>/dev/null lineno=$l}# insert before current line (without changing current)insert () { ( $dd if=$ed.a; Echo "$@" ) 2>/dev/null > $tmp.1 $dd if=$tmp.1 of=$ed.a 2>/dev/null add lineno $lineno 1}# previous lineprev () { case "$lineno" in 1) ;; *) subtract lineno $lineno 1 # read last line of $ed.a IFS='+' set `$dd if=$ed.a of=/dev/null bs=1 2>&1` IFS="$saveIFS" size=$1 # empty? case "$size" in 0) return ;; esac subtract size $size 1 # skip final newline case "$size" in 0) ;; *) subtract size1 $size 1 case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in ?*) ;; *) size=$size1 ;; esac ;; esac go=true while $go do case "$size" in 0) go=false ;; *) case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in ?*) go=true; subtract size $size 1 ;; *) go=false; add size $size 1 ;; esac ;; esac done # now $size is the size of the first n-1 lines # add $ed.c to $ed.b ( $dd if=$ed.c; $dd if=$ed.b ) 2>/dev/null > $tmp.5 $dd if=$tmp.5 of=$ed.b 2>/dev/null # move line to ed.c case "$size" in 0) $dd if=$ed.a of=$ed.c 2>/dev/null $dd if=/dev/null of=$tmp.5 2>/dev/null ;; *) $dd if=$ed.a of=$ed.c bs=1 skip=$size 2>/dev/null $dd if=$ed.a of=$tmp.5 bs=1 count=$size 2>/dev/null ;; esac # move rest to ed.a $dd if=$tmp.5 of=$ed.a 2>/dev/null ;; esac}# goes to a given linegoto () { rl="$1" compare bla "$rl" $lineno case "$bla" in eq) return ;; gt) while gt "$rl" $lineno do next done ;; lt) while lt "$rl" $lineno do prev done ;; esac}lineout () { Echo -n "$lineno: " $dd if=$ed.c 2>/dev/null}state=closedname=autoprint=truewhile truedo Echo -n '> ' read cmd arg case "$cmd:$state" in open:open) Echo "There is a file open already" ;; open:*) if open "$arg" then state=open; name="$arg"; $autoprint else Echo "Cannot open $arg" fi ;; new:open) Echo "There is a file open already" ;; new:*) open "$arg" state=open name="$arg" $autoprint ;; close:changed) Echo "Use 'discard' or 'save'" ;; close:closed) Echo "Closed already" ;; close:*) state=closed ;; save:closed) Echo "There isn't a file to save" ;; save:*) case "$arg" in ?*) save "$arg" ;; *) save "$name" ;; esac state=open ;; discard:changed) Echo "Your problem!"; state=closed ;; discard:*) state=closed ;; print:closed) Echo "No current file" ;; print:*) lineout ;; goto:closed) Echo "No current file" ;; goto:*) goto "$arg"; $autoprint ;; next:closed) Echo "No current file" ;; next:*) next; $autoprint ;; prev:closed) Echo "No current file" ;; prev:*) prev; $autoprint ;; name:closed) Echo "No current file" ;; name:*) name="$arg" ;; replace:closed) Echo "No current file" ;; replace:*) if rstring 1 $arg then state=changed; $autoprint else Echo "Not found" fi ;; nreplace:closed) Echo "No current file" ;; nreplace:*) if rstring $arg then state=changed; $autoprint else Echo "Not found" fi ;; delete:closed) Echo "No current file" ;; delete:*) delete; state=changed; $autoprint ;; insert:closed) Echo "No current file" ;; insert:*) insert "$arg"; prev; state=changed; $autoprint ;; quit:changed) Echo "Use 'save' or 'discard'" ;; quit:*) Echo "bye"; exit;; autoprint:*) autoprint="lineout" ;; noprint:*) autoprint="" ;; :*) ;; *) Echo "Command not understood" ;; esacdone
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -