[comp.sources.amiga] v90i214: MSH 1.30 - Messydos File System Handler, Part02/06

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (07/16/90)

Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 214
Archive-name: devices/msh-1.30/part02

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 6)."
# Contents:  doc/dev.man.uu src/hanlock.c src/hanmain.c
# Wrapped by tadguy@xanth on Sun Jul 15 19:59:05 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'doc/dev.man.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'doc/dev.man.uu'\"
else
echo shar: Extracting \"'doc/dev.man.uu'\" \(15215 characters\)
sed "s/^X//" >'doc/dev.man.uu' <<'END_OF_FILE'
Xbegin 664 dev.man
XM"B`@("`@;65S<WED:7-K+F1E=FEC92@I("`@($%M:6=A(%!R;V=R86UM97(GW
XM<R!-86YU86P@("`@(&UE<W-Y9&ES:RYD979I8V4H*0H*"@H@("`@()LQ;5-9)
XM3D]04UE3(`H@("`@("`@("`@FS!M(VEN8VQU9&4@/&5X96,O:6\N:#X*("`@;
XM("`@("`@("-I;F-L=61E(#QD979I8V5S+W1R86-K9&ES:RYH/@H@("`@("`@3
XM("`@(VEN8VQU9&4@/&1E=BYH/@H*("`@("`@("`@($]P96Y$979I8V4H(FUEH
XM<W-Y9&ES:RYD979I8V4B+"!5;FET+"!)3T5X=%1$+"!&;&%G<RD["B`@("`@J
XM("`@("!"96=I;DE/*$E/17AT5$0I.PH@("`@("`@("`@0VQO<V5$979I8V4H"
XM24]%>'141"D["@H@("`@()LQ;55304=%(`H@("`@("`@("`@FS!M365S<WED^
XM:7-K+F1E=FEC92`@8V%N("!B92`@=7-E9"!I;G-T96%D(&]F('1R86-K9&ES!
XM:RYD979I8V4N"B`@("`@("`@("!!;&P@8W5R<F5N="!T<F%C:V1I<VLN9&5V*
XM:6-E(&-O;6UA;F1S(&%R92`@<W5P<&]R=&5D+B`@("!4:&4*("`@("`@("`@:
XM(&]N;'D@(&9U;F-T:6]N86P@9&EF9F5R96YC92!I<R!T:&4@9F]R;6%T(&]F[
XM('1H92!D:7-K+"!W:&EC:`H@("`@("`@("`@:7,@8V]M<&%T:6)L92!W:71H-
XM(&UE<W-Y9&]S('-Y<W1E;7,N("`*"B`@("`@FS%M0T]-34%.1%,@"B`@("`@&
XM("`@("";,&U3=7!P;W)T960@8V]M;6%N9',@87)E.B`*"B`@("`@("`@("`]3
XM($--1%])3E9!3$E$("`@("`]($--1%]215-%5"`@("`@("`J($--1%]214%$&
XM"B`@("`@("`@("`J($--1%]74DE412`@("`@("`J($--1%]54$1!5$4@("`@R
XM("`]($--1%]#3$5!4@H@("`@("`@("`@/2!#341?4U1/4"`@("`@("`@/2!#W
XM341?4U1!4E0@("`@("`@/2!#341?1DQ54T@*("`@("`@("`@("`@5$1?34]4A
XM3U(@("`@("`@(#T@5$1?4T5%2R`@("`@("`@("H@5$1?1D]234%4"B`@("`@Q
XM("`@("`@(%1$7U)%34]612`@("`@("`@(%1$7T-(04Y'14Y532`@("`@(%1$J
XM7T-(04Y'15-4051%"B`@("`@("`@("`@(%1$7U!23U135$%455,@("`@(%1$H
XM7U)!5U)%040@("`@("`@(%1$7U)!5U=2251%"B`@("`@("`@("`@(%1$7T=%>
XM5$12259%5%E012`@(%1$7T=%5$Y5351204-+4R`](%1$7T%$1$-(04Y'14E.Y
XM5`H@("`@("`@("`@/2!41%]214U#2$%.1T5)3E0@*B!%5$1?5U))5$4@("`@8
XM("`@*B!%5$1?4D5!1`H@("`@("`@("`@("!%5$1?34]43U(@("`@("`@/2!%;
XM5$1?4T5%2R`@("`@("`@*B!%5$1?1D]234%4"B`@("`@("`@("`J($541%]53
XM4$1!5$4@("`@("`]($541%]#3$5!4B`@("`@("`@($541%]205=214%$"B`@:
XM("`@("`@("`@($541%]205=74DE410H*("`@("`@("`@(%1H92!C;VUM86YDE
XM<R!M87)K960@=VET:"`J(&%R92!H86YD;&5D(&EN(&ET<R!O=VX@9F]R;6%TY
XM("!B>0H@("`@("`@("`@;65S<WED:7-K+B`@(%1H92`@8V]M;6%N9',@(&UA0
XM<FME9"`@=VET:"`@/2`@87)E("!H86YD;&5D(&)Y"B`@("`@("`@("!M97-SD
XM>61I<VL@:6X@=&AE('-A;64@=V%Y(&%S('1R86-K9&ES:R!S=7!P;W-E9"`@:
XM:7,@('1O("!D;RX*("`@("`@("`@(%1H92`@;W1H97(@(&-O;6UA;F1S("!A`
XM<F4@<V5N="!S=')A:6=H="!T:')O=6=H('1O('1R86-K9&ES:PH@("`@("`@E
XM("`@86YD(&%R92!S=6)J96-T('1O(&%L;"!I=',@9FQA=W,@86YD(&9E871U,
XM<F5S+B`@"B`@("`@("`@("!4:&4@97AT96YD960@8V]M;6%N9',@<W5P<&]R/
XM="!T:&4@9&ES:R`@8VAA;F=E("!C;W5N="`@;VYL>2X*("`@("`@("`@(%1HE
XM92`@<V5C=&]R("!L86)E;"!P;VEN=&5R("AI;W1D7U-E8TQA8F5L*2!I<R!I6
XM9VYO<F5D+"!S:6YC90H@("`@("`@("`@;65S<WD@9F]R;6%T=&5D(&1I<VMS\
XM(&1O;B=T(&AA=F4@86YY(&QA8F5L(&%R96%S+B`@"@H@("`@()LQ;4]014Y$%
XM159)0T4@1DQ!1U,@"B`@("`@("`@("";,&U41$9?04Q,3U=?3D].7S-?-3H@A
XM06QL;W=S(&]P96YI;F<@-#`@=')A8VL@9')I=F5S+B`@26=N;W)E9"P*("`@T
XM("`@("`@('-I;F-E(&ET(&ES('1H92!D969A=6QT(&)E:&%V:6]U<B!W:&EC!
XM:"!C86YN;W0@8F4@9&ES86)L960N("`*"B`@("`@FS%M24\@4D5154535"!&L
XM3$%'4R`*("`@("`@("`@()LP;51H92!I;U]&;&%G<R!F:65L9"!O9B!A;B!)?
XM3U-T9%)E<2!O<B!)3T5X=%1$("!S=')U8W1U<F4@(&UA>0H@("`@("`@("`@F
XM8V]N=&%I;B!T:&4@9F]L;&]W:6YG(&9L86=S.B`*"B`@("`@("`@("!)3T9?6
XM455)0TLZ(%1H:7,@9FQA9R!I<R!S=7!P;W)T960@9F]R(&]N;'D@82!F97<@W
XM8V]M;6%N9',N("`*"B`@("`@("`@("!)3U1$1E])3D1%6%-93D,Z($9O<B!%$
XM5$0O0TU$7U)!5U)%040@86YD(%]74DE412X@(`H*("`@("`@("`@($E/341&.
XM7S0P5%)!0TM3.B`@5&AI<R`@9FQA9R`@:7,@(&9O<B`@151$+T--1%]214%$U
XM+"`@7U=2251%+`H@("`@("`@("`@7T9/4DU!5"P@86YD(%]3145++B`@268@7
XM<V5T+"!T<F5A="!T:&4@9&ES:R!A<R`@-#`@(&-Y;&EN9&5R"B`@("`@("`@@
XM("!M961I82X@("!)9B`@:70@:&%P<&5N<R!T;R!B92!I;B!A;B`X,"!C>6QI2
XM;F1E<B!D<FEV92P@979E<GD*("`@("`@("`@(&]T:&5R(&-Y;&EN9&5R('=I`
XM;&P@8F4@<VMI<'!E9"!T;R!U<V4@=&AE('-A;64@(&-Y;&EN9&5R<R`@80H@^
XM("`@("`@("`@-#`M8WEL:6YD97(@9')I=F4@('=O=6QD+B`@("!.;W)M86QLS
XM>2`@>6]U('-E="!O<B!C;&5A<B!T:&ES"B`@("`@("`@("!F;&%G(&]N8V4@+
XM870@=&AE('1I;64@>6]U(')E860@=&AE(&)O;W1B;&]C:R!A;F0@97AA;6ENS
XM92!I=',*"@H@("`@(%)H:6%L=&\L($MO<VUO4V]F="`@("`@("`@("`@("`@7
XM("TQ+2`@("`@("`@("`@("`@("`@5F5R<VEO;B`S-"XX+S$N,S`*"@H@("`@M
XM(&UE<W-Y9&ES:RYD979I8V4H*2`@("!!;6EG82!0<F]G<F%M;65R)W,@36%N<
XM=6%L("`@("!M97-S>61I<VLN9&5V:6-E*"D*"@H@("`@("`@("`@;&%Y;W5TI
XM(&EN9F]R;6%T:6]N+B`@270@:7,@9W5A<F%N=&5E9"!T:&%T("!M97-S>61I\
XM<VL@('=O;B=T"B`@("`@("`@("!C:&%N9V4@=&AI<R`@9FQA9RX@("`@24]-"
XM1$9?-#!44D%#2U,@(&ES("!N;W0@('-U<'!O<G1E9"!F;W(*("`@("`@("`@Y
XM($541"]41%]205=214%$(&%N9"!?4D%75U))5$4L('-I;F-E('1H97-E(&%RZ
XM92!C;VYS:61E<F5D("!T;PH@("`@("`@("`@8F4@(")D:6%G;F]S=&EC(B!F(
XM=6YC=&EO;G,@86YD('1H97)E9F]R92!S:&]U;&0@8F4@87,@<F%W(&%S"B`@<
XM("`@("`@("!P;W-S:6)L92X@(`H*("`@("";,6U$25-+($9/4DU!5"`*("`@:
XM("`@("`@()LP;51H92!D:7-K(&9O<FUA="!U<V5D(&ES(&-O;7!A=&EB;&4@:
XM('=I=&@@(&UE<W-Y9&]S("!S>7-T96US+@H@("`@("`@("`@0F]T:"`T,"`@T
XM86YD("`X,"`@8WEL:6YD97(@(&UE9&EA("!A<F4@<W5P<&]R=&5D+B`@5&AE"
XM<F4@87)E"B`@("`@("`@("!N;W)M86QL>2`Y('-E8W1O<G,@<&5R('1R86-K_
XM(&%N9"!T=V\@('1R86-K<R`@<&5R("!C>6QI;F1E<BX*("`@("`@("`@("A/E
XM=71D871E9"D@('-I;F=L92US:61E9"`@9&ES:W,@(&-A;B`@;VYL>2`@8F4@%
XM(')E860@(&EF('1H90H@("`@("`@("`@9')I=FEN9R`@<V]F='=A<F4@(&ESP
XM("!P<F5P87)E9"`@=&\@('1R96%T("!T:&5M("!A<R`@;F]R;6%L"B`@("`@+
XM("`@("!D;W5B;&4M<VED960@9&ES:W,L(&%N9"!S:VEP('1H92!S:61E<R!T2
XM:&%T(&%R92!N;W0@86-T=6%L;'D*("`@("`@("`@('!R97-E;G0N("!/;B!T/
XM:&4@;W1H97(@:&%N9"P@=&AE(&YU;6)E<B!O9B!S96-T;W)S('!E<B!T<F%C)
XM:PH@("`@("`@("`@;6%Y('9A<GD@9G)O;2`X('1O(#$P+B!)9B!T:&4@;G5M<
XM8F5R(&]F('-E8W1O<G,@:7,@9&EF9F5R96YT"B`@("`@("`@("!F<F]M("!T6
XM:&4@(&1E9F%U;'0@=F%L=64@*#DI+"!T:&5N('1R86-K(#`@;VX@<VED92`PK
XM(&UU<W0@8F4*("`@("`@("`@(')E860@9FER<W0@8F5F;W)E('1H:7,@:7,@%
XM<F5C;V=N:7IE9"X@($ET("!I<R`@=&AE;B`@87-S=6UE9`H@("`@("`@("`@<
XM=&AA="`@86QL("!T<F%C:W,@;VX@=&AE(&5N=&ER92!D:7-K(&AA=F4@=&AEG
XM('-A;64@;G5M8F5R(&]F"B`@("`@("`@("!S96-T;W)S+B`@4V\L(&%L;"!PV
XM;W-I=&EO;B!C86QC=6QA=&EO;G,@(&%R92`@8F%S960@(&]N("!T:&4*("`@F
XM("`@("`@(&QA>6]U="!O9B!T:&4@=F5R>2!F:7)S="!T<F%C:RX@(`H*("`@9
XM("`@("`@("TM+2!);F1E>"!P=6QS92`@+2TM+2TM+2TM+2TM+2TM+2TM+2TMY
XM+2TM+2TM+2TM+2TM+2TM+@H@("`@("`@("`@("`@("`@("`@("`@("`@("`@;
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("!\"B`@("`@("`@F
XM("`@("`@-#`@*B`D-&4@("`@1T%0(#%!("`H)#DR-30@96YC;V1E9"D@("`@P
XM("`@("`@("`@('P*("`@("`@("`@("`@("`Q,B`J("0P,"`@("`@("`@("`@W
XM("@D04%!02!E;F-O9&5D*2`@("`@("`@("`@("`@?`H@("`@("`@("`@("`@Q
XM("`S("H@)&,R("`@(%-93D,@("`@*"0U,C(T(&5N8V]D960I("`@("`@("`@G
XM("`@("!\"B`@("`@("`@("`@("`@(#$@*B`D9F,@("`@:6YD97@@;6%R:R`@Q
XM("`@("`@("`@*"0U-34R*2`@("`@("`@('P*("`@("`@("`@("`@("`@("`@,
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@@
XM?`H@("`@("`@("`@("`@(#0P("H@)#1E("`@($=!4"`Q0B`@*"0Y,C4T(&5N/
XM8V]D960I("`@("`@("`@("`@("!\"B`@("`@("`@("`@("`@("`@("`@("`@.
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@('P*("`@F
XM("`@("`@("TM+2!&:7)S="!396-T;W(@+2TM+2TM+2TM+2TM+2TM+2TM+2TMP
XM+2TM+2TM+2TN("`@("`@?`H@("`@("`@("`@("`@("`@("`@("`@("`@("`@<
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@('P@("`@("!\"B`@("`@("`@"
XM("`@("`@,3(@*B`D,#`@("`@1T%0(#(@("`H)$%!04$@96YC;V1E9"D@("`@&
XM("`@?"`@("`@('P*("`@("`@("`@("!\("`@,R`J("1A,2`@("!364Y#("`@.
XM("@D-#0X.2!E;F-O9&5D*2`@("`@("!\("`@("`@?`H@("`@("`@("`@('P@^
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@('P@\
XM("`@("!\"B`@("`@("`@("`@?"`@(#$@*B`D9F4@("`@240M061D<F5S<R!-9
XM87)K("`@+2X@*"0U-34T*2`@?"`@("`@('P*("`@("`@("`@("!\("!T<F%CI
XM:R`@("`@("`@("`@("`@("`@("`@("`@("`@?"`@("`@("`@("!\("`@("`@C
XM?`H@("`@("`@("`@('P@('-I9&4@("`@("`@("`@("`@("`@("`@("`@("`@G
XM("!\("`@("`@("`@('P@("`@("!\"B`@("`@("`@("`@?"`@<V5C=&]R("`@*
XM("`@("`@("`@("`@("`@("`@("`@("`^($E$("`@("`@?"`@("`@('P*("`@M
XM("`@("`@("!\("!L96YG=&@@*#`],3(X+#$],C4V+#(]-3$R(&5T8RXI?"`@G
XM1FEE;&0@("!\("!3("`@?`H@("`@("`@("`@("T^($-20R`Q("`@("`@("`@M
XM("`@("`@("`@("`@("`@("!\("`@("`@("`@('P@(&4@("!\("!4"B`@("`@7
XM("`@("`@+3X@0U)#(#(@("`@("`@("`@("`@("`@("`@("`@("`@+2<@("`@I
XM("`@("`@?"`@8R`@('P@('(*("`@("`@("`@("`@("`@("`@("`@("`@("`@7
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@/B!T("`@(#X@80H@("`@;
XM("`@("`@("`@(#(R("H@)#1E("`@($=!4"`S02`@*"0Y,C4T(&5N8V]D960IR
XM("`@("`@('P@(&\@("!\("!C"B`@("`@("`@("`@("`@,3(@*B`D,#`@("`@E
XM1T%0(#-"("`H)$%!04$@96YC;V1E9"D@("`@("`@?"`@<B`@('P@(&L*("`@7
XM("`@("`@("!\("`@,R`J("1A,2`@("!364Y#("`@("@D-#0X.2!E;F-O9&5D#
XM*2`@("`@("!\("`@("`@?`H@("`@("`@("`@('P@("`@("`@("`@("`@("`@G
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@('P@("`@("!\"B`@("`@("`@"
XM("`@?"`@(#$@*B`D9F(@("`@1$%402U!9')E<W,@36%R:R`@+2X@*"0U-30UQ
XM*2`@?"`@("`@('P*("`@("`@("`@("!\("`U,3(@8GET97,@9&%T82`@("`@`
XM("`@("`@("`@("`@(#X@1$%402`@("!\("`@("`@?`H@("`@("`@("`@("T^E
XM($-20R`Q("`@("`@("`@("`@("`@("`@("`@("`@("!\("!&:65L9"`@('P@E
XM("`@("!\"B`@("`@("`@("`@+3X@0U)#(#(@("`@("`@("`@("`@("`@("`@;
XM("`@("`@+2<@("`@("`@("`@?"`@("`@('P*("`@("`@("`@("`@("`@("`@6
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("!\("`@("`@\
XM?`H@("`@("`@("`@("`@(#<X("H@)#1E("`@($=!4"`T("`@*"0Y,C4T(&5N[
XM8V]D960I("`@("`@('P@("`@("!\"B`@("`@("`@("`@;W(@-#`@*B`D-&4@6
XM("`@1T%0(#0@("`H)#DR-30@96YC;V1E9"D@("`@("`@?"`@("`@('P*("`@I
XM("`@("`@("TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM.
XM+2TM+2TM+2TG("`@("`@?`H@("`@("`@("`@("`@("`@("`@("`@("`@("`@5
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("!\"@H*"@H@("`@.
XM(%)H:6%L=&\L($MO<VUO4V]F="`@("`@("`@("`@("`@("TR+2`@("`@("`@0
XM("`@("`@("`@5F5R<VEO;B`S-"XX+S$N,S`*"@H@("`@(&UE<W-Y9&ES:RYD0
XM979I8V4H*2`@("!!;6EG82!0<F]G<F%M;65R)W,@36%N=6%L("`@("!M97-SH
XM>61I<VLN9&5V:6-E*"D*"@H@("`@("`@("`@("XN+BXN+B`X(&]R(#D@;6]R%
XM92!S96-T;W)S("XN+BXN+BX@("`@("`@("`@("`@("`@("!\"B`@("`@("`@P
XM("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@@
XM("`@("`@("`@('P*("`@("`@("`@("`@("!R97-T("`D-&4@=7`@=&\@-C(U&
XM,"!B>71E<R`@("`@("`H)#DR-30I("`@("`@("`@?`H@("`@("`@("`@+2TM-
XM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TMI
XM+2TM+2TG"@H@("`@()LQ;41%5DE!5$E/3E,@"B`@("`@("`@("";,&U4:&4@.
XM5$1?4D5-0TA!3D=%24Y4(&-O;6UA;F0@=V]R:W,N("`H270@9&]E<VXG="!W_
XM:71H('1H92`Q+C(*("`@("`@("`@(&]R(#$N,R!T<F%C:V1I<VLN9&5V:6-ER
XM*2X@(`H*("`@("`@("`@(%1H92`@(&UE<W-Y9&ES:R`@(%5N:70@("!S=')UV
XM8W1U<F4@("!D;V5S("`@;F]T("`@;&]O:R`@;&EK90H@("`@("`@("`@5$150
XM7U!U8FQI8U5N:70@870@86QL+B`@"@H@("`@("`@("`@26X@<V]M92!S:71U>
XM871I;VYS+"!E<G)O<B!R971U<FYS(&UA>2!B92!D:69F97)E;G0@(&]R("!LI
XM97-S"B`@("`@("`@("!S<&5C:69I8RX@(`H*("`@("`@("`@($1A=&$@(&)UN
XM9F9E<G,@('1O(&)E(')E860@;W(@=W)I='1E;B!C=7)R96YT;'D@9&]N)W0@N
XM;F5E9"!T;PH@("`@("`@("`@8F4@:6X@0VAI<"!M96UO<GDN("!4:&ES(&UI`
XM9VAT(&-H86YG92!I;B!T:&4@(&9U='5R92X@("`@1F]R"B`@("`@("`@("!F!
XM=71U<F4@(&-O;7!A=&EB:6QI='D@(&%N9"`@=VET:"`@=&AE("!T<F%C:V1I!
XM<VLN9&5V:6-E("!Y;W4*("`@("`@("`@('-H;W5L9"!N;W0@9&5P96YD(&]NP
XM('1H:7,N("`*"B`@("`@("`@("!);B!O<F1E<B!T;R!B92!A8FQE('1O(&9OC
XM<FUA="!A("!T<F%C:R`@=VET:"`@82`@;F]N<W1A;F1A<F0*("`@("`@("`@M
XM(&YU;6)E<B!O9B!S96-T;W)S("AF<F]M(#@@=&\@,3`I+"!Y;W4@;75S="!UI
XM<V4@=&AE(%1$7T9/4DU!5`H@("`@("`@("`@8V]M;6%N9"`@=VET:"`@;&5S"
XM<R!T:&%N(#0P('-E8W1O<G,@*#(P-#@P(&)Y=&5S*2!A="!A('1I;64N"B`@8
XM("`@("`@("`H268@>6]U('=H:7-H('1O(&9O<FUA="!T<F%C:W,@=VET:"!L3
XM97-S('1H86X@."!S96-T;W)S+"!Y;W4*("`@("`@("`@(&-A;B!O;FQY(&1O+
XM('1H:7,@;VYE('1R86-K(&%T(&$@=&EM92XI(%-I;F-E('EO=2!M=7-T(&%LP
XM=V%Y<PH@("`@("`@("`@9F]R;6%T(&5N=&ER92!T<F%C:W,@870@82!T:6UEK
XM+"!T:&ES('5N:7%U96QY(&1E=&5R;6EN97,@=&AE"B`@("`@("`@("!N=6UB-
XM97(@;V8@<V5C=&]R<R!Y;W4@=V%N="`@;VX@(&5A8V@@('1R86-K+B`@("!4+
XM:&4@(&-U<G)E;G0*("`@("`@("`@(&YU;6)E<B`@;V8@('-E8W1O<G,@<&5RB
XM('1R86-K+"!W:&EC:"!W:6QL(&)E('5S960@;&%T97(@=VAE;@H@("`@("`@8
XM("`@<F5A9&EN9R!F<F]M('1H92!S86UE(&1I<VLL(&ES('5P9&%T960@86-CV
XM;W)D:6YG;'DN("!)9B`@>6]U"B`@("`@("`@("!S<&5C:69Y("`T,"`@<V5C^
XM=&]R<R`@;W(@(&UO<F4L("!M97-S>61I<VLN9&5V:6-E("!D;V5S("!N;W0*(
XM("`@("`@("`@(&%T=&5M<'0@=&\@9W5E<W,@=VAA="!Y;W4@=V%N="!B=70@_
XM('-I;7!L>2`@=7-E<R`@=&AE("!V86QU90H@("`@("`@("`@=&AA="!W87,@B
XM(&%L<F5A9'D@<V5T+B`@4V\L('EO=2!M87D@8VAO;W-E('1O(&9I<G-T(&9O/
XM<FUA="!A"B`@("`@("`@("!S:6YG;&4@=')A8VL@=&\@:6YD:6-A=&4@=&AE[
XM(&1E<VER960@;G5M8F5R(&]F("!S96-T;W)S("!P97(*("`@("`@("`@('1R8
XM86-K+"`@86YD("!T:&5N("!I;B!O;F4@8V%L;"!F;W)M870@=&AE(&5N=&ER^
XM92!R97-T(&]F('1H90H@("`@("`@("`@9&ES:RX@(`H*("`@("`@("`@(%=H/
XM96X@82!D:7-K('=I=&@@;6]R92!O<B!L97-S('1H86X@.2!S96-T;W)S("!P=
XM97(@('1R86-K("!I<PH@("`@("`@("`@<F5A9"P@('EO=2!M=7-T(&9I<G-TH
XM(&ES<W5E(&$@0TU$7U)%040@8V]M;6%N9"!F;W(@=&AE(&9I<G-T"B`@("`@R
XM("`@("!S96-T;W(H<RD@;V8@('1H92`@(&1I<VLN("`@("`@5VAE;B`@('1R[
XM86-K("`@,"`@(&ES("`@<F5A9"P*("`@("`@("`@(&UE<W-Y9&ES:RYD979I?
XM8V4@(&1E=&5R;6EN97,@:&]W(&UA;GD@<V5C=&]R<R!T:&5R92!A<F4@;VX@+
XM80H@("`@("`@("`@=')A8VLN("!4:&ES(&YU;6)E<B!I<R!T:&5N('5S960@K
XM869T97)W87)D<R!W:&5N(&-A;&-U;&%T:6YG"B`@("`@("`@("!T:&4@=')A>
XM8VMS(&%N9"!S:61E<R!O9B!O=&AE<B!S96-T;W)S+B`@(%-O+"`@=&AI<R`@:
XM:7,@('9E<GD*("`@("`@("`@(&EM<&]R=&%N="X@($)U="!F;W(@=&AO<V4@M
XM*'9I<G1U86QL>2!I;7!O<W-I8FQE*2!C87-E<R!W:&5R90H@("`@("`@("`@(
XM;F]T("!A;&P@=')A8VMS(&AA=F4@=&AE('-A;64@;G5M8F5R(&]F('-E8W1OY
XM<G,L(&%L;"!S96-T;W)S"B`@("`@("`@("`H=7`@=&\@=&AE(&UA>&EM=6T@&
XM<W5P<&]R=&5D*2!T:&%T('=E<F4@<F5A9"`@;VX@(&%N>2`@=')A8VL*("`@3
XM("`@("`@('=I;&P@8F4@=W)I='1E;B!B86-K+B`@1F]R='5N871E;'DL(&%L!
XM;"!C=7)R96YT;'D@:VYO=VX@9FEL90H@("`@("`@("`@<WES=&5M<R`@:&%VB
XM92`@82`@8F]O=&)L;V-K("!A="!T<F%C:R`P+"!S:61E(#`L('-O('1H97D@(
XM86QL"B`@("`@("`@("!A9&%P="!T<F%N<W!A<F5N=&QY+B`@"@H@("`@()LQR
XM;4-!5D5!5%,@"B`@("`@("`@("";,&U4:&4@*&]U=&1A=&5D*2!41%]214U/,
XM5D4@8V]M;6%N9"!I<R!R;W5T960@=&\@=')A8VMD:7-K+B`@07,*("`@("`@8
XM("`@(&$@<F5S=6QT(&]F('1H:7,L(&]N;'D@;VYE(&1I<VL@<F5M;W9E(&EN-
XM=&5R<G5P="`H<&5R("!U;FET*0H@("`@("`@("`@;6%Y("!B92`@:6YS=&%LQ
XM;&5D("`H=VET:"`@=&AI<R`@8V]M;6%N9"DL("!W:&5T:&5R("!T:')O=6=H]
XM"B`@("`@("`@("!T<F%C:V1I<VL@;W(@;65S<WED:7-K+B`@("`H5&AI<R`@U
XM<V5E;7,@('1O("!H879E("!B965N("!T:&4*("`@("`@("`@(&]R:6=I;F%L>
XM("`@:6YT96YT:6]N("`@;V8@(%1$7U)%34]612XI("!/9B`@8V]U<G-E+"`@[
XM8W5R<F5N=`H@("`@("`@("`@<')O9W)A;7,@<VAO=6QD('5S92!41%]!1$1#W
XM2$%.1T5)3E0N("`*"@H*"B`@("`@4FAI86QT;RP@2V]S;6]3;V9T("`@("`@2
XM("`@("`@("`@+3,M("`@("`@("`@("`@("`@("!697)S:6]N(#,T+C@O,2XSA
XM,`H*"B`@("`@;65S<WED:7-K+F1E=FEC92@I("`@($%M:6=A(%!R;V=R86UM]
XM97(G<R!-86YU86P@("`@(&UE<W-Y9&ES:RYD979I8V4H*0H*"B`@("`@FS%M1
XM551)3$E42453(`H@("`@("`@("`@FS!M02!S97!A<F%T92!P<F]G<F%M(&-A0
XM;&QE9"`@26=N;W)E("!I<R`@<W5P<&QI960L("!W:&EC:"`@;6%Y"B`@("`@W
XM("`@("!S=7!P<F5S<R!T:&4@0U)#(&-H96-K(&EN(&%N>2!C=7)R96YT;'D@C
XM;W!E;B!U;FET+B`@5VAE;B!T:&4*("`@("`@("`@('5N:70@:7,@9&5F:6YI(
XM=&EV96QY(&-L;W-E9"!A;F0@;&%T97(@<F5O<&5N960L(&EG;F]R:6YG($-22
XM0PH@("`@("`@("`@;6ES;6%T8VAE<R!A9V%I;B!H87,@:71S(&1E9F%U;'0@,
XM=F%L=64@;V8@3F\N("`*"B`@("`@("`@("!5<V%G93H@:6=N;W)E(#QU;FET@
XM;G(^(#Q915,O3D\^"@H@("`@("`@("`@268@('EO=2!O;FQY(&=I=F4@=&AEC
XM('5N:70@;G5M8F5R+"!I9VYO<F4@=VEL;"!O=71P=70@96ET:&5R"B`@("`@-
XM("`@("!997,@;W(@3F\L(')E9FQE8W1I;F<@=VAE=&AE<B!#4D,@;6ES;6%T8
XM8VAE<R`@8W5R<F5N=&QY("!A<F4*("`@("`@("`@(&EG;F]R960N("`*"B`@W
XM("`@("`@("!4:&ES("!P<F]G<F%M("!C86X@(&)E("!U<V5F=6P@=VAE;B!Y(
XM;W4@=VES:"!T;R!R96-O=F5R(&1A=&$*("`@("`@("`@(&9R;VT@82!D86UA$
XM9V5D(&]R(&UA<F=I;F%L(&1I<VLN("!3;VUE=&EM97,@=&5X="`@9FEL97,@C
XM(&UA>0H@("`@("`@("`@8F4@=7-A8FQE(&5V96X@:68@=&AE<F4@87)E(&$@"
XM9F5W(&5R<F]R<R!I;B!T:&5M+B`@"@H@("`@()LQ;4%55$A/4E,@"B`@("`@/
XM("`@("";,&U-97-S>61I<VL@(&ES('=R:71T96X@8GD@FS-M4V]U<F-E<F5RM
XM()LP;4]L868@FS-M4FAI86QT;R";,&U396EB97)T+B!4:&4*("`@("`@("`@'
XM(&QO=RUL979E;"`@=W)I=&EN9R`@<&%R="`@=V%S("!O<FEG:6YA;&QY("!D;
XM;VYE("!B>2`@(%=E<FYE<@H@("`@("`@("`@1_QN=&AE<BX@57-E9"!W:71H9
XM('!E<FUI<W-I;VXN("`*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*-
XM"@H*"@H*"@H*"B`@("`@4FAI86QT;RP@2V]S;6]3;V9T("`@("`@("`@("`@(
XJ("`@+30M("`@("`@("`@("`@("`@("!697)S:6]N(#,T+C@O,2XS,`H*F
X``
Xend
Xsize 10842
END_OF_FILE
if test 15215 -ne `wc -c <'doc/dev.man.uu'`; then
    echo shar: \"'doc/dev.man.uu'\" unpacked with wrong size!
fi
# end of 'doc/dev.man.uu'
fi
if test -f 'src/hanlock.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/hanlock.c'\"
else
echo shar: Extracting \"'src/hanlock.c'\" \(18293 characters\)
sed "s/^X//" >'src/hanlock.c' <<'END_OF_FILE'
X/*-
X * $Id: hanlock.c,v 1.30 90/06/04 23:17:18 Rhialto Rel $
X * $Log:	hanlock.c,v $
X * Revision 1.30  90/06/04  23:17:18  Rhialto
X * Release 1 Patch 3
X * 
X * HANLOCK.C
X *
X * The code for the messydos file system handler
X *
X * The Lock department. Takes care of operations on locks, and consequently,
X * on directories.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dos.h"
X#include "han.h"
X
X#ifdef HDEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xstruct LockList *LockList;	/* List of all locked files we have. Note
X				 * this is not the same as all locks we
X				 * have */
Xstruct MSFileLock *RootLock;	/* Lock on root directory */
Xstruct MSFileLock *EmptyFileLock;	/* 2nd result of MSLock() */
X
Xstruct DirEntry FakeRootDirEntry = {
X    {				/* de_Msd */
X	"Unnamed ",             /* msd_Name */
X	"   ",                  /* msd_Ext */
X	ATTR_VOLUMELABEL,	/* msd_Attributes */
X	{0},			/* msd_Pad1 */
X	0,			/* msd_Time */
X	DATE_MIN,		/* msd_Date, 1/1/80 */
X	0,			/* msd_Cluster */
X	0			/* msd_Filesize */
X    },
X    ROOT_SEC,			/* de_Sector */
X    0				/* de_Offset */
X};
Xbyte		DotDot[1 + 8 + 3] = "..          ";
X
X/*
X * This routine compares a name in a directory entry with a given name
X */
X
Xint
XCompareNames(dir, name)
Xregister struct MsDirEntry *dir;
Xregister byte  *name;
X{
X    if (dir->msd_Name[0] & DIR_DELETED_MASK)
X	return CMP_FREE_SLOT;
X
X    if (dir->msd_Name[0] == 0)
X	return CMP_END_OF_DIR;	/* end of directory */
X
X    if (dir->msd_Attributes & ATTR_VOLUMELABEL)
X	return CMP_NOT_EQUAL;
X
X    if (strncmp(dir->msd_Name, name, 8 + 3))
X	return CMP_NOT_EQUAL;
X
X    if (dir->msd_Attributes & ATTR_DIRECTORY)
X	return CMP_OK_DIR;
X
X    return CMP_OK_FILE;
X}
X
Xvoid
XNextDirEntry(sector, offset)
Xregister word  *sector;
Xregister word  *offset;
X{
X    if ((*offset += MS_DIRENTSIZE) >= Disk.bps) {
X	*offset = 0;
X	if (*sector >= Disk.datablock) {
X	    /* Must be subdirectory */
X	    *sector = NextClusteredSector(*sector);
X	    debug(("NextClusteredSector: %d\n", *sector));
X	} else {
X	    if (++*sector >= Disk.datablock) {
X		*sector = SEC_EOF;
X	    }
X	}
X    }
X    /* else no more work needed */
X}
X
X/*
X * Get the directory entry following the given one. If requested, we make
X * the directory longer.
X */
X
Xstruct DirEntry *
XFindNext(previous, createit)
Xregister struct DirEntry *previous;
Xint		createit;
X{
X    byte	   *sector;
X    word	    prevsec = previous->de_Sector;
X
X    NextDirEntry(&previous->de_Sector, &previous->de_Offset);
X
X    if (previous->de_Sector == SEC_EOF) {
X	error = ERROR_OBJECT_NOT_FOUND;
X#ifndef READONLY
X	if (createit) {
X	    if (prevsec < Disk.datablock - 1) { /* Should not be necessary */
X		previous->de_Sector = prevsec + 1;
X	    } else if (prevsec >= Disk.datablock) {
X		previous->de_Sector = FindFreeSector(prevsec);
X	    }
X	    if (previous->de_Sector != SEC_EOF) {
X		sector = EmptySec(previous->de_Sector);
X		setmem(sector, (int) Disk.bps, 0);
X		MarkSecDirty(sector);
X		FreeSec(sector);
X		setmem(&previous->de_Msd, sizeof (previous->de_Msd), 0);
X
X		return previous;
X	    }
X	}
X#endif
X    } else if (sector = GetSec(previous->de_Sector)) {
X	CopyMem(sector + previous->de_Offset, &previous->de_Msd,
X		(long) MS_DIRENTSIZE);
X	OtherEndianMsd(&previous->de_Msd);
X	FreeSec(sector);
X
X	return previous;
X    }
X    return NULL;
X}
X
X#ifdef HDEBUG
X
Xvoid
XPrintDirEntry(de)
Xstruct DirEntry *de;
X{
X    debug(("%d,%d ", de->de_Sector, de->de_Offset));
X    debug(("%.8s.%.3s attr:%x time:%x date:%x start:%x size:%lx\n",
X	   de->de_Msd.msd_Name,
X	   de->de_Msd.msd_Ext,
X	   de->de_Msd.msd_Attributes,
X	   de->de_Msd.msd_Time,
X	   de->de_Msd.msd_Date,
X	   de->de_Msd.msd_Cluster,
X	   de->de_Msd.msd_Filesize
X	   ));
X}
X
X#endif
X
X/*
X * MakeLock makes a struct MSFileLock from a directory entry and the
X * parent directory MSFileLock pointer. It looks if it already has a Lock
X * on it. In that case, it simply increments its reference count, when
X * possible.
X */
X
Xstruct MSFileLock *
XMakeLock(parentdir, dir, mode)
Xstruct MSFileLock *parentdir;
Xstruct DirEntry *dir;
Xulong		mode;
X{
X    register struct MSFileLock *fl;
X    struct MSFileLock *nextfl;
X
X    if (mode != EXCLUSIVE_LOCK || (dir->de_Msd.msd_Attributes & ATTR_DIR))
X	mode = SHARED_LOCK;
X
X#ifdef HDEBUG
X    debug(("MakeLock: "));
X    PrintDirEntry(dir);
X#endif
X
X    /*
X     * Look through our list to see if we already have it. The criteria
X     * for this are: 1. the directory entries are the same or 2. they have
X     * the same first cluster and are both directories (which can have
X     * multiple directory entries). Sigh.
X     */
X
X    for (fl = (struct MSFileLock *) LockList->ll_List.mlh_Head;
X	 nextfl = (struct MSFileLock *) fl->msfl_Node.mln_Succ;
X	 fl = nextfl) {
X#ifdef HDEBUG
X	debug(("> "));
X	PrintDirEntry(&fl->msfl_Msd);
X#endif
X	if ((fl->msfl_DirSector == dir->de_Sector &&
X	     fl->msfl_DirOffset == dir->de_Offset) ||
X	    (fl->msfl_Msd.msd_Cluster == dir->de_Msd.msd_Cluster &&
X	     (dir->de_Msd.msd_Attributes & ATTR_DIR) &&
X	     (fl->msfl_Msd.msd_Attributes & ATTR_DIR))
X	    ) {
X	    /* Found existing lock on file */
X	    if (fl->msfl_Refcount < 0 || mode == EXCLUSIVE_LOCK) {
X		error = ERROR_OBJECT_IN_USE;
X		return NULL;
X	    }
X	    fl->msfl_Refcount++;
X	    return fl;
X	}
X    }
X
X    fl = AllocMem((long) sizeof (*fl), MEMF_PUBLIC);
X    if (fl == NULL) {
X	error = ERROR_NO_FREE_STORE;
X	return NULL;
X    }
X    fl->msfl_Parent = parentdir ? MSDupLock(parentdir) : NULL;
X
X    fl->msfl_Refcount = (mode == EXCLUSIVE_LOCK) ? -1 : 1;
X    fl->msfl_DirSector = dir->de_Sector;
X    fl->msfl_DirOffset = dir->de_Offset;
X    fl->msfl_Msd = dir->de_Msd;
X
X    AddHead(&LockList->ll_List, fl);
X
X    return fl;
X}
X
X/*
X * This routine Locks a file. It first searches it in the directory, then
X * lets the rest of the work be done by MakeLock(). If it encounters an
X * empty slot in the directory, it remembers where, in case we need it. If
X * you clear the MODE_CREATEFILE bit in the mode parameter, we fabricate a
X * new MSFileLock from the empty directory entry. It then becomes the
X * caller's responsibility to MSUnLock() it eventually.
X */
X
Xstruct MSFileLock *
XMSLock(parentdir, name, mode)
Xstruct MSFileLock *parentdir;
Xbyte	       *name;
Xulong		mode;
X{
X    byte	   *sector;
X    struct MSFileLock *newlock;
X    register struct DirEntry *de;
X    struct DirEntry sde;
X    byte	   *nextpart;
X    byte	    component[8 + 3];	/* Note: not null-terminated */
X    int 	    createit;
X    word	    freesec;
X    word	    freeoffset;
X
X    de = &sde;
X    newlock = NULL;
X
X    /*
X     * See if we have an absolute path name (starting at the root).
X     */
X    {
X	register byte  *colon;
X
X	if (colon = index(name, ':')) {
X	    name = colon + 1;
X	    parentdir = RootLock;
X	    /*
X	     * MSH::Command or ::Command?
X	     */
X	    if (name[0] == ':') {
X		HandleCommand(name);
X		error = ERROR_OBJECT_NOT_FOUND;
X
X		return NULL;
X	    }
X	}
X    }
X
X
X    /*
X     * Get a copy of the parent dir lock, so we can walk it over the
X     * directory tree.
X     */
X    parentdir = MSDupLock(parentdir);
X
X    /*
X     * Start with the directory entry of the parent dir.
X     */
X
X    sde.de_Msd = parentdir->msfl_Msd;
X    sde.de_Sector = parentdir->msfl_DirSector;
X    sde.de_Offset = parentdir->msfl_DirOffset;
X#ifdef HDEBUG
X    debug(("pdir %08lx: ", parentdir));
X    PrintDirEntry(&parentdir->msfl_Msd);
X#endif
X
Xnewdir:
X    freesec = SEC_EOF;		/* Means none found yet */
X
X    nextpart = ToMSName(component, name);
X    debug(("Component: '%11s'\n", component));
X    if (nextpart[0] != '/') {
X	nextpart = NULL;
X#ifndef READONLY
X	/*
X	 * See if we are requested to get an empty spot in the directory
X	 * if the given name does not exist already. The value of mode is
X	 * not important until we actually create the filelock.
X	 */
X	if (!(mode & MODE_CREATEFILE)) {
X	    mode ^= MODE_CREATEFILE;
X	    createit = 1;
X	} else
X	    createit = 0;
X#endif
X    } else
X	nextpart++;
X
X    /*
X     * Are we at the end of the name? Then we are finished now. This works
X     * because sde contains the directory entry of parentdir.
X     */
X
X    if (name[0] == '\0')
X	goto exit;
X
X    /*
X     * If there is more name, we enter the directory, and here we get the
X     * first entry.
X     */
X
X    sde.de_Sector = DirClusterToSector(sde.de_Msd.msd_Cluster);
X    sde.de_Offset = 0;
X
X    if ((sector = GetSec(sde.de_Sector)) == NULL)
X	goto error;
X
X    CopyMem(sector, &sde.de_Msd, (long) sizeof (struct MsDirEntry));
X    OtherEndianMsd(&sde.de_Msd);
X    FreeSec(sector);
X
X    while (de) {
X	switch (CompareNames(&sde.de_Msd, component)) {
X	case CMP_FREE_SLOT:
X	    if (freesec == SEC_EOF) {
X		freesec = sde.de_Sector;
X		freeoffset = sde.de_Offset;
X	    }
X	    /* Fall through */
X	case CMP_NOT_EQUAL:
X	    de = FindNext(&sde, createit);      /* Try next directory
X						 * entry */
X	    continue;
X	case CMP_OK_DIR:
X	    if (name = nextpart) {
X		/*
X		 * We want to keep locks on all directories between each
X		 * bottom directory and file lock, so we can easily find
X		 * the parent of a lock. There just seems to be a problem
X		 * here when we enter the 'subdirectories' . or .. , but
X		 * that in not so: MakeLock will detect that it has
X		 * already a lock on those, and NOT make :one/two the
X		 * parent of :one/two/.. .
X		 */
X		newlock = MakeLock(parentdir, de, SHARED_LOCK);
X		MSUnLock(parentdir);
X		parentdir = newlock;
X		goto newdir;
X	    }
X	    goto exit;
X	case CMP_OK_FILE:
X	    if (nextpart) {
X		error = ERROR_OBJECT_WRONG_TYPE;
X		de = NULL;
X	    }
X	    goto exit;
X	case CMP_END_OF_DIR:	/* means we will never find it */
X	    error = ERROR_OBJECT_NOT_FOUND;
X	    if (freesec == SEC_EOF) {
X		freesec = sde.de_Sector;
X		freeoffset = sde.de_Offset;
X	    }
X	    de = NULL;
X	    goto exit;
X	}
X    }
X
Xexit:
X    if (de) {
X	newlock = MakeLock(parentdir, &sde, mode);
X    } else {
X	newlock = NULL;
X#ifndef READONLY
X	if (createit &&         /* Do we want to make it? */
X	    error == ERROR_OBJECT_NOT_FOUND &&	/* does it not exist yet? */
X	    nextpart == NULL) { /* do we have the last part of the name */
X	    if (freesec != SEC_EOF) {   /* is there any room? */
X		if (IDDiskState == ID_VALIDATED) {
X		    error = 0;
X		    setmem(&sde.de_Msd, sizeof (sde.de_Msd), 0);
X		    sde.de_Sector = freesec;
X		    sde.de_Offset = freeoffset;
X		    /* ToMSName(sde.de_Msd.msd_Name, name); */
X		    strncpy(sde.de_Msd.msd_Name, component, 8 + 3);
X		    EmptyFileLock = MakeLock(parentdir, &sde, mode);
X		    WriteFileLock(EmptyFileLock);
X		} else
X		    error = ERROR_DISK_WRITE_PROTECTED;
X	    } else
X		error = ERROR_DISK_FULL;
X	}
X#endif
X    }
X
Xerror:
X    MSUnLock(parentdir);
X
X    return newlock;
X}
X
X/*
X * This routine DupLocks a file. This simply means incrementing the
X * reference count, if it was not an exclusive Lock.
X */
X
Xstruct MSFileLock *
XMSDupLock(fl)
Xstruct MSFileLock *fl;
X{
X    if (fl == NULL)
X	fl = RootLock;
X    if (fl->msfl_Refcount <= 0) {
X	error = ERROR_OBJECT_IN_USE;
X	return NULL;
X    } else {
X	fl->msfl_Refcount++;
X    }
X
X    return fl;
X}
X
X/*
X * This routine DupLocks the parent of a lock, if there is one.
X */
X
Xstruct MSFileLock *
XMSParentDir(fl)
Xregister struct MSFileLock *fl;
X{
X    if (fl == NULL || fl == RootLock) {
X	error = ERROR_OBJECT_NOT_FOUND;
X    } else if (fl->msfl_Parent)
X	return MSDupLock(fl->msfl_Parent);
X
X    return NULL;
X}
X
X/*
X * This routine UnLocks a file.
X */
X
Xint
XMSUnLock(fl)
Xstruct MSFileLock *fl;
X{
X#ifdef HDEBUG
X    debug(("MSUnLock %08lx: ", fl));
X    PrintDirEntry(&fl->msfl_Msd);
X#endif
X
X    if (fl) {
X	if (--fl->msfl_Refcount <= 0) {
X	    struct LockList *list;
X
X	    list = (struct LockList *) fl->msfl_Node.mln_Pred;
X	    Remove(fl);
X	    debug(("Remove()d %08lx\n", fl));
X
X	    /*
X	     * We may need to get rid of the LockList if it is empty. This
X	     * is the current LockList iff we are called from
X	     * MSDiskRemoved(). Please note that we are not even sure that
X	     * 'list' really is the list header, therefore the careful
X	     * test if fl refers to a volume label (root lock) which is
X	     * finally UnLock()ed. Because of the recursion, we only try to
X	     * free the LockList iff there is no parent anymore, since
X	     * otherwise list may be invalid by the time we use it.
X	     */
X	    if (fl->msfl_Parent) {
X		MSUnLock(fl->msfl_Parent);
X	    } else {
X		if ((fl->msfl_Msd.msd_Attributes & ATTR_VOLUMELABEL) &&
X		    ((void *) list->ll_List.mlh_Head ==
X		     (void *) &list->ll_List.mlh_Tail)
X		    ) {
X		    FreeLockList(list);
X		}
X	    }
X	    FreeMem(fl, (long) sizeof (*fl));
X	}
X    }
X    return DOSTRUE;
X}
X
X/*
X * This is (among other things) the inverse of ToMSName().
X */
X
Xvoid
XExamineDirEntry(msd, fib)
Xstruct MsDirEntry *msd;
Xregister struct FileInfoBlock *fib;
X{
X#ifdef HDEBUG
X    debug(("+ "));
X    PrintDirEntry(msd);
X#endif
X    /*
X     * Special treatment when we examine the root directory
X     */
X    if (fib->fib_DiskKey == (long)ROOT_SEC << 16) {
X	strncpy(&fib->fib_FileName[1], msd->msd_Name, 8 + 3);
X	(void) ZapSpaces(&fib->fib_FileName[2], &fib->fib_FileName[1 + 8 + 3]);
X    } else {
X	register byte  *end,
X		       *dot;
X
X	strncpy(&fib->fib_FileName[1], msd->msd_Name, 8);
X	/* Keep at least one character, even a space, before the dot */
X	dot = ZapSpaces(&fib->fib_FileName[2], &fib->fib_FileName[1 + 8]);
X	dot[0] = ' ';
X	strncpy(dot + 1, msd->msd_Ext, 3);
X	dot[4] = '\0';
X	end = ZapSpaces(dot, dot + 4);
X	if (end > dot)
X	    dot[0] = '.';
X    }
X    fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
X
X    fib->fib_EntryType =
X    fib->fib_DirEntryType =
X	(msd->msd_Attributes & ATTR_DIR) ? FILE_DIR : FILE_FILE;
X    fib->fib_Protection = 0;
X    if (!(msd->msd_Attributes & ATTR_ARCHIVED))
X	fib->fib_Protection |= FIBF_ARCHIVE;
X    if (msd->msd_Attributes & ATTR_READONLY)
X	fib->fib_Protection |= (FIBF_WRITE | FIBF_DELETE);
X    if (msd->msd_Attributes & (ATTR_HIDDEN|ATTR_SYSTEM))
X	fib->fib_Protection |= FIBF_HIDDEN;
X    fib->fib_Size = msd->msd_Filesize;
X    fib->fib_NumBlocks = (msd->msd_Filesize + Disk.bps - 1) / Disk.bps;
X    ToDateStamp(&fib->fib_Date, msd->msd_Date, msd->msd_Time);
X    fib->fib_Comment[0] = 0;
X}
X
X/*
X * We remember what we should do when we call ExNext with a lock on
X * a directory (enter or step over it) by a flag in fib_EntryType.
X * Unfortunately, the Commodore (1.3) List and Dir commands expect
X * that fib_EntryType contains the information that the documentation
X * (libraries/dos.h) specifies to be in fib_DirEntryType. Therefore
X * we use the low bit in fib_DiskKey instead. Yech.
X */
X
Xint
XMSExamine(fl, fib)
Xstruct MSFileLock *fl;
Xregister struct FileInfoBlock *fib;
X{
X    if (fl == NULL)
X	fl = RootLock;
X
X    fib->fib_DiskKey = ((ulong) fl->msfl_DirSector << 16) |
X		       fl->msfl_DirOffset +
X		       1;	/* No ExNext called yet */
X    ExamineDirEntry(&fl->msfl_Msd, fib);
X
X    return DOSTRUE;
X}
X
Xint
XMSExNext(fl, fib)
Xstruct MSFileLock *fl;
Xregister struct FileInfoBlock *fib;
X{
X    word	    sector = fib->fib_DiskKey >> 16;
X    word	    offset = (word) fib->fib_DiskKey;
X    byte	   *buf;
X
X    if (fl == NULL)
X	fl = RootLock;
X
X    if (offset & 1) {
X	if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
X	    /* Enter subdirectory */
X	    sector = DirClusterToSector(fl->msfl_Msd.msd_Cluster);
X	    offset = 0;
X	} else {
X	    offset--;		/* Remember, it was odd */
X	    NextDirEntry(&sector, &offset);
X	}
X    } else {
Xskip:
X	NextDirEntry(&sector, &offset);
X    }
X
X    if (sector != SEC_EOF) {
X	struct MsDirEntry msd;
X
X	if (buf = GetSec(sector)) {
X	    msd = *(struct MsDirEntry *) (buf + offset);
X	    FreeSec(buf);
X	    if (msd.msd_Name[0] == '\0') {
X		goto end;
X	    }
X	    if (msd.msd_Name[0] & DIR_DELETED_MASK ||
X		msd.msd_Name[0] == '.' ||       /* Hide "." and ".." */
X		(msd.msd_Attributes & ATTR_VOLUMELABEL)) {
X		goto skip;
X	    }
X	    OtherEndianMsd(&msd);       /* Get correct endianness */
X	    fib->fib_DiskKey = ((ulong) sector << 16) | offset;
X	    ExamineDirEntry(&msd, fib);
X
X	    return DOSTRUE;
X	}
X    }
Xend:
X    error = ERROR_NO_MORE_ENTRIES;
X    return DOSFALSE;
X}
X
X/*
X * Convert AmigaDOS protection bits to messy attribute bits.
X */
X
Xlong
XMSSetProtect(parentdir, name, mask)
Xregister struct MSFileLock *parentdir;
Xchar	   *name;
Xlong	   mask;
X{
X    register struct MSFileLock *lock;
X
X    if (parentdir == NULL)
X	parentdir = RootLock;
X
X    lock = MSLock(parentdir, name, EXCLUSIVE_LOCK);
X    if (lock) {
X	/* Leave SYSTEM bit as-is */
X	lock->msfl_Msd.msd_Attributes &= ATTR_SYSTEM;
X	/* write or delete protected -> READONLY */
X	if (mask & (FIBF_WRITE|FIBF_DELETE))
X	    lock->msfl_Msd.msd_Attributes |= (ATTR_READONLY);
X	/* hidden -> hidden */
X	if (mask & FIBF_HIDDEN)
X	    lock->msfl_Msd.msd_Attributes |= (ATTR_HIDDEN);
X	/* archived=0 (default) -> archived=1 (default) */
X	if (!(mask & FIBF_ARCHIVE))
X	    lock->msfl_Msd.msd_Attributes |= (ATTR_ARCHIVED);
X	WriteFileLock(lock);
X	MSUnLock(lock);
X	return TRUE;
X    }
X
X    return FALSE;
X}
X
Xint
XCheckLock(lock)
Xregister struct MSFileLock *lock;
X{
X    register struct MSFileLock *parent;
X
X    if (lock) {
X	while (parent = lock->msfl_Parent)
X	    lock = parent;
X	if (lock != RootLock)
X	    error = ERROR_DEVICE_NOT_MOUNTED;
X    }
X    return error;
X}
X
X#ifndef READONLY
X
Xvoid
XWriteFileLock(fl)
Xregister struct MSFileLock *fl;
X{
X    debug(("WriteFileLock %08lx\n", fl));
X
X    if (fl) {
X	register byte  *block = GetSec(fl->msfl_DirSector);
X
X	if (block) {
X	    CopyMem(&fl->msfl_Msd, block + fl->msfl_DirOffset,
X		    (long) sizeof (fl->msfl_Msd));
X	    OtherEndianMsd(block + fl->msfl_DirOffset);
X	    MarkSecDirty(block);
X	    FreeSec(block);
X	}
X    }
X}
X
Xvoid
XUpdateFileLock(fl)
Xregister struct MSFileLock *fl;
X{
X    struct DateStamp dateStamp;
X
X    debug(("UpdateFileLock %08lx\n", fl));
X
X    DateStamp(&dateStamp);
X    ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, &dateStamp);
X    WriteFileLock(fl);
X}
X
X#endif
X
Xstruct LockList *
XNewLockList(cookie)
Xvoid	       *cookie;
X{
X    struct LockList *ll;
X
X    if (ll = AllocMem((long) sizeof (*ll), MEMF_PUBLIC)) {
X	NewList(&ll->ll_List);
X	ll->ll_Cookie = cookie;
X    } else
X	error = ERROR_NO_FREE_STORE;
X
X    return ll;
X}
X
Xvoid
XFreeLockList(ll)
Xstruct LockList *ll;
X{
X    debug(("FreeLockList %08lx\n", ll));
X
X    if (ll) {
X	MayFreeVolNode(ll->ll_Cookie);  /* not too happy about this */
X	FreeMem(ll, (long) sizeof (*ll));
X	if (ll == LockList)     /* locks on current volume */
X	    LockList = NULL;
X    }
X}
END_OF_FILE
if test 18293 -ne `wc -c <'src/hanlock.c'`; then
    echo shar: \"'src/hanlock.c'\" unpacked with wrong size!
fi
# end of 'src/hanlock.c'
fi
if test -f 'src/hanmain.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/hanmain.c'\"
else
echo shar: Extracting \"'src/hanmain.c'\" \(10018 characters\)
sed "s/^X//" >'src/hanmain.c' <<'END_OF_FILE'
X/*-
X * $Id: hanmain.c,v 1.30 90/06/04 23:16:50 Rhialto Rel $
X * $Log:	hanmain.c,v $
X * Revision 1.30  90/06/04  23:16:50  Rhialto
X * Release 1 Patch 3
X * 
X *  HANMAIN.C
X *
X *  The code for the messydos file system handler.
X *
X *  Some start/stop stuff that is not really part of the
X *  file system itself but that must be done anyway.
X *
X *  This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X *  not be used or copied without a licence.
X-*/
X
X#include "dos.h"
X#include "han.h"
X
X#ifdef HDEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xextern int	CheckBootBlock;
Xextern char	DotDot[1 + 8 + 3];
Xstruct Library *IntuitionBase;
Xstatic char RCSId[] = "Messydos filing system $Revision: 1.30 $ $Date: 90/06/04 23:16:50 $, by Olaf Seibert";
X
Xbyte
XToUpper(ch)
Xregister byte	ch;
X{
X    if (ch >= 'a' && ch <= 'z')
X	return ch + ('A' - 'a');
X    if (ch == '.')
X	return '!';
X    return ch & ~DIR_DELETED_MASK;
X}
X
Xlong
Xlmin(a, b)
Xlong		a,
X		b;
X{
X    return (a < b) ? a : b;
X}
X
Xbyte	       *
XZapSpaces(begin, end)
Xregister byte  *begin,
X	       *end;
X{
X    while (end > begin && end[-1] == ' ')
X	*--end = '\0';
X
X    return end;
X}
X
X/*
X * Map an arbitrary file name to MS-DOS conventions. The output format is
X * 8+3 without dot, padded with spaces, suitable for direct comparison
X * with directory entries. Return a pointer to the delimiter found ('\0'
X * or '/'). [[Make sure that Examine/ExNext return a proper inverse of
X * this...]]
X */
X
Xbyte	       *
XToMSName(dest, source)
Xbyte	       *dest;
Xregister byte  *source;
X{
X    byte	   *dotp;
X    byte	   *slashp;
X    register int    i,
X		    len;
X
X    if (*source == '/') {       /* parentdir */
X	strncpy(dest, DotDot, 8 + 3);   /* ".." */
X	return source;
X    }
X    /*
X     * Remove any strictly leading dots. .info -> info, .indent.pro ->
X     * indent.pro, .profile -> profile, etc.
X     */
X    while (*source == '.')
X	source++;
X
X    /*
X     * Find dot and slash which are delimiters of name and extension.
X     */
X    {
X	register byte  *cp;
X
X	cp = source;
X	while (*cp) {
X	    if (*cp == '.' || *cp == '/')
X		break;
X	    cp++;
X	}
X	dotp = cp;
X	while (*cp) {
X	    if (*cp == '/')
X		break;
X	    cp++;
X	}
X	slashp = cp;
X    }
X
X    len = dotp - source;
X    if (len > 8)
X	len = 8;
X
X    for (i = 0; i < len; i++) {
X	*dest++ = ToUpper(*source++);
X    }
X    for (; i < 8; i++) {
X	*dest++ = ' ';
X    }
X
X    source = dotp + 1;
X    len = slashp - source;	/* so will be -1 if no suffix */
X    if (len > 3)
X	len = 3;
X
X    for (i = 0; i < len; i++) {
X	*dest++ = ToUpper(*source++);
X    }
X    for (; i < 3; i++) {
X	*dest++ = ' ';
X    }
X
X    return slashp;
X}
X
X/*
X * Do the Info call.
X */
X
Xlong
XMSDiskInfo(infodata)
Xstruct InfoData *infodata;
X{
X    extern DEVLIST *VolNode;
X
X    setmem(infodata, sizeof (*infodata), 0);
X
X    infodata->id_DiskState = IDDiskState;
X    infodata->id_DiskType = IDDiskType;
X    infodata->id_UnitNumber = UnitNr;
X
X    infodata->id_VolumeNode = (BPTR) CTOB(VolNode);
X    infodata->id_InUse = LockList ? 1 : 0;
X
X    if (IDDiskType == ID_DOS_DISK) {
X	infodata->id_NumBlocks = Disk.nsects;
X	infodata->id_NumBlocksUsed = Disk.nsects - Disk.nsectsfree;
X	infodata->id_BytesPerBlock = Disk.bps;
X    }
X    return DOSTRUE;
X}
X
X/*
X * We (re-)establish our List of MSFileLocks after a disk has been
X * (re-)inserted. If there are no known locks, we make the root lock from
X * the volume label, if there is one.
X *
X * We get a special cookie to hand to a cleanup routine that we must call
X * when finally all locks on the current disk are UnLock()ed. (this is
X * actually the volume node, but we don't want to know that.)
X *
X * This must be called some time after IdentifyDisk().
X */
X
Xvoid
XMSDiskInserted(locks, cookie)
Xregister struct LockList **locks;
Xvoid	       *cookie;
X{
X    debug(("MSDiskInserted %08lx\n", cookie));
X
X    LockList = *locks;
X
X    if (LockList == NULL) {
X	LockList = NewLockList(cookie);
X	RootLock = MakeLock(NULL, &Disk.vollabel, SHARED_LOCK);
X    } else {
X	RootLock = MSDupLock(GetTail(&LockList->ll_List));
X    }
X
X    InitCacheList();
X}
X
X/*
X * Remove the current disk. A place is offered to save the current
X * LockList to restore later. We must unlock the root lock since it isn't
X * a real reference to the disk, just a placeholder for dummies that hand
X * us NULL locks.
X */
X
Xint
XMSDiskRemoved(locks)
Xregister struct LockList **locks;
X{
X#ifndef READONLY
X    if (FatDirty || (DelayState & DELAY_DIRTY))
X	MSUpdate(1);            /* Force a requester */
X#endif
X
X    FreeFat();
X    FreeCacheList();
X
X    IDDiskType = ID_NO_DISK_PRESENT;
X    *locks = NULL;
X
X    if (RootLock == NULL) {
X	debug(("MSDiskRemoved with no RootLock\n"));
X	return 1;
X    }
X#ifdef HDEBUG
X    if (RootLock != GetTail(&LockList->ll_List)) {
X	debug(("RootLock not at end of LockList!\n"));
X	/* Get the lock on the root dir at the tail of the List */
X	Remove(RootLock);
X	AddTail(&LockList->ll_List, RootLock);
X    }
X#endif
X
X    /*
X     * If there are no real locks on the disk, we need not keep any
X     * information about it.
X     */
X
X    MSUnLock(RootLock);         /* may call FreeLockList and free VolNode
X				 * (!) */
X    RootLock = NULL;
X
X    if (LockList) {
X	*locks = LockList;	/* VolNode can't be gone now... */
X	LockList = NULL;
X	return 0;		/* not all references gone */
X    } else {
X	return 1;		/* all gone, even the VolNode */
X    }
X}
X
Xvoid
XHanCloseDown()
X{
X#ifdef HDEBUG
X    register struct MSFileLock *fl;
X
X    while (LockList && (fl = (struct MSFileLock *) GetHead(&LockList->ll_List))) {
X	debug(("UNLOCKING %08lx: ", fl));
X	PrintDirEntry(&fl->msfl_Msd);
X	MSUnLock(fl);           /* Remove()s it from this List */
X    }
X#endif
X    if (DiskIOReq) {
X	if (DiskIOReq->iotd_Req.io_Unit) {
X	    MSUpdate(1);
X	    CloseDevice(DiskIOReq);
X	}
X	DeleteExtIO(DiskIOReq);
X	DiskIOReq = NULL;
X    }
X    if (TimeIOReq) {
X	if (TimeIOReq->tr_node.io_Unit) {
X	    WaitIO(TimeIOReq);
X	    CloseDevice(TimeIOReq);
X	}
X	DeleteExtIO(TimeIOReq);
X	TimeIOReq = NULL;
X    }
X    if (DiskReplyPort) {
X	DeletePort(DiskReplyPort);
X	DiskReplyPort = NULL;
X    }
X    if (IntuitionBase) {
X	CloseLibrary(IntuitionBase);
X	IntuitionBase = NULL;
X    }
X}
X
Xint
XHanOpenUp()
X{
X    LockList = NULL;
X    RootLock = NULL;
X    Fat = NULL;
X    IDDiskState = ID_WRITE_PROTECTED;
X    IDDiskType = ID_NO_DISK_PRESENT;
X    DelayState = DELAY_OFF;
X    Disk.bps = MS_BPS;
X    CheckBootBlock = 1;
X    InitCacheList();
X
X    TimeIOReq = NULL;
X
X#ifdef HDEBUG
X    if (!(DiskReplyPort = CreatePort("MSH:disk.replyport", -1L)))
X	goto abort;
X#else
X    if (!(DiskReplyPort = CreatePort(NULL, -1L)))
X	goto abort;
X#endif
X
X    debug(("DiskReplyPort = 0x%08lx\n", DiskReplyPort));
X
X    if (!(DiskIOReq = CreateExtIO(DiskReplyPort, (long) sizeof (*DiskIOReq)))) {
X	debug(("Failed to CreateExtIO\n"));
X	goto abort;
X    }
X    if (OpenDevice(DevName, UnitNr, DiskIOReq, DevFlags | TDF_ALLOW_NON_3_5)) {
X	debug(("Failed to OpenDevice\n"));
X	goto abort;
X    }
X    TimeIOReq = (struct timerequest *) CreateExtIO(DiskReplyPort,
X					     (long) sizeof (*TimeIOReq));
X
X    if (TimeIOReq == NULL || OpenDevice(TIMERNAME, UNIT_VBLANK, TimeIOReq, 0L))
X	goto abort;
X    TimeIOReq->tr_node.io_Flags = IOF_QUICK;	/* For the first WaitIO() */
X
X    IntuitionBase = OpenLibrary("intuition.library", 0L);
X    return DOSTRUE;
X
Xabort:
X    HanCloseDown();
X    return 0;
X}
X
X/*
X * Relabel the disk. We create new labels if necessary.
X */
X
Xlong
XMSRelabel(newname)
Xbyte	       *newname;
X{
X#ifdef READONLY
X    return DOSFALSE;
X#else
X    /*
X     * A null or empty string means: remove the label, if any.
X     */
X    if (!newname || !*newname) {
X	if ((int) RootLock->msfl_DirSector >= (int) Disk.rootdir) {
X	    RootLock->msfl_Msd.msd_Name[0] = DIR_DELETED;
X	    RootLock->msfl_Msd.msd_Attributes = 0;
X	    WriteFileLock(RootLock);
X	    RootLock->msfl_Msd = FakeRootDirEntry.de_Msd;
X	    RootLock->msfl_DirSector = -1;
X	    Disk.vollabel = FakeRootDirEntry;
X	}
X	return DOSTRUE;
X    }
X    /*
X     * No label yet? Then we must create one, even if we need to move
X     * something else for it.
X     */
X
X    if ((int) RootLock->msfl_DirSector < 0) {
X	struct MSFileLock *new;
X
X	new = MSLock(RootLock, "><>.\\", EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
X	if ((new == NULL) && (new = EmptyFileLock)) {
X	    error = 0;
X	    if (new->msfl_DirSector == Disk.rootdir) {
X		RootLock->msfl_DirSector = Disk.rootdir;
X		RootLock->msfl_DirOffset = new->msfl_DirOffset;
X	    } else {
X		/*
X		 * Move something out of the first directory block. Try
X		 * not to move system files or directories (. ..), but
X		 * we'll do it if we need to. Set the root dir date to
X		 * now.
X		 */
X		byte	       *fromsec;
X		byte	       *tosec;
X		register struct MsDirEntry *dir;
X
X		fromsec = GetSec(Disk.rootdir);
X		tosec = GetSec(new->msfl_DirSector);
X
X		dir = (struct MsDirEntry *) fromsec;
X		while (dir->msd_Attributes & (ATTR_SYSTEM | ATTR_DIRECTORY)) {
X		    if ((byte *) ++dir >= fromsec + Disk.bps) {
X			--dir;	/* Back to last entry in the block */
X			break;	/* and move it no matter what */
X		    }
X		}
X		CopyMem(dir, tosec + new->msfl_DirOffset,
X			(long) sizeof (struct MsDirEntry));
X		MarkSecDirty(tosec);
X		RootLock->msfl_DirSector = Disk.rootdir;
X		RootLock->msfl_DirOffset = (byte *) dir - fromsec;
X
X		FreeSec(tosec);
X		FreeSec(fromsec);
X
X	    }
X	}
X	EmptyFileLock = NULL;
X	MSUnLock(new);
X    }
X    if ((int) RootLock->msfl_DirSector >= Disk.rootdir) {
X	struct DateStamp dateStamp;
X
X	/*
X	 * The easy part: Copy the name to Disk.vollabel and RootLock.
X	 */
X	{
X	    register int    i;
X	    register byte  *s,
X			   *d;
X
X	    s = newname;
X	    d = Disk.vollabel.de_Msd.msd_Name;
X	    for (i = 0; i < 8 + 3; i++) {
X		if (s[0])
X		    *d++ = ToUpper(*s++);
X		else
X		    *d++ = ' ';
X	    }
X	}
X	DateStamp(&dateStamp);
X	ToMSDate(&Disk.vollabel.de_Msd.msd_Date,
X		 &Disk.vollabel.de_Msd.msd_Time, &dateStamp);
X	RootLock->msfl_Msd = Disk.vollabel.de_Msd;	/* Just for the name and
X							 * date */
X	WriteFileLock(RootLock);
X
X	return DOSTRUE;
X    }
X    return DOSFALSE;
X#endif
X}
X
X#ifdef HDEBUG
X
X_abort()
X{
X    HanCloseDown();
X    RemTask(NULL);
X}
X
X#endif				/* HDEBUG */
END_OF_FILE
if test 10018 -ne `wc -c <'src/hanmain.c'`; then
    echo shar: \"'src/hanmain.c'\" unpacked with wrong size!
fi
# end of 'src/hanmain.c'
fi
echo shar: End of archive 2 \(of 6\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.