Patch-ID: bash40-021
[bash.git] / examples / scripts / dd-ex.sh
blobfafc83f2516d249e32a6abb268f13444a58a88d1
1 #!/bin/sh
3 # this is a line editor using only /bin/sh, /bin/dd and /bin/rm
5 # /bin/rm is not really required, but it is nice to clean up temporary files
7 PATH=
8 dd=/bin/dd
9 rm=/bin/rm
11 # temporary files we might need
12 tmp=/tmp/silly.$$
13 ed=/tmp/ed.$$
14 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
16 # from now on, no more rm - the above trap is enough
17 unset rm
19 # we do interesting things with IFS, but better save it...
20 saveIFS="$IFS"
22 # in case "echo" is not a shell builtin...
24 Echo () {
25 case "$1" in
26 -n) shift
27 $dd of=$tmp 2>/dev/null <<EOF
29 EOF
30 IFS="+"
31 set `$dd if=$tmp bs=1 of=/dev/null skip=1 2>&1`
32 IFS="$saveIFS"
33 $dd if=$tmp bs=1 count=$1 2>/dev/null
35 *) $dd 2>/dev/null <<EOF
37 EOF
39 esac
42 # this is used to generate garbage files
44 true () {
45 return 0
48 false () {
49 return 1
52 zero () {
53 ( trap 'go=false' 13
54 go=true
55 while $go
57 $dd "if=$0"
58 case "$?" in
59 0) ;;
60 *) go=false ;;
61 esac
62 done
63 ) 2>/dev/null
66 # arithmetic using dd!
68 # add variable n1 n2 n3...
69 # assigns n1+n2+n3+... to variable
71 add () {
72 result="$1"
73 shift
74 $dd if=/dev/null of=$tmp bs=1 2>/dev/null
75 for n in "$@"
77 case "$n" in
78 0) ;;
79 *) zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null
80 ( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null
81 $dd if=$tmp.2 of=$tmp 2>/dev/null
83 esac
84 done
85 IFS="+"
86 set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
87 IFS="$saveIFS"
88 eval $result='$1'
91 # subtract variable n1 n2
92 # subtracts n2 from n1, assigns result to variable
94 subtract () {
95 result="$1"
96 zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null
97 IFS="+"
98 set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1`
99 IFS="$saveIFS"
100 case "$1" in
101 dd*) set 0 ;;
102 esac
103 eval $result='$1'
106 # multiply variable n1 n2
107 # variable = n1 * n2
109 multiply () {
110 result="$1"
111 zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null
112 IFS="+"
113 set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
114 IFS="$saveIFS"
115 eval $result='$1'
118 # divide variable n1 n2
119 # variable = int( n1 / n2 )
121 divide () {
122 result="$1"
123 zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null
124 IFS="+"
125 set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1`
126 IFS="$saveIFS"
127 eval $result='$1'
130 # compare variable n1 n2 sets variable to lt if n1<n2, gt if n1>n2, eq if n1==n2
132 compare () {
133 res="$1"
134 n1="$2"
135 n2="$3"
136 subtract somename "$n1" "$n2"
137 case "$somename" in
138 0) ;;
139 *) eval $res=gt; return;
140 esac
141 subtract somename "$n2" "$n1"
142 case "$somename" in
143 0) ;;
144 *) eval $res=lt; return;
145 esac
146 eval $res=eq
149 # lt n1 n2 returns true if n1 < n2
151 lt () {
152 n1="$1"
153 n2="$2"
154 subtract somename "$n2" "$n1"
155 case "$somename" in
156 0) return 1 ;;
157 esac
158 return 0
161 # le n1 n2 returns true if n1 <= n2
163 le () {
164 n1="$1"
165 n2="$2"
166 subtract somename "$n1" "$n2"
167 case "$somename" in
168 0) return 0 ;;
169 esac
170 return 1
173 # gt n1 n2 returns true if n1 > n2
175 gt () {
176 n1="$1"
177 n2="$2"
178 subtract somename "$n1" "$n2"
179 case "$somename" in
180 0) return 1 ;;
181 esac
182 return 0
185 # ge n1 n2 returns true if n1 >= n2
187 ge () {
188 n1="$1"
189 n2="$2"
190 subtract somename "$n2" "$n1"
191 case "$somename" in
192 0) return 0 ;;
193 esac
194 return 1
197 # useful functions for the line editor
199 # open a file - copy it to the buffers
201 open () {
202 file="$1"
203 set `$dd "if=$file" of=/dev/null 2>&1`
204 case "$1" in
205 dd*) return 1
206 esac
207 # copy the first line to $ed.c
208 go=true
209 len=0
210 while $go
212 case "`$dd "if=$file" bs=1 skip=$len count=1 2>/dev/null`" in
213 ?*) go=true ;;
214 *) go=false ;;
215 esac
216 add len 1 $len
217 done
218 # now $len is the length of the first line (including newline)
219 $dd "if=$file" bs=1 count=$len of=$ed.c 2>/dev/null
220 $dd "if=$file" bs=1 skip=$len of=$ed.b 2>/dev/null
221 $dd if=/dev/null of=$ed.a 2>/dev/null
222 lineno=1
225 # save a file - copy the buffers to the file
227 save () {
228 # make a backup copy of the original
229 $dd "if=$1" "of=$1.bak" 2>/dev/null
230 # and save
231 ( $dd if=$ed.a; $dd if=$ed.c; $dd if=$ed.b ) > "$1" 2>/dev/null
234 # replace n1 n2 bla replaces n2 chars of current line, starting n1-th
236 replace () {
237 $dd if=$ed.c of=$tmp.1 bs=1 "count=$1" 2>/dev/null
238 ( $dd if=$ed.c "skip=$1" bs=1 | $dd of=$tmp.2 bs=1 "skip=$2" ) 2>/dev/null
239 shift
240 shift
241 ( $dd if=$tmp.1; Echo -n "$@"; $dd if=$tmp.2 ) > $tmp.3 2>/dev/null
242 $dd if=$tmp.3 of=$ed.c 2>/dev/null
245 # rstring n s bla
246 # replace the n-th occurence of s with bla
248 rstring () {
249 n="$1"
250 shift;
251 # first we have to find it - this is fun!
252 # we have $tmp.4 => text before string, $tmp.5 => text after
253 $dd if=/dev/null of=$tmp.4 2>/dev/null
254 $dd if=$ed.c of=$tmp.5 2>/dev/null
255 string="$1"
256 shift
257 $dd of=$tmp.6 2>/dev/null <<EOF
260 while :
262 case "`$dd if=$tmp.5 2>/dev/null`" in
263 $string*)
264 if lt $n 2
265 then
266 # now we want to replace the string
267 Echo -n "$@" > $tmp.2
268 Echo -n "$string" > $tmp.1
269 IFS="+"
270 set `$dd bs=1 if=$tmp.1 of=/dev/null 2>&1`
271 IFS="$saveIFS"
272 slen=$1
273 IFS="+"
274 ( $dd if=$tmp.4; $dd if=$tmp.2; $dd if=$tmp.5 bs=1 skip=$slen ) \
275 2>/dev/null > $tmp
276 $dd if=$tmp of=$ed.c 2>/dev/null
277 return 0
278 else
279 subtract n $n 1
280 ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null
281 $dd if=$tmp of=$tmp.4 2>/dev/null
282 # and remove it from $tmp.5
283 $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null
284 $dd if=$tmp of=$tmp.5 2>/dev/null
287 ?*) # add one more byte...
288 ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null
289 $dd if=$tmp of=$tmp.4 2>/dev/null
290 # and remove it from $tmp.5
291 $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null
292 $dd if=$tmp of=$tmp.5 2>/dev/null
294 *) # not found
295 return 1
297 esac
298 done
301 # skip to next line
302 next () {
303 add l $lineno 1
304 ( $dd if=$ed.a; $dd if=$ed.c ) 2>/dev/null > $tmp.3
305 $dd if=$ed.b of=$tmp.4 2>/dev/null
306 open $tmp.4
307 $dd if=$tmp.3 of=$ed.a 2>/dev/null
308 lineno=$l
311 # delete current line
312 delete () {
313 l=$lineno
314 $dd if=$ed.a 2>/dev/null > $tmp.1
315 $dd if=$ed.b of=$tmp.2 2>/dev/null
316 open $tmp.2
317 $dd if=$tmp.1 of=$ed.a 2>/dev/null
318 lineno=$l
321 # insert before current line (without changing current)
322 insert () {
323 ( $dd if=$ed.a; Echo "$@" ) 2>/dev/null > $tmp.1
324 $dd if=$tmp.1 of=$ed.a 2>/dev/null
325 add lineno $lineno 1
328 # previous line
329 prev () {
330 case "$lineno" in
331 1) ;;
332 *) subtract lineno $lineno 1
333 # read last line of $ed.a
334 IFS='+'
335 set `$dd if=$ed.a of=/dev/null bs=1 2>&1`
336 IFS="$saveIFS"
337 size=$1
338 # empty?
339 case "$size" in
340 0) return ;;
341 esac
342 subtract size $size 1
343 # skip final newline
344 case "$size" in
345 0) ;;
346 *) subtract size1 $size 1
347 case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in
348 ?*) ;;
349 *) size=$size1 ;;
350 esac
352 esac
353 go=true
354 while $go
356 case "$size" in
357 0) go=false ;;
358 *) case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in
359 ?*) go=true; subtract size $size 1 ;;
360 *) go=false; add size $size 1 ;;
361 esac
363 esac
364 done
365 # now $size is the size of the first n-1 lines
366 # add $ed.c to $ed.b
367 ( $dd if=$ed.c; $dd if=$ed.b ) 2>/dev/null > $tmp.5
368 $dd if=$tmp.5 of=$ed.b 2>/dev/null
369 # move line to ed.c
370 case "$size" in
371 0) $dd if=$ed.a of=$ed.c 2>/dev/null
372 $dd if=/dev/null of=$tmp.5 2>/dev/null
374 *) $dd if=$ed.a of=$ed.c bs=1 skip=$size 2>/dev/null
375 $dd if=$ed.a of=$tmp.5 bs=1 count=$size 2>/dev/null
377 esac
378 # move rest to ed.a
379 $dd if=$tmp.5 of=$ed.a 2>/dev/null
381 esac
384 # goes to a given line
385 goto () {
386 rl="$1"
387 compare bla "$rl" $lineno
388 case "$bla" in
389 eq) return
391 gt) while gt "$rl" $lineno
393 next
394 done
396 lt) while lt "$rl" $lineno
398 prev
399 done
401 esac
404 lineout () {
405 Echo -n "$lineno: "
406 $dd if=$ed.c 2>/dev/null
409 state=closed
410 name=
411 autoprint=true
413 while true
415 Echo -n '> '
416 read cmd arg
417 case "$cmd:$state" in
418 open:open) Echo "There is a file open already" ;;
419 open:*) if open "$arg"
420 then state=open; name="$arg"; $autoprint
421 else Echo "Cannot open $arg"
424 new:open) Echo "There is a file open already" ;;
425 new:*) open "$arg"
426 state=open
427 name="$arg"
428 $autoprint
430 close:changed) Echo "Use 'discard' or 'save'" ;;
431 close:closed) Echo "Closed already" ;;
432 close:*) state=closed ;;
433 save:closed) Echo "There isn't a file to save" ;;
434 save:*) case "$arg" in
435 ?*) save "$arg" ;;
436 *) save "$name" ;;
437 esac
438 state=open
440 discard:changed) Echo "Your problem!"; state=closed ;;
441 discard:*) state=closed ;;
442 print:closed) Echo "No current file" ;;
443 print:*) lineout ;;
444 goto:closed) Echo "No current file" ;;
445 goto:*) goto "$arg"; $autoprint ;;
446 next:closed) Echo "No current file" ;;
447 next:*) next; $autoprint ;;
448 prev:closed) Echo "No current file" ;;
449 prev:*) prev; $autoprint ;;
450 name:closed) Echo "No current file" ;;
451 name:*) name="$arg" ;;
452 replace:closed) Echo "No current file" ;;
453 replace:*) if rstring 1 $arg
454 then state=changed; $autoprint
455 else Echo "Not found"
458 nreplace:closed) Echo "No current file" ;;
459 nreplace:*) if rstring $arg
460 then state=changed; $autoprint
461 else Echo "Not found"
464 delete:closed) Echo "No current file" ;;
465 delete:*) delete; state=changed; $autoprint ;;
466 insert:closed) Echo "No current file" ;;
467 insert:*) insert "$arg"; prev; state=changed; $autoprint ;;
468 quit:changed) Echo "Use 'save' or 'discard'" ;;
469 quit:*) Echo "bye"; exit;;
470 autoprint:*) autoprint="lineout" ;;
471 noprint:*) autoprint="" ;;
472 :*) ;;
473 *) Echo "Command not understood" ;;
474 esac
475 done