home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
program
/
m2posx02
/
lib.ipp
< prev
next >
Wrap
Text File
|
1993-10-23
|
46KB
|
1,417 lines
IMPLEMENTATION MODULE lib;
(*__NO_CHECKS__*)
(*****************************************************************************)
(* "fnmatch()": *)
(* Als Grundlage dienten die 'C'-Dateien 'glob.c' der GNU-Shell BASH und *)
(* 'fnmatch.c/h' der GNU-Fileutils; das vorliegende Modul hat jedoch nur *)
(* noch wenig Aehnlichkeit. Insbesondere die Behandlung der Flags fuer den *)
(* fuehrenden Punkt bei Dateinamen und den Verzeichnistrenner ist anders *)
(* geloest, da neben mindestens einem wirklichen Fehler in 'fnmatch.c' meh- *)
(* rere Stellen vorhanden sind, an denen mir nicht klar ist, ob dort nun *)
(* ein Fehler vorliegt, oder ob das tatsaechlich so gemeint war. Leider *)
(* kenne ich die "POSIX"-Definitionen fuer ``fnmatch'' nicht. *)
(* Meiner Meinung nach sind bei dem Versuch die Rekursion im '*'-Fall auf- *)
(* zuloesen, einige Kombinationen auf der Strecke geblieben. *)
(* Aber sicherlich habe ich auch noch eigene Fehler eingebaut... *)
(* *)
(* Die Funktion "rand()" ist eine direkte Umsetzung aus der GnuLib/MiNTLib. *)
(*---------------------------------------------------------------------------*)
(* STATUS: OK *)
(*---------------------------------------------------------------------------*)
(* 12-Feb-93, Holger Kleinschmidt *)
(*****************************************************************************)
VAL_INTRINSIC
CAST_IMPORT
PTR_ARITH_IMPORT
INLINE_CODE_IMPORT
FROM SYSTEM IMPORT
(* TYPE *) ADDRESS,
(* PROC *) ADR;
FROM types IMPORT
(* CONST*) NULL, XDIRSEP,
(* TYPE *) UNSIGNEDLONG, SIGNEDLONG;
FROM CTYPE IMPORT
(* PROC *) TODIGIT, TOCARD, TOUPPER, ISSPACE;
FROM pSTRING IMPORT
(* CONST*) EOS,
(* PROC *) SLEN, ASSIGN;
FROM err IMPORT
(* CONST*) eOK, eRROR, eDRVNR, eUNCMD, eCRC, eBADRQ, eSEEK, eMEDIA, eSECNF,
ePAPER, eWRITF, eREADF, eGENRL, eWRPRO, eCHNG, eUNDEV, eBADSF,
eOTHER, eINSERT, eDVNRSP, eINVFN, eFILNF, ePTHNF, eNHNDL, eACCDN,
eIHNDL, eNSMEM, eIMBA, eDRIVE, eNSAME, eNMFIL, eLOCKED, eNSLOCK,
eRANGE, eINTRN, ePLFMT, eGSBF,
E2BIG, EAGAIN, EBUSY, EDEADLK, EDOM, EEXIST, EFBIG, EINTR, EINVAL,
EISDIR, EMLINK, ENAMETOOLONG, ENOLCK, ENOSPC, ENOTEMPTY, ENOTTY,
EPIPE, ERANGE, ESPIPE, ELOOP,
(* VAR *) errno;
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
CONST
MINLINT = 80000000H;
MAXLINT = 7FFFFFFFH;
MAXLCARD = 0FFFFFFFFH;
VAR
Seed : SIGNEDLONG;
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
PROCEDURE bswap ((* EIN/ -- *) blk1 : ADDRESS;
(* EIN/ -- *) blk2 : ADDRESS;
(* EIN/ -- *) len : UNSIGNEDLONG );
(*T*)
(* Die Prozedur veraendert nur die Register D0/D1 und A0/A1.
* Dies sollte fuer die gaengigen M2-Compiler ausreichen. Falls
* weitere Register gerettet werden muessen, koennen die
* einkommentierten Inline-Sequenzen benutzt werden, ohne dass
* der restliche Code geaendert werden muss.
*)
BEGIN
SETREG(8, blk1);
SETREG(9, blk2);
SETREG(0, len);
(*
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w D2,-(SP)
#else
movem.l D1/D2/A0/A1,-(SP)
#endif
tst.l D0
beq.s ende
move.w A0,D1
move.w A1,D2
eor.b D2,D1 ; genau eine der Adressen ungerade ?
btst #0,D1 ;
beq.s fastswap ; B: nein
slowswap: ; Bloecke byteweise vertauschen
move.b (A0),D1
move.b (A1),(A0)+
move.b D1,(A1)+
subq.l #1,D0
bne.s slowswap
bra.s ende ; fertig
fastswap:
btst #0,D2 ; beide Adr. ungerade oder beide gerade ?
beq.s longcnt ; B: beide gerade
move.b (A0),D1 ; sonst ein Byte vorneweg tauschen
move.b (A1),(A0)+ ; -> gerade Adresse
move.b D1,(A1)+
subq.l #1,D0 ; eins weniger zu tauschen
longcnt:
move.b D0,D2 ; fuer spaeteren Ueberhangtest
lsr.l #2,D0 ; Anzahl auszutauschender Langworte
beq.s tstwswap ; B: weniger als 4 Byte
swaplp: ; Bloecke langwortweise vertauschen
move.l (A0),D1
move.l (A1),(A0)+
move.l D1,(A1)+
subq.l #1,D0
bne.s swaplp
tstwswap:
btst #1,D2 ; noch ein zusaetzl. Wort auszutauschen ?
beq.s tstbswap ; B: nein
move.w (A0),D1
move.w (A1),(A0)+
move.w D1,(A1)+
tstbswap:
btst #0,D2 ; noch ein Byte ?
beq.s ende ; B: nein, fertig
move.b (A0),D1
move.b (A1),(A0)
move.b D1,(A1)
ende:
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w (SP)+,D2
#else
movem.l (SP)+,D1/D2/A0/A1
#endif
*)
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(3F02H);
#else
CODE(48E7H,40C0H);
#endif
CODE(4A80H,674EH,3208H,3409H,0B501H,0801H,0000H);
CODE(670CH,1210H,10D1H,12C1H,5380H,66F6H,6036H,0802H);
CODE(0000H,6708H,1210H,10D1H,12C1H,5380H,1400H,0E488H);
CODE(670AH,2210H,20D1H,22C1H,5380H,66F6H,0802H,0001H);
CODE(6706H,3210H,30D1H,32C1H,0802H,0000H,6706H,1210H);
CODE(1091H,1281H);
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(341FH);
#else
CODE(4CDFH,0302H);
#endif
END bswap;
(*---------------------------------------------------------------------------*)
PROCEDURE bcopy ((* EIN/ -- *) src : ADDRESS;
(* EIN/ -- *) dst : ADDRESS;
(* EIN/ -- *) len : UNSIGNEDLONG );
(*T*)
(* Die Prozedur veraendert nur die Register D0/D1 und A0/A1.
* Dies sollte fuer die gaengigen M2-Compiler ausreichen. Falls
* weitere Register gerettet werden muessen, koennen die
* einkommentierten Inline-Sequenzen benutzt werden, ohne dass
* der restliche Code geaendert werden muss.
*)
BEGIN
SETREG(8, src); (* a0 -> src *)
SETREG(9, dst); (* a1 -> dst *)
SETREG(0, len); (* d0 := len *)
(*
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w D2,-(SP)
#else
movem.l D1/D2/A0/A1,-(SP)
#endif
tst.l D0 ; len = 0 ?
beq ende ; B: ja, nix zu tun
cmpa.l A0,A1 ; Zieladresse groesser als Quelladresse ?
bhi special ; B: ja, muss von hinten nach vorne kopiert werden
* ; falls sich die Bereiche ueberschneiden
move.w A0,D1 ; genau eine Adresse ungerade ?
move.w A1,D2 ;
eor.b D2,D1 ;
btst #0,D1 ;
beq.s nfastcpy ; B: nein, beide gerade/ungerade -> schnell kopieren
* Es muss langsam byteweise kopiert werden.
* Der Trick mit dem Sprung in die Kopieranweisungen stammt aus
* dem "bcopy()" der GnuLib/MiNTLib
move.w D0,D1 ; die Anzahl Bytes im letzten unvollstaendigen 8-er-
neg.w D1 ; Block und entsprechenden Index in die
andi.w #7,D1 ; Kopieranweisungen berechnen
add.w D1,D1 ;
addq.l #7,D0 ; plus 1 Block, falls unvollst. Block
lsr.l #3,D0 ; Anzahl kompletter 8-er Bloecke [+ unvollst. Block]
jmp nloop8(PC,D1.w) ; ersten vollst. oder unvollst. Block kopieren
nloop8: ; jeweils 8 Byte kopieren
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
move.b (A0)+,(A1)+
subq.l #1,D0
bne.s nloop8
bra ende
nfastcpy:
btst #0,D2 ; beide Adressen ungerade ?
beq.s neven ; B: nein
move.b (A0)+,(A1)+ ; ein Byte vorneweg -> gerade Adressen
subq.l #1,D0 ; ein Byte weniger zu kopieren
neven:
move.b D0,D2 ; fuer spaeteren Ueberhangtest
lsr.l #2,D0 ; Anzahl zu kopierender Langworte
beq.s ntstw ; B: weniger als 4 Byte
move.w D0,D1 ; die Anzahl Bytes im letzten unvollstaendigen 32-er-
neg.w D1 ; Block und entsprechenden Index in die
andi.w #7,D1 ; Kopieranweisungen berechnen
add.w D1,D1 ;
addq.l #7,D0 ; plus 1 Block, falls unvollst. Block
lsr.l #3,D0 ; Anzahl von 32-er-Bloecken [+ unvollst Block]
jmp nloop32(PC,D1.w) ; ersten vollst. oder unvollst. Block kopieren
nloop32: ; jeweils 32 Byte kopieren
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
move.l (A0)+,(A1)+
subq.l #1,D0
bne.s nloop32
ntstw:
btst #1,D2 ; ein zusaetzliches Wort ?
beq.s ntstb ; B: nein
move.w (A0)+,(A1)+
ntstb:
btst #0,D2 ; ein zusaetzliches Byte ?
beq ende ; B: nein, fertig
move.b (A0)+,(A1)+
bra.s ende
* wie oben, nur alles von hinten nach vorne kopieren
special:
adda.l D0,A0
adda.l D0,A1
move.w A0,D1
move.w A1,D2
eor.b D2,D1
btst #0,D1
beq.s sfastcpy
move.w D0,D1
neg.w D1
andi.w #7,D1
add.w D1,D1
addq.l #7,D0
lsr.l #3,D0
jmp sloop8(PC,D1.w)
sloop8:
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
move.b -(A0),-(A1)
subq.l #1,D0
bne.s sloop8
bra.s ende
sfastcpy:
btst #0,D2
beq.s seven
move.b -(A0),-(A1)
subq.l #1,D0
seven:
move.b D0,D2
lsr.l #2,D0
beq.s ststw
move.w D0,D1
neg.w D1
andi.w #7,D1
add.w D1,D1
addq.l #7,D0
lsr.l #3,D0
jmp sloop32(PC,D1.w)
sloop32:
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
move.l -(A0),-(A1)
subq.l #1,D0
bne.s sloop32
ststw:
btst #1,D2
beq.s ststb
move.w -(A0),-(A1)
ststb:
btst #0,D2
beq.s ende
move.b -(A0),-(A1)
ende:
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w (SP)+,D2
#else
movem.l (SP)+,D1/D2/A0/A1
#endif
*)
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(3F02H);
#else
CODE(48E7H,40C0H);
#endif
CODE(4A80H,6700H,0106H,0B3C8H,6200H,0082H,3208H);
CODE(3409H,0B501H,0801H,0000H,672AH,3200H,4441H,0241H);
CODE(0007H,0D241H,5E80H,0E688H,4EFBH,1002H,12D8H,12D8H);
CODE(12D8H,12D8H,12D8H,12D8H,12D8H,12D8H,5380H,66ECH);
CODE(6000H,00CAH,0802H,0000H,6704H,12D8H,5380H,1400H);
CODE(0E488H,6726H,3200H,4441H,0241H,0007H,0D241H,5E80H);
CODE(0E688H,4EFBH,1002H,22D8H,22D8H,22D8H,22D8H,22D8H);
CODE(22D8H,22D8H,22D8H,5380H,66ECH,0802H,0001H,6702H);
CODE(32D8H,0802H,0000H,6700H,0084H,12D8H,607EH,0D1C0H);
CODE(0D3C0H,3208H,3409H,0B501H,0801H,0000H,6728H,3200H);
CODE(4441H,0241H,0007H,0D241H,5E80H,0E688H,4EFBH,1002H);
CODE(1320H,1320H,1320H,1320H,1320H,1320H,1320H,1320H);
CODE(5380H,66ECH,6046H,0802H,0000H,6704H,1320H,5380H);
CODE(1400H,0E488H,6726H,3200H,4441H,0241H,0007H,0D241H);
CODE(5E80H,0E688H,4EFBH,1002H,2320H,2320H,2320H,2320H);
CODE(2320H,2320H,2320H,2320H,5380H,66ECH,0802H,0001H);
CODE(6702H,3320H,0802H,0000H,6702H,1320H);
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(341FH);
#else
CODE(4CDFH,0302H);
#endif
END bcopy;
(*---------------------------------------------------------------------------*)
PROCEDURE bzero ((* EIN/ -- *) dst : ADDRESS;
(* EIN/ -- *) len : UNSIGNEDLONG );
(*T*)
(* Die Prozedur veraendert nur die Register D0/D1 und A0/A1.
* Dies sollte fuer die gaengigen M2-Compiler ausreichen. Falls
* weitere Register gerettet werden muessen, koennen die
* einkommentierten Inline-Sequenzen benutzt werden, ohne dass
* der restliche Code geaendert werden muss.
*)
BEGIN
SETREG(8, dst); (* a0 -> dst *)
SETREG(0, len); (* d0 := len *)
(*
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w D2,-(SP)
#else
movem.l D1/D2/A0/A1,-(SP)
#endif
tst.l D0 ; len = 0 ?
beq.s ende ; B: ja, nix zu tun
moveq #0,D1 ; wird zum Loeschen benoetigt
move.w A0,D2 ; Anfangsadresse ungerade ?
btst #0,D2 ;
beq.s even ; B: nein
move.b D1,(A0)+ ; sonst ein Byte vorneweg kopieren
subq.l #1,D0
even:
movea.w D0,A1 ; Anzahl der Bytes fuer spaeteren Ueberhangtest merken
lsr.l #2,D0 ; Anzahl von Langworten
beq.s tstw ; B: weniger als 4 Byte zu kopieren
move.w D0,D2 ; die Anzahl Bytes im letzten unvollstaendigen 32-er-
neg.w D2 ; Block und entsprechenden Index in die
andi.w #7,D2 ; Kopieranweisungen berechnen
add.w D2,D2
addq.l #7,D0 ; plus 1 Block, falls unvollst. Block (< 32 Byte)
lsr.l #3,D0 ; Anzahl kompletter 32-Byte-Bloecke [+ unvollst. Block]
jmp loop32(PC,D2.w) ; ersten kompletten oder unvollst. Block loeschen
loop32: ; jeweils 32 Byte loeschen
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
move.l D1,(A0)+
subq.l #1,D0
bne.s loop32 ; naechsten kompletten Block loeschen
tstw:
move.w A1,D2
btst #1,D2 ; ein zusaetzliches Wort ?
beq.s tstb ; B: nein
move.w D1,(A0)+
tstb:
btst #0,D2 ; ein zusaetzliches Byte ?
beq.s ende ; B: nein
move.b D1,(A0)+
ende:
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* keine Register retten */
#elif HM2
move.w (SP)+,D2
#else
movem.l (SP)+,D1/D2/A0/A1
#endif
*)
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(3F02H);
#else
CODE(48E7H,40C0H);
#endif
CODE(4A80H,674CH,7200H,3408H,0802H,0000H,6704H);
CODE(10C1H,5380H,3240H,0E488H,6726H,3400H,4442H,0242H);
CODE(0007H,0D442H,5E80H,0E688H,4EFBH,2002H,20C1H,20C1H);
CODE(20C1H,20C1H,20C1H,20C1H,20C1H,20C1H,5380H,66ECH);
CODE(3409H,0802H,0001H,6702H,30C1H,0802H,0000H,6702H);
CODE(10C1H);
#if LPRM2 || SPCM2 || TDIM2 || MM2 || FTLM2
/* */
#elif HM2
CODE(341FH);
#else
CODE(4CDFH,0302H);
#endif
END bzero;
(*---------------------------------------------------------------------------*)
PROCEDURE lfind ((* EIN/ -- *) key : ADDRESS;
(* EIN/ -- *) base : ADDRESS;
(* EIN/ -- *) nelems : UNSIGNEDLONG;
(* EIN/ -- *) size : UNSIGNEDLONG;
(* EIN/ -- *) compare : CompareProc ): ADDRESS;
(*T*)
VAR last : ADDRESS;
BEGIN
IF (key = NULL) OR (base = NULL) OR (size = LC(0)) OR (nelems = LC(0)) THEN
RETURN(NULL);
END;
last := ADDADR(base, (nelems - LC(1)) * size);
(* Indem das letzte zu vergleichende Feldelement
* mit dem zu suchenden ausgetauscht wird, wirkt
* es als Endemarke fuer das Suchen.
*)
bswap(key, last, size);
WHILE compare(base, last) <> 0 DO
base := ADDADR(base, size);
END;
(* Das Vertauschen muss natuerlich wieder rueckgaengig gemacht werden. *)
bswap(key, last, size);
(* Wenn das gesamte Feld durchsucht wurde, muss noch
* der Vergleich mit dem letzten Element erfolgen,
* ansonsten wurde schon vorher ein Element mit dem
* gesuchten Wert gefunden.
*)
IF (base = last) AND (compare(last, key) <> 0) THEN
RETURN(NULL);
ELSE
RETURN(base);
END;
END lfind;
(*---------------------------------------------------------------------------*)
PROCEDURE bsearch ((* EIN/ -- *) key : ADDRESS;
(* EIN/ -- *) base : ADDRESS;
(* EIN/ -- *) nelems : UNSIGNEDLONG;
(* EIN/ -- *) size : UNSIGNEDLONG;
(* EIN/ -- *) compare : CompareProc ): ADDRESS;
(*T*)
VAR left : UNSIGNEDLONG;
right : UNSIGNEDLONG;
mid : UNSIGNEDLONG;
BEGIN
IF (key = NULL) OR (base = NULL) OR (size = LC(0)) OR (nelems = LC(0)) THEN
RETURN(NULL);
END;
left := 0;
right := nelems - LC(1);
WHILE left < right DO
mid := (left + right) DIV LC(2);
(* left <= mid < right *)
IF compare(ADDADR(base, mid * size), key) < 0 THEN
left := mid + LC(1);
ELSE
right := mid;
END;
END;
base := ADDADR(base, left * size);
IF compare(base, key) = 0 THEN
RETURN(base);
ELSE
RETURN(NULL);
END;
END bsearch;
(*---------------------------------------------------------------------------*)
PROCEDURE qsort ((* EIN/ -- *) base : ADDRESS;
(* EIN/ -- *) nelems : UNSIGNEDLONG;
(* EIN/ -- *) size : UNSIGNEDLONG;
(* EIN/ -- *) compare : CompareProc );
(*T*)
CONST direct = LC(8);
VAR cmpP : ADDRESS;
VAR rP : ADDRESS;
(* wird bei Selectionsort benutzt, und ist hier deklariert,
* damit er keinen Stackplatz beim rekursiven Aufruf von "sort()"
* belegt. Er braucht keine lokale Variable von "sort()" zu sein,
* da er nur vom Selectionsort benutzt werden, aus dem heraus kein
* weiterer rekursiver Aufruf mehr stattfindet.
*)
(* Das Prinzip von Quicksort ist an sich recht einfach:
Als erstes wird ein beliebiges Element des Feldes ausgewaehlt, dann
werden von beiden Enden des Feldes zur Mitte hin Elemente gesucht, die
groesser bzw. kleiner oder gleich dem Vergleichselement sind - diese
beiden Elemente werden ausgetauscht; das Austauschen wird solange
wiederholt, bis sich die beiden Suchzeiger ueberschneiden; In der linken
Haelfte befinden sich dann die Elemente, die kleiner oder gleich dem
Vergleichselement sind, in der rechten Haelfte befinden sich die Elemente,
die groesser oder gleich dem Vergleichselement sind.
Diese Prozedur wird jetzt mit den beiden Haelften erneut ausgefuehrt
usw. bis die zu sortierenden Teilfelder nur noch ein Element gross sind,
dann ist das gesamte Feld sortiert. Die wiederholte Ausfuehrung gleicher
Taetigkeiten schreit natuerlich nach Rekursion.
Der Aufwand:
Den Partitionierungsvorgang kann man sich als das Suchen eines bestimmten
Elementes, naemlich das mit dem naechstgroesseren Wert, vorstellen.
Angenommen, das Vergleichselement ist immer das wertemaessig mittlere
Element: in diesem Fall wird die Suche zur Binaersuche, da immer die
Haelfte der Werte beim naechsten Suchvorgang ausgeschlossen wird. Der
Aufwand des binaeren Suchens betraegt O( ld( n )); da wir n Elemente
haben, betraegt der Sortieraufwand O( n * ld( n )).
Das waere der Idealfall.
Im schlechtesten Fall ist das ausgewaehlte Vergleichselement immer das
wertemaessig groesste bzw. kleinste; in diesem Fall wird die Suche zur
linearen Suche, deren mittlerer Aufwand n/2 betraegt; der Aufwand des
Sortierens betraegt dann O( n * n ). Ein Beispiel waere ein bereits
sortiertes Feld, bei dem man als Vergleichselement immer das erste
auswaehlt.
Den schlechtesten Fall kann man zwar nicht ganz ausschliessen, aber
doch sehr unwahrscheinlich machen: die einfachste Methode ist, als
Vergleichselement das positionsmaessig mittlere zu nehmen; die
Wahrscheinlichkeit hierbei haeufig die Extremwerte zu erwischen ist
gering. Noch unwahrscheinlicher wird es, wenn als Vergleichselement das
wertemaessig mittlere aus dreien genommen wird (z.B. dem positionsmaessig
ersten, mittleren und letzten).
Abgesehen von der Wahl des Vergleichselementes gibt es weitere
Moeglichkeiten zur Optimierung:
- Zuerst die kleinere Haelfte weitersortieren.
Hierdurch betraegt die Stackbelastung nur ~ld(n).
- Hinter dem rekursiven Aufruf zur Sortierung der zweiten, groesseren
Haelfte folgt kein Ausdruck, der vom Ergebnis dieses Aufrufs abhaengt;
die Sortierung der groesseren Feldes kann deswegen iterativ geschehen.
- Wie alle hoeheren Sortiermethoden ist auch bei Quicksort die Leistung
bei kleinem n schwach, da der Verwaltungsaufwand relativ gross ist.
Unterschreitet daher die Groesse des zu sortierenden Teilfeldes ein
hinreichend kleines n, kann das Feld durch eine einfachere Methode
(direktes Einfuegen, direkte Auswahl...) zuende sortiert werden.
*)
PROCEDURE sort ((* EIN/ -- *) bot : UNSIGNEDLONG;
(* EIN/ -- *) top : UNSIGNEDLONG );
VAR left : UNSIGNEDLONG;
right : UNSIGNEDLONG;
leftP : ADDRESS;
rightP : ADDRESS;
BEGIN (* sort *)
WHILE bot < top DO
left := bot;
right := top;
leftP := ADDADR(base, bot * size);
rightP := ADDADR(base, top * size);
IF top - bot < direct THEN
(* Direktes Sortieren durch Auswaehlen.
* 'SelectionSort' ist bei so wenigen Elementen
* (< 10) schneller als 'InsertionSort'.
*
* Funktionsweise:
* Der Reihe nach vom ersten bis zum vorletzten
* Element wird ein Vergleichselement gewaehlt,
* das mit allen Elementen rechts von ihm verglichen
* wird; das Minimum und das Vergleichselement
* werden ausgetauscht.
*)
WHILE DIFADR(leftP, rightP) < LIC(0) DO
cmpP := leftP;
rP := ADDADR(leftP, size);
WHILE DIFADR(rP, rightP) <= LIC(0) DO
IF compare(rP, cmpP) < 0 THEN
cmpP := rP;
END;
rP := ADDADR(rP, size);
END; (* WHILE *)
IF cmpP <> leftP THEN
bswap(cmpP, leftP, size);
END;
leftP := ADDADR(leftP, size);
END;
RETURN; (* fertig *)
ELSE
(* Es wird kein groesserer Aufwand bei der Auswahl des
* mittleren Elementes betrieben, da dies in den allermeisten
* Faellen mehr Zeit kostet, als es Zeit einspart, wenn das
* Feld wirklich so unguenstig belegt ist, dass das
* positionsmaessig mittlere immer das Extremelement ist.
*)
cmpP := ADDADR(base, ((left + right) DIV LC(2)) * size);
REPEAT
(* Bei der Suche nach den auszutauschenden Elementen gibt es
* zwei Moeglichkeiten:
*
* - Vom jeweiligen Rand ausgehend wird ein Element gesucht,
* dass groesser/kleiner ODER GLEICH dem Vergleichselement
* ist. Durch die Gleichbedingung wirkt das Vergleichselement
* als Endemarke der Iteration, da auf jeden Fall dieses
* Element gefunden wird.
* Der Nachteil: Kommt der Wert des Vergleichselementes
* haufig in dem Feld vor, so finden entsprechend viele
* unnoetige Austauschoperationen statt.
*
* - Vom jeweiligen Rand her wird ein Element gesucht, dass
* ECHT groesser (kleiner) als das Vergleichselement ist.
* Das vermeidet die unnoetigen Austauschoperationen bei
* Elementen, die gleich dem Vergleichselement sind;
* allerdings wirkt das Vergleichselement nun nicht mehr
* als Marke (es kann sein, dass kein Element gefunden
* wird, das echt groesser/kleiner als das Vergleichselement
* ist), sodass zusaetzlich der Laufindex als Endebedingung
* abgefragt werden muss.
*
* Es wird die erste Methode benutzt, da eine grosse Anzahl
* von Elementen mit gleichem Schluessel sicher selten vorkommt,
* und bei der zweiten Methode dafuer an anderer Stelle mehr
* Aufwand getrieben werden muss.
*)
WHILE compare(leftP, cmpP) < 0 DO
leftP := ADDADR(leftP, size);
INC(left);
END;
WHILE compare(cmpP, rightP) < 0 DO
rightP := SUBADR(rightP, size);
DEC(right);
END;
IF left <= right THEN
bswap(leftP, rightP, size);
(* Falls das Vergleichselement beim Austausch beteiligt war,
* muss auch der Zeiger auf das Vergleichselement entsprechend
* neu gesetzt werden.
*)
IF cmpP = leftP THEN
cmpP := rightP;
ELSIF cmpP = rightP THEN
cmpP := leftP;
END;
IF left < top THEN
INC(left);
leftP := ADDADR(leftP, size);
END;
IF right > bot THEN
DEC(right);
rightP := SUBADR(rightP, size);
END;
END;
UNTIL left > right;
(* (bot<=i<left)->(x[i]<=x[cmpP]) & (right<i<=top)->(x[i]>=x[cmpP]) *)
IF (right - bot) < (top - left) THEN
(* Nur das kleinere Teilfeld wird rekursiv
* weitersortiert, das groessere wird durch
* die darauffolgenden Zuweisungen in der
* Schleife weiter zerlegt.
*)
IF bot < right THEN
(* Rekursionsbasis: Teilfeld ist sortiert,
* wenn es nur noch ein Element enthaelt.
*)
sort(bot, right);
END;
(* Die Elemente left von <left> sind jetzt sortiert,
* die groessere Haelfte wird in der Schleife
* weiterbearbeitet.
*)
bot := left;
ELSE
IF left < top THEN
sort(left, top);
END;
top := right;
END; (* IF (right ..*)
END; (* IF (top ..*)
END; (* WHILE *)
END sort;
BEGIN (* qsort *)
IF (base = NULL) OR (size = LC(0)) OR (nelems <= LC(1)) THEN
RETURN;
END;
sort(LC(0), nelems - LC(1));
END qsort;
(*---------------------------------------------------------------------------*)
PROCEDURE ValToStr ((* EIN/ -- *) val : UNSIGNEDLONG;
(* EIN/ -- *) signed : BOOLEAN;
(* EIN/ -- *) base : CARDINAL;
(* -- /AUS *) VAR str : ARRAY OF CHAR );
(*T*)
VAR basis : UNSIGNEDLONG;
len, i : CARDINAL;
sign : BOOLEAN;
digits : ARRAY [0..33] OF CHAR;
BEGIN
IF (base < 2) OR (base > 36) THEN
basis := 10;
ELSE
basis := VAL(UNSIGNEDLONG,base);
END;
sign := signed AND (base = 10) AND (CAST(SIGNEDLONG,val) < LIC(0));
IF sign THEN
IF val <> MINLINT THEN
(* Abfrage verhindert Ueberlauffehler, da MINLINT im
* Zweierkomplement nicht als positive Zahl darstellbar ist
* und unveraendert bleibt.
*)
val := CAST(UNSIGNEDLONG,-CAST(SIGNEDLONG,val));
END;
END;
(* Die Zahl von hinten nach vorne in String wandeln;
* durch die REPEAT-Schleife wird auch die Null
* dargestellt.
*)
len := 0;
REPEAT
digits[len] := TOUPPER(TODIGIT(VAL(CARDINAL,val MOD basis)));
val := val DIV basis;
INC(len);
UNTIL val = LC(0);
IF sign THEN
digits[len] := '-';
INC(len);
END;
IF len > VAL(CARDINAL,HIGH(str)) THEN
len := VAL(CARDINAL,HIGH(str)) + 1;
ELSE
str[len] := 0C;
END;
(* Jetzt wird die Zahlendarstellung in umgekehrter
* Reihenfolge aus dem Hilfsstring in den eigentlichen
* String uebertragen. Ausserdem werden Prefix und fuehrende
* Nullen hinzugefuegt.
*)
i := 0;
WHILE len > 0 DO
DEC(len);
str[i] := digits[len];
INC(i);
END;
END ValToStr;
(*---------------------------------------------------------------------------*)
PROCEDURE ltoa ((* EIN/ -- *) num : SIGNEDLONG;
(* -- /AUS *) VAR str : ARRAY OF CHAR;
(* EIN/ -- *) base : CARDINAL );
(*T*)
BEGIN
ValToStr(CAST(UNSIGNEDLONG,num), TRUE, base, str);
END ltoa;
(*---------------------------------------------------------------------------*)
PROCEDURE ultoa ((* EIN/ -- *) num : UNSIGNEDLONG;
(* -- /AUS *) VAR str : ARRAY OF CHAR;
(* EIN/ -- *) base : CARDINAL );
(*T*)
BEGIN
ValToStr(num, FALSE, base, str);
END ultoa;
(*---------------------------------------------------------------------------*)
#if has_REF
PROCEDURE StrToVal ((* EIN/ -- *) REF str : ARRAY OF CHAR;
#else
PROCEDURE StrToVal ((* EIN/ -- *) VAR str : ARRAY OF CHAR;
#endif
(* EIN/ -- *) max : UNSIGNEDLONG;
(* EIN/ -- *) basis : CARDINAL;
(* EIN/ -- *) signed : BOOLEAN;
(* -- /AUS *) VAR nextIdx : CARDINAL;
(* -- /AUS *) VAR val : UNSIGNEDLONG );
(*T*)
VAR idx : CARDINAL;
neg : BOOLEAN;
digit : CHAR;
maxDivBase : UNSIGNEDLONG;
maxLastDigit : UNSIGNEDLONG;
num : UNSIGNEDLONG;
base : UNSIGNEDLONG;
BEGIN
val := 0;
idx := 0;
neg := FALSE;
(* Fuehrende Leerzeichen tun nichts zur Sache *)
WHILE (idx <= VAL(CARDINAL,HIGH(str))) AND ISSPACE(str[idx]) DO
INC(idx);
END;
(* Moegliches Vorzeichen feststellen, bei negativer Zahl ist der
* maximale Wert um eins groesser (im Zweierkomplement).
*)
IF signed AND (idx <= VAL(CARDINAL,HIGH(str))) THEN
digit := str[idx];
neg := digit = '-';
IF digit = '+' THEN
INC(idx);
ELSIF neg THEN
(* Negative Zahlen haben einen um eins groesseren
* Wertebereich als positive Zahlen (die Null ausgenommen).
*)
INC(idx);
INC(max);
END;
END;
(* Keine Zahl kann folgen => Fehler *)
IF idx > VAL(CARDINAL,HIGH(str)) THEN
nextIdx := idx;
RETURN;
END;
IF (basis < 2) OR (basis > 36) THEN
basis := 0;
END;
base := VAL(UNSIGNEDLONG,basis);
digit := str[idx];
IF basis = 0 THEN
(* Die Basis der Zahl soll aus der Zeichenfolge hervorgehen *)
INC(idx);
IF digit = '%' THEN
(* Zahl in Binaerdarstellung *)
base := 2;
ELSIF digit = '0' THEN
(* Zahl in Sedezimal- oder Oktaldarstellung oder einzelne Null *)
IF (idx <= VAL(CARDINAL,HIGH(str))) AND (TOUPPER(str[idx]) = 'X') THEN
base := 16;
INC(idx);
ELSE
base := 8;
END;
ELSIF digit = '$' THEN
base := 16;
ELSE
base := 10;
DEC(idx);
END;
(* Die Basis ist angegeben, zusaetzliche Angabe in Repraesentation
* ueberlesen (Oktalnull stoert nicht).
*)
ELSIF (basis = 2) AND (digit = '%') THEN
(* Binaerdarstellung *)
INC(idx);
ELSIF basis = 16 THEN
(* Sedezimaldarstellung *)
IF digit = '$' THEN
INC(idx);
ELSIF (digit = '0')
AND (idx < VAL(CARDINAL,HIGH(str)))
AND (TOUPPER(str[idx+1]) = 'X')
THEN
INC(idx, 2);
END;
END;
maxDivBase := max DIV base;
maxLastDigit := max MOD base;
LOOP
(* Abbrechen, sobald der String zuende ist, oder ein Zeichen gefunden
* wurde, das keine gueltige Ziffer ist, oder ein Ueberlauf stattfinden
* wuerde.
*)
nextIdx := idx;
IF idx > VAL(CARDINAL,HIGH(str)) THEN
EXIT;
END;
digit := str[idx];
num := VAL(UNSIGNEDLONG,TOCARD(digit));
IF num >= base THEN
EXIT;
END;
(* Da <val> mit jedem neuen Digit um eine Stelle erweitert wird,
* wird fuer die Ueberlaufpruefung der bisherige <val> vor der
* Erweiterung mit einem Zehntel des Maximalvales verglichen;
* wuerde nach der Erweiterung verglichen, waere der Ueberlauf
* ja womoeglich schon passiert, und dabei koennte auch ein
* UNSIGNEDLONG-Ueberlauf auftreten -- ein Vergleich wuerde dann
* nur Unsinn produzieren.
* Ist der bisherige Wert kleiner als ein Zehntel des Maximums,
* kann kein Ueberlauf auftreten, ist der bisherige Wert gleich
* dem Maximumszehntel, muss geprueft werden, ob das neue Digit
* den Wert des letzten Digits des Maximums ueberschreitet.
*)
IF (val < maxDivBase)
OR (val = maxDivBase) AND (num <= maxLastDigit)
THEN
val := val * base + num;
INC(idx);
ELSE (* Ueberlauf *)
errno := ERANGE;
IF neg AND (max <> MINLINT) THEN
val := CAST(UNSIGNEDLONG,-CAST(SIGNEDLONG,max));
ELSE
val := max;
END;
RETURN;
END;
END; (* LOOP *)
IF neg AND (val <> MINLINT) THEN
(* Wenn vor der Zahl ein '-' stand und negative Zahlen erlaubt
* sind, den bisher positiven Zahlenwert in einen negativen wandeln.
* Abfrage auf MINLINT verhindert Ueberlauf.
*)
val := CAST(UNSIGNEDLONG,-CAST(SIGNEDLONG,val));
END;
END StrToVal;
(*---------------------------------------------------------------------------*)
PROCEDURE strtol ((* EIN/ -- *) REF str : ARRAY OF CHAR;
(* -- /AUS *) VAR end : CARDINAL;
(* EIN/ -- *) base : CARDINAL ): SIGNEDLONG;
(*T*)
VAR val : UNSIGNEDLONG;
BEGIN
StrToVal(str, MAXLINT, base, TRUE, end, val);
RETURN(CAST(SIGNEDLONG,val));
END strtol;
(*---------------------------------------------------------------------------*)
PROCEDURE strtoul ((* EIN/ -- *) REF str : ARRAY OF CHAR;
(* -- /AUS *) VAR end : CARDINAL;
(* EIN/ -- *) base : CARDINAL ): UNSIGNEDLONG;
(*T*)
VAR val : UNSIGNEDLONG;
BEGIN
StrToVal(str, MAXLCARD, base, FALSE, end, val);
RETURN(val);
END strtoul;
(*---------------------------------------------------------------------------*)
PROCEDURE strerror ((* EIN/ -- *) errnum : INTEGER;
(* -- /AUS *) VAR errstr : ARRAY OF CHAR );
(*T*)
VAR text : ARRAY [0..40] OF CHAR;
BEGIN
CASE errnum OF
eOK : text := "OK";
|eRROR : text := "error";
|eDRVNR : text := "device not ready";
|eUNCMD : text := "unknown command";
|eCRC : text := "crc error";
|eBADRQ : text := "bad request";
|eSEEK : text := "seek error";
|eMEDIA : text := "unknown media";
|eSECNF : text := "sector not found";
|ePAPER : text := "out of paper";
|eWRITF : text := "write failure";
|eREADF : text := "read failure";
|eGENRL : text := "general error";
|eWRPRO : text := "write protected";
|eCHNG : text := "media changed";
|eUNDEV : text := "unknown device";
|eBADSF : text := "bad sectors found";
|eOTHER : text := "another disk";
|eINSERT : text := "insert media";
|eDVNRSP : text := "device not responding";
|eINVFN : text := "invalid function number";
|eFILNF : text := "file not found";
|ePTHNF : text := "path not found";
|eNHNDL : text := "no more handles";
|eACCDN : text := "access denied";
|eIHNDL : text := "invalid handle";
|eNSMEM : text := "out of memory";
|eIMBA : text := "invalid memory block";
|eDRIVE : text := "invalid drive";
|eNSAME : text := "different drives";
|eNMFIL : text := "no more files";
|eLOCKED : text := "file locked";
|eNSLOCK : text := "invalid lock";
|eRANGE : text := "range error";
|eINTRN : text := "internal error";
|ePLFMT : text := "not executable";
|eGSBF : text := "memory block growth failure";
|E2BIG : text := "argument list too long";
|EAGAIN : text := "try again";
|EBUSY : text := "resource unavailable";
|EDEADLK : text := "deadlock would result";
|EDOM : text := "domain error";
|EEXIST : text := "file exists";
|EFBIG : text := "file too large";
|EINTR : text := "interrupted by signal";
|EINVAL : text := "invalid argument";
|EISDIR : text := "is a directory";
|EMLINK : text := "too many links";
|ENAMETOOLONG : text := "filename too long";
|ENOLCK : text := "no locks available";
|ENOSPC : text := "no space left on device";
|ENOTEMPTY : text := "directory not empty";
|ENOTTY : text := "wrong i/o control op";
|EPIPE : text := "broken pipe";
|ERANGE : text := "result too large";
|ESPIPE : text := "invalid seek";
|ELOOP : text := "too many symbolic links";
ELSE
text := "unknown error";
END;
ASSIGN(text, errstr);
END strerror;
(*---------------------------------------------------------------------------*)
PROCEDURE rand ( ): UNSIGNEDLONG;
(*T*)
CONST
A = LIC(16807);
M = LIC(2147483647);
Q = LIC(127773);
R = LIC(2836);
BEGIN
Seed := A * (Seed MOD Q) - R * (Seed DIV Q);
IF Seed < LIC(0) THEN
INC(Seed, M);
END;
RETURN(Seed);
END rand;
(*---------------------------------------------------------------------------*)
PROCEDURE srand ((* EIN/ -- *) seed : UNSIGNEDLONG );
(*T*)
BEGIN
Seed := CAST(SIGNEDLONG,seed);
END srand;
(*---------------------------------------------------------------------------*)
PROCEDURE fnmatch ((* EIN/ -- *) REF str : ARRAY OF CHAR;
(* EIN/ -- *) REF pat : ARRAY OF CHAR;
(* EIN/ -- *) flags : FNMFlags ): BOOLEAN;
(*T*)
VAR sLen, pLen : CARDINAL;
dot : BOOLEAN;
escape : BOOLEAN;
pathname : BOOLEAN;
PROCEDURE match (sidx : CARDINAL;
pidx : CARDINAL ): BOOLEAN;
(*T*)
VAR inverted : BOOLEAN;
pend : CARDINAL;
cmin : CHAR;
cmax : CHAR;
cs : CHAR;
BEGIN (* match *)
WHILE pidx < pLen DO
IF sidx < sLen THEN
cs := str[sidx];
ELSE
cs := EOS;
END;
CASE pat[pidx] OF
'[': IF (cs = EOS)
OR pathname AND (cs = XDIRSEP)
OR dot AND (cs = '.')
AND ( (sidx = 0)
OR pathname AND (str[sidx-1] = XDIRSEP))
THEN
(* Wenn der String kein Zeichen mehr enthaelt, oder ein
* Pfadtrenner nicht ``gematched'' werden darf, oder ein Dateiname
* mit fuehrendem Punkt nicht ``gematched'' werden darf (entweder
* am Stringanfang oder direkt nach einem Pfadtrenner), schlaegt
* der Vergleich fehl.
*)
RETURN(FALSE);
END;
INC(pidx);
IF (pidx < pLen) AND (pat[pidx] = INVERTCHAR) THEN
inverted := TRUE;
INC(pidx);
ELSE
inverted := FALSE;
END;
pend := pidx;
(* Ein ']' an erster Stelle, evtl. hinter einem '!', beendet nicht
* die Menge, sondern steht fuer das zu ``matchende'' Zeichen,
* hat also keine Spezialbedeutung. Deswegen wird das erste
* Zeichen der Menge uebersprungen.
* Wenn das Escapezeichen erlaubt ist, bedeutet "...\]..."
* nicht das Ende der Menge, sondern steht fuer ein zu
* ``matchendes'' ']'.
*)
REPEAT
INC(pend);
UNTIL (pend >= pLen) OR (pat[pend] = ']')
AND ( NOT escape
OR (pat[pend-1] <> ESCAPECHAR));
IF pend >= pLen THEN
(* Syntaxfehler: Menge nicht korrekt abgeschlossen *)
RETURN(FALSE);
END;
(* Durch das Testen auf korrekten Abschluss mit ']' koennen
* in der nachfolgenden Schleife einige Tests auf zu grosses
* 'pidx' entfallen.
*)
LOOP
IF escape AND (pat[pidx] = ESCAPECHAR) THEN
INC(pidx);
END;
cmin := pat[pidx];
cmax := cmin;
INC(pidx);
IF (pat[pidx] = '-') AND (pidx + 1 < pend) THEN
(* Ein Bereich ist nur vorhanden, falls die Obergrenze
* nicht ']' ist; in diesem Fall steht '-' fuer ein
* Einzelzeichen, und die Klammer beendet die Menge.
*)
INC(pidx);
IF escape AND (pat[pidx] = ESCAPECHAR) THEN
INC(pidx);
END;
cmax := pat[pidx];
INC(pidx);
END;
IF (cmin <= cs) AND (cs <= cmax) THEN
(* --> MATCH *)
IF inverted THEN
RETURN(FALSE);
ELSE
pidx := pend;
EXIT;
END;
ELSIF pidx = pend THEN
(* --> NO MATCH *)
IF inverted THEN
EXIT;
ELSE
RETURN(FALSE);
END;
END; (* IF cmin <= cs ... *)
END; (* LOOP *)
INC(sidx);
INC(pidx);
|'*': REPEAT
INC(pidx);
UNTIL (pidx = pLen) OR (pat[pidx] <> '*');
DEC(pidx);
(* Mehrere '*' hintereinander sind aequivalent zu einem einzelnen.
* Bis zum letzten '*' ueberlesen.
*)
IF pathname AND (cs = XDIRSEP) THEN
(* Wenn '*' auf einen Pfadtrenner trifft, ``matched'' es nur
* die leere Zeichenkette, d.h. der Rest des Musters muss
* ohne '*' auf den augenblicklichen String passen.
*)
INC(pidx);
ELSIF dot AND (cs = '.')
AND ( (sidx = 0)
OR pathname AND (str[sidx-1] = XDIRSEP))
THEN
RETURN(FALSE);
ELSE
(* Das Muster hinter dem '*' wird mit jedem moeglichen Reststring
* verglichen. Das muss rekursiv geschehen, da das Restmuster
* wiederum '*' enthalten kann (und auch jedesmal wieder auf
* '.' und '/' geachtet werden muss).
* Es werden soviele Rekursionsebenen aufgebaut, wie der Reststring
* noch lang ist. Beim rekursiven Aufstieg wird dann der Vergleich
* durchgefuehrt, wobei in jeder Ebene der Reststring mit dem Muster
* hinter dem '*' verglichen wird.
*
* Der ``schlimmste'' Fall, also der mit den meisten rekursiven
* Aufrufen, ist ein Muster folgender Art:
*
* pat = "*?*?*?*?*?..."
*
* und ein String mit mindestens soviel Zeichen, wie das Muster
* '*' hat.
* Die Zahl an Rekursionsaufrufen berechnet sich in diesem Fall aus:
*
* rcalls = 2^stars - 1 + (sLen - stars)
*
* wobei 'stars' die Anzahl der '*' im Muster ist und sich aus
*
* stars = pLen DIV 2
*
* ergibt.
* Der Aufwand ist also exponentiell, falls mehrere '*' im Muster
* vorkommen!
*
* Die ``schlimmste'' Rekursionstiefe ist dagegen nicht ganz so
* wild, sie entspricht der Stringlaenge:
*
* rdepth = sLen
*
* Beispiel: str = "xxxx" (sLen =4), pat = "*?*?*?*?" (stars=4)
*
* Graph der Aufrufe; die Waagerechte kennzeichnet die Rekursions-
* ebene, die Zahlen bedeuten die Anzahl der Aufrufe auf der
* jeweiligen Ebene (haengen von der jeweiligen Laenge des Rest-
* strings ab):
*
* Ebene 0: Aufruf durch 'fnmatch()'
* |
* V
* Ebene 1: -----4--------
* / | \
* / | \
* -1---2---3----
* . / / \
* . / / \
* . ---1--1-----2-
* /
* /
* Ebene 4: ---------1----
*
* insgesamt 15 rekursive Aufrufe.
*
* Die Strings, dargestellt zum Zeitpunkt des rekursiven Aufrufs:
*
* pat = "*?*?*?" "*?*?" "*?*?" "*?*?"
*
* str = "123" ............. "23" .. "3"
* | | |
* "23" .. "3" "3" ""
* | | |
* "3" "" ""
* |
* ""
*
* Falls der String laenger ist, wird die Rekursionsebene erst
* solange linear erhoeht, bis der Reststring genauso lang wie die
* Anzahl der '*', dann spannt sich der Baum genauso auf.
*)
IF (cs <> EOS) AND match(sidx+1, pidx) THEN
RETURN(TRUE);
END;
INC(pidx);
END;
|'?': IF (cs = EOS)
OR pathname AND (cs = XDIRSEP)
OR dot AND (cs = '.')
AND ( (sidx = 0)
OR pathname AND (str[sidx-1] = XDIRSEP))
THEN
RETURN(FALSE);
END;
INC(sidx);
INC(pidx);
|ESCAPECHAR:
IF escape THEN
INC(pidx);
END;
IF pidx = pLen THEN
RETURN(cs = EOS);
ELSIF pat[pidx] <> cs THEN
RETURN(FALSE);
END;
INC(sidx);
INC(pidx);
ELSE
IF pat[pidx] <> cs THEN
RETURN(FALSE);
END;
INC(sidx);
INC(pidx);
END; (* CASE *)
END; (* WHILE *)
(* Wenn das Muster beendet ist, muss auch der String zuende sein.*)
RETURN(sidx = sLen);
END match;
BEGIN (* fnmatch *)
escape := NOT (fnmNOESCAPE IN flags);
pathname := fnmPATHNAME IN flags;
dot := fnmPERIOD IN flags;
sLen := SLEN(str);
pLen := SLEN(pat);
RETURN(match(0, 0));
END fnmatch;
(*===========================================================================*)
BEGIN (* lib *)
Seed := 1;
END lib.