IMPLEMENTATION MODULE strUtils; IMPORT InOut, Strings,MemUtils, SYSTEM ; CONST CR = 0DH; LF = 0AH; CONST less = -1; equal = 0; greater = 1; PROCEDURE Compare (stringVal1, stringVal2: ARRAY OF CHAR): INTEGER ; VAR index : CARDINAL ; L,L1,L2 : CARDINAL; (* length of the arrays *) BEGIN L1 := Strings.Length(stringVal1); L2 := Strings.Length(stringVal2); index := 0; (*checking the # lengths of the strings and setting L to the shortest one *) IF L1 stringVal2[index]) THEN RETURN greater ELSE RETURN equal END END Compare; PROCEDURE Copy(VAR Ns: ARRAY OF CHAR; S: ARRAY OF CHAR); (* MemCopy - copys a region of memory to the required destination. PROCEDURE MemCopy (from: ADDRESS; length: CARDINAL; to: ADDRESS) ; *) VAR H,L : CARDINAL; BEGIN H := HIGH(Ns)+1; L := Strings.Length(S); IF L > H THEN L := H END; MemUtils.MemCopy(SYSTEM.ADR(S),L,SYSTEM.ADR(Ns)); IF L < H THEN Ns[L] := 0C; END; END Copy; PROCEDURE Assign (source: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR); (* several cases : - the strings are equal length - the destination is shorter - the destination is longer *) VAR index: CARDINAL ; L1,L2 : CARDINAL; BEGIN L1 := Strings.Length(source); L2 := HIGH(destination); index := 0; IF L2 > L1 THEN (* destination longer than source : need to put a 0C ate the end *) FOR index := 0 TO L1 DO destination[index] := source[index]; END; source[index+1] := 0C; ELSIF L2 < L1 THEN (*we only copy what is possible *) FOR index := 0 TO L2 DO destination[index] := source[index]; END; ELSE (* equal length *) FOR index := 0 TO L1 DO destination[index] := source[index]; END; END; END Assign; PROCEDURE Pos (substr : CHAR ; s : ARRAY OF CHAR; n : CARDINAL ) : CARDINAL ; (* find th first occurence of a CHAR in the String, search beginning at the n.th Character *) VAR i : CARDINAL ; l : CARDINAL; BEGIN l := Strings.Length(s); FOR i := n + 1 TO l -1 DO IF s[i] = substr THEN RETURN(i) END; END; RETURN MAX(CARDINAL); END Pos; (* python like functions *) PROCEDURE isalnum(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are alphanumeric*) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l -1 DO IF Pos(s[i], alphanum,0) = MAX(CARDINAL) THEN result := FALSE; END; END; RETURN result END isalnum; PROCEDURE isalpha(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are in the alphabet *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l -1 DO IF Pos(s[i], asciiLetters,0) = MAX(CARDINAL) THEN result := FALSE; END; END; RETURN result END isalpha; PROCEDURE isascii(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are ascii characters *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l -1 DO IF ORD(s[i]) > 127 THEN result := FALSE; END; END; RETURN result END isascii; PROCEDURE isdigit(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are digits *) VAR i,l : CARDINAL; value : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l DO value := ORD(s[i]); IF (value < 48) OR (value > 58) THEN result := FALSE; END; END; RETURN result END isdigit; PROCEDURE isidentifier(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if the string is an identifier *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); i := 1; IF Pos(s[i], asciiLetters,0) = MAX(CARDINAL) THEN result := FALSE; ELSE FOR i := 1 TO l DO IF Pos(s[i], alphanum,0) = MAX(CARDINAL) THEN result := FALSE; END; END; END; RETURN result END isidentifier; PROCEDURE islower(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are lower case *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l DO IF Pos(s[i], asciiLowercase,0) = MAX(CARDINAL) THEN result := FALSE; END; END; RETURN result END islower; PROCEDURE isprintable(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are printable *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l DO IF Pos(s[i], printable,0) = MAX(CARDINAL) THEN result := FALSE; END; END; RETURN result END isprintable; PROCEDURE isspace(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are whitespaces *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s); FOR i := 0 TO l DO IF Pos(s[i], whitespace,0) = MAX(CARDINAL) THEN result := FALSE; END; END; RETURN result END isspace; PROCEDURE istitle(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if the string follows the rules of a title *) (* Check if each word start with an upper case letter:*) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; (* we need to separate the string into words and test the first char of every word *) (* would be nice to have an equivalent to Python "split" *) l := Strings.Length(s) -1; WHILE i <= l DO DEC(i); END; RETURN result END istitle; PROCEDURE isupper(s : ARRAY OF CHAR) : BOOLEAN; (* Returns True if all characters in the string are upper case *) VAR i : CARDINAL; l : CARDINAL; result : BOOLEAN; BEGIN result := TRUE; l := Strings.Length(s) -1 ; FOR i := 0 TO l DO IF( ORD(s[i]) < 65 ) OR (ORD(s[i]) > 90 ) THEN result := FALSE; END; END; RETURN result END isupper; (* end of python like functions *) PROCEDURE RemoveLeftChars (VAR s : ARRAY OF CHAR; ch : CHAR); BEGIN REPEAT IF s[0] = ch THEN Strings.Delete (s, 0, 1) END; UNTIL s[0] # ch; END RemoveLeftChars; PROCEDURE RemoveRightChars (VAR s : ARRAY OF CHAR; ch : CHAR); VAR l : INTEGER; BEGIN l := Strings.Length(s); REPEAT IF s[l] = ch THEN Strings.Delete (s, l, 1) END; DEC(l); UNTIL s[l] # ch; END RemoveRightChars; PROCEDURE WordCount (source : ARRAY OF CHAR; separator : CHAR) : CARDINAL ; (* count the number of words separated by the separator*) VAR k,l : CARDINAL ; BEGIN (* remove the separator from the beginning and from the end : ltrim and rtrim *) RemoveLeftChars(source, separator); RemoveRightChars (source, separator); k := 0; l := 0; REPEAT IF source[l] = separator THEN INC(k); REPEAT INC(l); UNTIL source[l] # separator; END; INC(l); UNTIL l = Strings.Length(source); INC(k); RETURN(k); END WordCount; PROCEDURE NumberOfChar (s : ARRAY OF CHAR; ch : CHAR) : CARDINAL ; (* counts the number of a given char ch in string s*) VAR n, i : CARDINAL ; BEGIN n := 0; FOR i := 0 TO Strings.Length(s) DO IF s[i] = ch THEN INC(n) END; END; RETURN(n); END NumberOfChar; PROCEDURE Rpos ( s : ARRAY OF CHAR ; ch : CHAR) : CARDINAL ; (* display the position OF e caracter ch IN a STRING s beginning FROM the right *) VAR l : CARDINAL; BEGIN l := Strings.Length (s); REPEAT DEC(l) UNTIL (s[l] = ch) OR (l = 0); RETURN l; END Rpos; PROCEDURE Lpos ( s : ARRAY OF CHAR ; ch : CHAR) : CARDINAL; (* display the position OF e caracter ch IN a STRING s beginning FROM the left *) VAR l : CARDINAL ; ll : CARDINAL; (* length *) BEGIN l := 0; ll := Strings.Length(s); REPEAT INC(l) UNTIL (s[l] = ch) OR (l = ll); RETURN l; END Lpos; PROCEDURE copyBytes(VAR src, dst: ARRAY OF CHAR; start, quantity: CARDINAL); (* copy quantity of char from source to destination, beginning at start *) VAR i, j : CARDINAL; L1 : CARDINAL; (*LENGTH OF the source *) L2 : CARDINAL; (* length of the destination*) BEGIN (* the lengths *) L1 := Strings.Length(src); L2 := Strings.Length(dst); (* iteration ; initialisation de i au premier caractere à copier, et j au début de la chaine destination*) i := start; j := 0; IF i < (L2 - 1) THEN REPEAT dst[j] := src[i]; INC(i); INC(j); UNTIL (i = L1) OR (j = L2) OR (j = quantity); IF i < L2 THEN dst[i+1] := 0C; END; END END copyBytes; (* PROCEDURE appendNumChars(VAR extra: pstring; extraNum: LONGINT; VAR destination: pstring; destinationNum: LONGINT); VAR tmp: pstring; BEGIN IF extra # NIL THEN IF destination = NIL THEN NEW(destination, extraNum); copyBytes(extra^, destination^, 0, extraNum); ELSE NEW(tmp, extraNum + destinationNum); copyBytes(destination^, tmp^, 0, destinationNum); copyBytes(extra^, tmp^, destinationNum, extraNum); NEW(destination, LEN(tmp^)); copyBytes(tmp^, destination^, 0, LEN(tmp^)); END; END END appendNumChars; PROCEDURE string2pstring(s: ARRAY OF CHAR; VAR d : pstring); BEGIN NEW(d, Strings.Length(s)+1); Assign (s, d^); END string2pstring; PROCEDURE string2pstrings(VAR text: ARRAY OF CHAR): pstrings; VAR i, j, lineNum, start, number: INTEGER; pstrs: pstrings; pstr: pstring; BEGIN i := 0; j := 0; REPEAT IF text[i] = 0AH THEN INC(j) END; INC(i); UNTIL (i = LEN(text)) OR (text[i] = 0H); NEW(pstrs, j); lineNum := 0; number := 0; REPEAT WHILE (text[number] = 0AH) OR (text[number] = 0DH) DO INC(number) END; start := number; REPEAT INC(number) UNTIL (number = LEN(text) - 1) OR (text[number] = 0AH) OR (text[number] = 0DH) OR (text[number] = 0H); NEW(pstr, number - start + 1); Strings.Extract(text, start, number - start, pstr^); pstrs^[lineNum] := pstr; INC(lineNum); UNTIL (lineNum = j) OR (number = i); RETURN pstrs END string2pstrings; PROCEDURE ExtractWord (n : INTEGER; s : ARRAY OF CHAR; ch : CHAR) : pstring; VAR i, j, k, l : INTEGER; str : string; pstr : pstring; BEGIN RemoveLeftChars (s, ch); RemoveRightChars (s, ch); IF n = WordCount (s, ch) THEN l := Rpos (s, ch); Strings.Extract (s, l+1, Strings.Length(s)-l-1, str); string2pstring(str, pstr); RETURN (pstr); ELSIF n = 1 THEN l := Lpos(s, ch); Strings.Extract (s, 0, l, str); string2pstring(str, pstr); RETURN (pstr); ELSE j := 0; k := 0; l := 0; i := 0; REPEAT IF s[i] = ch THEN INC(j) ; IF j = (n-1) THEN k := i END; IF j = n THEN l := i END; END; INC(i); UNTIL i = Strings.Length(s); Strings.Extract(s, k+1, l-k-1, str); string2pstring(str, pstr); RETURN (pstr) END END ExtractWord; PROCEDURE tokenize(s : ARRAY OF CHAR; ch : CHAR) : pstrings; VAR pstr : pstring; pstrs : pstrings; l, n : INTEGER; BEGIN n := WordCount(s, ch); IF n # 0 THEN NEW (pstrs, n) ELSE pstrs := NIL; RETURN pstrs END; IF n = 1 THEN RemoveRightChars (s, ch); RemoveLeftChars (s, ch); string2pstring(s, pstrs^[0]); ELSE l := 0; REPEAT pstr := ExtractWord ( l+1, s, ch); pstrs^[l] := pstr; INC(l); UNTIL l = n; END; RETURN pstrs END tokenize; *) PROCEDURE copyAll (src : ARRAY OF CHAR ; VAR dst : ARRAY OF CHAR); (* copies all array, even after 0X, but puts 0X in the end *) VAR i, k : CARDINAL ; L1, L2 : CARDINAL; BEGIN L1 := HIGH(src); L2 := HIGH(dst); InOut.WriteCard(L1,5); InOut.WriteCard(L2,5); InOut.WriteLn; (* 3 cases : - the arrays are the same size - the source is bigger than the destination - the destination is bigger than the source *) IF L1 = L2 THEN FOR i := 0 TO L1 DO (* the null if present in the source is copied as it is in the destination *) dst[i] := src[i]; END ELSIF L1< L2 THEN (* here we need to terminate the des with the 0C char *) InOut.WriteString("source plus petite que destination"); InOut.WriteLn; FOR i := 0 TO L1 DO dst[i] := src[i] END; dst[i+1] := 0C; ELSE (* here we only copy what is possible in the destination : no need for a 0C terminal *) InOut.WriteString("source plus grande que destination"); InOut.WriteLn; FOR i := 0 TO L2 DO dst[i] := src[i] END END; END copyAll; PROCEDURE zeroStr(VAR str: ARRAY OF CHAR); VAR i, j : CARDINAL ; BEGIN i := Strings.Length(str); FOR j := 0 TO i-1 DO str[j] := 0C; END; (* j := 0; REPEAT str[j] := 0H; INC(j) UNTIL j = i;*) END zeroStr; PROCEDURE appendLFCR(VAR str: ARRAY OF CHAR); (* what happens if the array is too small ???? *) VAR l : INTEGER; BEGIN l := Strings.Length(str); IF l <= (l - 3) THEN str[l] := CHR(LF); str[l+1] := CHR(CR); str[l+2] := 0C; END; END appendLFCR; (* PROCEDURE findChar(ch: CHAR; VAR line: ARRAY OF CHAR; VAR b: BOOLEAN; VAR pos: INTEGER); VAR i : INTEGER; BEGIN i := -1; pos := -1; b := FALSE; REPEAT INC(i); IF line[i] = ch THEN b := TRUE; pos := i END; UNTIL b OR (i = LEN(line) - 1); END findChar; PROCEDURE cutLine(VAR src, dst: ARRAY OF CHAR); VAR found: BOOLEAN; pos : INTEGER; i : INTEGER; BEGIN COPY("", dst); findChar(LF, src, found, pos); IF found THEN i := 0; REPEAT dst[i] := src[i]; INC(i); UNTIL (i = pos) OR (i = LEN(dst)-2); dst[i] := src[i]; dst[i+1] := 0H END; END cutLine; PROCEDURE terminateLine(VAR str: ARRAY OF CHAR); VAR found: BOOLEAN; pos : INTEGER; BEGIN findChar(LF, str, found, pos); IF found THEN IF (pos + 1) < LEN(str) THEN str[pos + 1] := 0H END END; END terminateLine; PROCEDURE getTillEOL(VAR src: ARRAY OF CHAR; spos: INTEGER; VAR dst: ARRAY OF CHAR); VAR i, j: INTEGER; l, k: LONGINT; BEGIN l := Strings.Length(src); k := Strings.Length(dst); zeroStr(dst); i := 0; j := spos+1; REPEAT dst[i] := src[i+j]; INC(i); UNTIL (i+j >= l) OR (src[i+j] < ' ') OR (i+j >= LEN(src)) OR (j >= LEN(dst)); END getTillEOL; PROCEDURE getNextWord(VAR src: ARRAY OF CHAR; spos: INTEGER; VAR dst: ARRAY OF CHAR); VAR i, j: INTEGER; BEGIN zeroStr(dst); i := 0; j := spos; REPEAT dst[i] := src[i+j]; INC(i); UNTIL (i+j = Strings.Length(src)) OR (src[i+j] <= ' '); END getNextWord; PROCEDURE getNextAlphaNumWord(VAR src: ARRAY OF CHAR; spos: INTEGER; VAR dst: ARRAY OF CHAR); VAR i, j: INTEGER; notAN: BOOLEAN; o: INTEGER; BEGIN zeroStr(dst); i := 0; j := spos; notAN := FALSE; REPEAT dst[i] := src[i+j]; INC(i); o := ORD(src[i+j]); IF ~ ( ((o >= 48) & (o <=57)) OR ((o >= 65) & (o <= 90)) OR ( (o >= 97) & (o <= 122) ) ) THEN notAN := TRUE END; UNTIL (i+j = Strings.Length(src))OR notAN OR (src[i+j] <= ' '); END getNextAlphaNumWord; PROCEDURE contains (VAR line : ARRAY OF CHAR; pattern: ARRAY OF CHAR): BOOLEAN; END contains; PROCEDURE contains1(VAR line: ARRAY OF CHAR; pat : ARRAY OF CHAR): BOOLEAN; VAR found: BOOLEAN; pos : INTEGER; BEGIN Strings.FindNext(pat, line, 0, found, pos); IF found THEN RETURN TRUE ELSE RETURN FALSE END END contains1; PROCEDURE Reverse0 (VAR str : ARRAY OF CHAR; start, end : INTEGER); VAR h : CHAR; BEGIN WHILE start < end DO h := str[start]; str[start] := str[end]; str[end] := h; INC(start); DEC(end) END END Reverse0; PROCEDURE dumpChars(VAR s : ARRAY OF CHAR); VAR i : INTEGER; l : LONGINT; BEGIN i := 0; l := Strings.Length(s); WHILE i < l DO Out.Int(i, 0); Out.Char(" "); Out.Char(s[i]); Out.Ln; INC(i) END END dumpChars; PROCEDURE dumpAllChars(VAR s : ARRAY OF CHAR); VAR i : INTEGER; l : LONGINT; BEGIN i := 0; l := LEN(s); WHILE i < l-1 DO Out.Int(i, 0); Out.Char(" "); Out.Char(s[i]); Out.Ln; INC(i) END END dumpAllChars; *) END strUtils.