(* Strings.mod implement the ISO Strings specification. Copyright (C) 2008-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . *) IMPLEMENTATION MODULE Strings ; IMPORT ASCII ; FROM libc IMPORT printf ; CONST Debugging = FALSE ; (* Length - Returns the length of stringVal (the same value as would be returned by the pervasive function LENGTH). *) PROCEDURE Length (stringVal: ARRAY OF CHAR) : CARDINAL; BEGIN RETURN( LENGTH(stringVal) ) END Length ; (* The following seven procedures construct a string value, and attempt to assign it to a variable parameter. They all have the property that if the length of the constructed string value exceeds the capacity of the variable parameter, a truncated value is assigned, while if the length of the constructed string value is less than the capacity of the variable parameter, a string terminator is appended before assignment is performed. *) (* Assign - Copies source to destination. *) PROCEDURE Assign (source: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR) ; VAR i, sh, dh: CARDINAL ; BEGIN sh := Length(source) ; dh := HIGH(destination) ; i := 0 ; WHILE (i 0 THEN length := Length (stringVar) ; IF startIndex < length THEN high := HIGH (stringVar) ; (* Calculate the number of characters to delete. *) last := MinCard (high, length-1) ; IF last - startIndex < numberToDelete THEN numberToDelete := last - startIndex + 1 END ; IF numberToDelete > 0 THEN IF Debugging THEN printf ("startIndex = %d, numberToDelete = %d, last = %d\n", startIndex, numberToDelete, last) END ; WHILE startIndex + numberToDelete <= last DO IF Debugging THEN printf ("strVar[%d] is %c\n", startIndex, stringVar[startIndex]) ; printf (" overwriting with strVar[%d] <- %c\n", startIndex + numberToDelete, stringVar[startIndex + numberToDelete]) END ; stringVar[startIndex] := stringVar[startIndex + numberToDelete] ; INC (startIndex) ; END END ; IF startIndex <= high THEN stringVar[startIndex] := ASCII.nul END END END END Delete ; (* Inserts source into destination at position startIndex *) PROCEDURE Insert (source: ARRAY OF CHAR; startIndex: CARDINAL; VAR destination: ARRAY OF CHAR) ; VAR newEnd, endCopy, i, j, sh, dh, dl: CARDINAL ; BEGIN sh := Length(source) ; dh := HIGH(destination) ; dl := Length(destination) ; (* make space for source *) IF Debugging THEN printf("sh = %d dh = %d dl = %d\n", sh, dh, dl); END ; newEnd := dl+sh ; IF newEnd>dh THEN (* insert will truncate destination *) newEnd := dh END ; IF newEnd>sh THEN endCopy := newEnd-sh ELSE endCopy := 0 END ; IF Debugging THEN printf("\ndestination contains\n%s\nnewEnd = %d endCopy = %d\n", destination, newEnd, endCopy) ; printf("newEnd = %d\n", newEnd) ; printf("endCopy = %d\n", endCopy) ; END ; INC(newEnd) ; INC(endCopy) ; WHILE endCopy>startIndex DO DEC(newEnd) ; DEC(endCopy) ; IF Debugging THEN printf("copying dest %d to %d (%c) (startIndex=%d)\n", endCopy, newEnd, destination[newEnd], startIndex) END ; destination[newEnd] := destination[endCopy] END ; IF Debugging THEN printf("destination now contains %s\n", destination) END ; (* copy source into destination *) j := startIndex ; i := 0 ; WHILE (i=numberToExtract) ) END CanExtractAll ; PROCEDURE CanDeleteAll (stringLength, startIndex, numberToDelete: CARDINAL) : BOOLEAN ; (* Returns TRUE if there are numberToDelete characters starting at startIndex and within the stringLength of some string; otherwise returns FALSE. *) BEGIN RETURN( startIndex+numberToDelete<=stringLength ) END CanDeleteAll ; PROCEDURE CanInsertAll (sourceLength, startIndex: CARDINAL; VAR destination: ARRAY OF CHAR) : BOOLEAN ; (* Returns TRUE if there is room for the insertion of sourceLength characters from some string into destination starting at startIndex; otherwise returns FALSE. *) BEGIN RETURN( (HIGH(destination)-Length(destination)=sourceLength ) END CanAppendAll ; PROCEDURE CanConcatAll (source1Length, source2Length: CARDINAL; VAR destination: ARRAY OF CHAR) : BOOLEAN; (* Returns TRUE if there is sufficient room in destination for a two strings of lengths source1Length and source2Length; otherwise returns FALSE. *) BEGIN RETURN( HIGH(destination)-Length(destination)>=source1Length+source2Length ) END CanConcatAll ; (* The following type and procedures provide for the comparison of string values, and for the location of substrings within strings. *) PROCEDURE Compare (stringVal1, stringVal2: ARRAY OF CHAR) : CompareResults ; (* Returns less, equal, or greater, according as stringVal1 is lexically less than, equal to, or greater than stringVal2. *) VAR i, l1, l2: CARDINAL ; BEGIN l1 := Length(stringVal1) ; l2 := Length(stringVal2) ; i := 0 ; WHILE (istringVal2[i] THEN RETURN greater ELSE INC(i) END END ; IF l1l2 THEN RETURN greater ELSE RETURN equal END END Compare ; PROCEDURE Equal (stringVal1, stringVal2: ARRAY OF CHAR) : BOOLEAN ; (* Returns Strings.Compare(stringVal1, stringVal2) = Strings.equal *) VAR h1, h2, i: CARDINAL ; c1, c2 : CHAR ; BEGIN i := 0 ; h1 := HIGH(stringVal1) ; h2 := HIGH(stringVal2) ; IF h1=h2 THEN REPEAT c1 := stringVal1[i] ; c2 := stringVal2[i] ; IF c1#c2 THEN RETURN FALSE END ; IF c1=ASCII.nul THEN RETURN TRUE END ; INC(i) ; UNTIL i>h1 ; RETURN TRUE ELSE c1 := stringVal1[0] ; c2 := stringVal2[0] ; WHILE c1=c2 DO IF c1=ASCII.nul THEN RETURN TRUE END ; INC(i) ; IF i<=h1 THEN c1 := stringVal1[i] ; IF i<=h2 THEN c2 := stringVal2[i] ELSE RETURN c1=ASCII.nul END ELSIF i<=h2 THEN c2 := stringVal2[i] ; RETURN c2=ASCII.nul END END ; RETURN FALSE END END Equal ; PROCEDURE FindNext (pattern, stringToSearch: ARRAY OF CHAR; startIndex: CARDINAL; VAR patternFound: BOOLEAN; VAR posOfPattern: CARDINAL) ; (* Looks forward for next occurrence of pattern in stringToSearch, starting the search at position startIndex. If startIndex < LENGTH(stringToSearch) and pattern is found, patternFound is returned as TRUE, and posOfPattern contains the start position in stringToSearch of pattern. Otherwise patternFound is returned as FALSE, and posOfPattern is unchanged. *) VAR i, j, hp, hs: CARDINAL ; BEGIN i := startIndex ; hp := Length(pattern) ; hs := Length(stringToSearch) ; IF hp<=hs THEN WHILE (i<=hs-hp) DO j := 0 ; WHILE (j0 DO DEC(i) ; j := 0 ; WHILE (j