1 (* DynamicStrings.mod provides a dynamic string type and procedures.
3 Copyright (C) 2001-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE DynamicStrings
;
29 FROM libc
IMPORT strlen
, strncpy
, write
, exit
, snprintf
;
30 FROM StrLib
IMPORT StrLen
;
31 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
32 FROM Assertion
IMPORT Assert
;
33 FROM SYSTEM
IMPORT ADR
;
34 FROM ASCII
IMPORT nul
, tab
, lf
;
35 FROM M2RTS
IMPORT Halt
;
39 PoisonOn
= FALSE ; (* to enable debugging of this module, turn on PoisonOn and DebugOn. *)
41 CheckOn
= FALSE ; (* to enable debugging of users of this module turn on *)
42 TraceOn
= FALSE ; (* CheckOn and TraceOn. Enabling both of these is very expensive. *)
46 buf
: ARRAY [0..MaxBuf
-1] OF CHAR ;
51 Descriptor
= POINTER TO descriptor
;
53 String
= POINTER TO stringRecord
;
56 next
: String
; (* a mechanism for tracking used/lost strings *)
68 desState
= (inuse
, marked
, onlist
, poisoned
) ;
71 charStarUsed
: BOOLEAN ; (* can we garbage collect this? *)
73 charStarSize
: CARDINAL ;
74 charStarValid
: BOOLEAN ;
76 garbage
: String
; (* temporary strings to be destroyed
77 once this string is killed *)
80 frame
= POINTER TO frameRec
;
82 alloc
, dealloc
: String
;
87 Initialized
: BOOLEAN ;
89 captured
: String
; (* debugging aid. *)
92 (* writeStringDesc write out debugging information about string, s. *)
94 PROCEDURE writeStringDesc (s
: String
) ;
96 writeCstring (s^.debug.file
) ; writeString (':') ;
97 writeCard (s^.debug.line
) ; writeString (':') ;
98 writeCstring (s^.debug.proc
) ; writeString (' ') ;
101 CASE s^.head^.state
OF
103 inuse
: writeString ("still in use (") ; writeCard (s^.contents.len
) ; writeString (") characters") |
104 marked
: writeString ("marked") |
105 onlist
: writeString ("on a (lost) garbage list") |
106 poisoned
: writeString ("poisoned")
109 writeString ("unknown state")
111 END writeStringDesc
;
118 PROCEDURE writeNspace (n
: CARDINAL) ;
131 PROCEDURE DumpStringInfo (s
: String
; i
: CARDINAL) ;
135 writeNspace (i
) ; writeStringDesc (s
) ; writeLn
;
136 IF s^.head^.garbage #
NIL
138 writeNspace (i
) ; writeString ('garbage list:') ; writeLn
;
140 s
:= s^.head^.garbage
;
141 DumpStringInfo (s
, i
+1) ; writeLn
153 PopAllocationExemption - test to see that all strings are deallocated, except
154 string e since the last push.
155 Post-condition: it pops to the previous allocation/deallocation
158 If halt is true then the application terminates
159 with an exit code of 1.
162 PROCEDURE PopAllocationExemption (halt
: BOOLEAN; e
: String
) : String
;
173 Halt ("mismatched number of PopAllocation's compared to PushAllocation's",
174 __FILE__
, __FUNCTION__
, __LINE__
) ;
175 (* writeString ("mismatched number of PopAllocation's compared to PushAllocation's") *)
177 IF frameHead^.alloc #
NIL
180 s
:= frameHead^.alloc
;
182 IF NOT ((e
= s
) OR IsOnGarbage (e
, s
) OR IsOnGarbage (s
, e
))
186 writeString ("the following strings have been lost") ; writeLn
;
189 DumpStringInfo (s
, 0)
198 frameHead
:= frameHead^.next
202 END PopAllocationExemption
;
206 PopAllocation - test to see that all strings are deallocated since
207 the last push. Then it pops to the previous
208 allocation/deallocation lists.
210 If halt is true then the application terminates
211 with an exit code of 1.
214 PROCEDURE PopAllocation (halt
: BOOLEAN) ;
218 IF PopAllocationExemption (halt
, NIL) = NIL
226 PushAllocation - pushes the current allocation/deallocation lists.
229 PROCEDURE PushAllocation
;
251 PROCEDURE doDSdbEnter
;
264 PROCEDURE doDSdbExit (s
: String
) ;
268 s
:= PopAllocationExemption (TRUE, s
)
277 PROCEDURE DSdbEnter
;
286 PROCEDURE DSdbExit (s
: String
) ;
292 * #undef GM2_DEBUG_DYNAMICSTINGS
293 * #if defined(GM2_DEBUG_DYNAMICSTINGS)
294 * # define DSdbEnter doDSdbEnter
295 * # define DSdbExit doDSdbExit
296 * # define CheckOn TRUE
297 * # define TraceOn TRUE
302 PROCEDURE Capture (s
: String
) : CARDINAL ;
313 PROCEDURE Min (a
, b
: CARDINAL) : CARDINAL ;
328 PROCEDURE Max (a
, b
: CARDINAL) : CARDINAL ;
340 writeString - writes a string to stdout.
343 PROCEDURE writeString (a
: ARRAY OF CHAR) ;
347 i
:= write (1, ADR (a
), StrLen (a
))
352 writeCstring - writes a C string to stdout.
355 PROCEDURE writeCstring (a
: ADDRESS
) ;
361 writeString ('(null)')
363 i
:= write (1, a
, strlen (a
))
372 PROCEDURE writeCard (c
: CARDINAL) ;
379 writeCard (c
DIV 10) ;
382 ch
:= CHR (ORD ('0') + c
) ;
383 i
:= write (1, ADR (ch
), 1)
392 PROCEDURE writeLongcard (l
: LONGCARD) ;
399 writeLongcard (l
DIV 16) ;
400 writeLongcard (l
MOD 16)
403 ch
:= CHR (ORD ('0') + VAL (CARDINAL, l
)) ;
404 i
:= write(1, ADR(ch
), 1)
407 ch
:= CHR (ORD ('a') + VAL(CARDINAL, l
) - 10) ;
408 i
:= write (1, ADR (ch
), 1)
414 writeAddress - writes out the address of a with a C style hex prefix.
417 PROCEDURE writeAddress (a
: ADDRESS
) ;
419 buffer
: ARRAY [0.
.30] OF CHAR ;
421 snprintf (ADR (buffer
), SIZE (buffer
), "0x%", a
) ;
422 writeString (buffer
) ;
427 writeLn - writes a newline.
436 i
:= write (1, ADR (ch
), 1)
441 AssignDebug - assigns, file, and, line, information to string, s.
444 PROCEDURE AssignDebug (s
: String
; file
: ARRAY OF CHAR; line
: CARDINAL; proc
: ARRAY OF CHAR) : String
;
451 ALLOCATE (debug.file
, StrLen (file
) + 1) ;
452 IF strncpy(debug.file
, f
, StrLen(file
)+1)=NIL
456 ALLOCATE (debug.proc
, StrLen (proc
) + 1) ;
457 IF strncpy (debug.proc
, p
, StrLen (proc
) + 1) = NIL
466 CopyOut - copies string, s, to a.
469 PROCEDURE CopyOut (VAR a
: ARRAY OF CHAR; s
: String
) ;
473 l
:= Min (HIGH (a
) + 1, Length (s
)) ;
476 a
[i
] := char (s
, i
) ;
487 IsOn - returns TRUE if, s, is on one of the debug lists.
490 PROCEDURE IsOn (list
, s
: String
) : BOOLEAN ;
492 WHILE (list # s
) AND (list #
NIL) DO
493 list
:= list^.debug.next
500 AddTo - adds string, s, to, list.
503 PROCEDURE AddTo (VAR list
: String
; s
: String
) ;
510 s^.debug.next
:= list
;
517 SubFrom - removes string, s, from, list.
520 PROCEDURE SubFrom (VAR list
: String
; s
: String
) ;
526 list
:= s^.debug.next
;
529 WHILE (p^.debug.next #
NIL) AND (p^.debug.next # s
) DO
534 p^.debug.next
:= s^.debug.next
536 (* not found, quit *)
545 AddAllocated - adds string, s, to the head of the allocated list.
548 PROCEDURE AddAllocated (s
: String
) ;
551 AddTo (frameHead^.alloc
, s
)
556 AddDeallocated - adds string, s, to the head of the deallocated list.
559 PROCEDURE AddDeallocated (s
: String
) ;
562 AddTo (frameHead^.dealloc
, s
)
567 IsOnAllocated - returns TRUE if the string, s, has ever been allocated.
570 PROCEDURE IsOnAllocated (s
: String
) : BOOLEAN ;
577 IF IsOn (f^.alloc
, s
)
589 IsOnDeallocated - returns TRUE if the string, s, has ever been deallocated.
592 PROCEDURE IsOnDeallocated (s
: String
) : BOOLEAN ;
599 IF IsOn (f^.dealloc
, s
)
607 END IsOnDeallocated
;
611 SubAllocated - removes string, s, from the list of allocated strings.
614 PROCEDURE SubAllocated (s
: String
) ;
621 IF IsOn (f^.alloc
, s
)
623 SubFrom (f^.alloc
, s
) ;
633 SubDeallocated - removes string, s, from the list of deallocated strings.
636 PROCEDURE SubDeallocated (s
: String
) ;
643 IF IsOn (f^.dealloc
, s
)
645 SubFrom (f^.dealloc
, s
) ;
655 SubDebugInfo - removes string, s, from the list of allocated strings.
658 PROCEDURE SubDebugInfo (s
: String
) ;
660 IF IsOnDeallocated (s
)
662 Assert (NOT DebugOn
) ;
663 (* string has already been deallocated *)
672 (* string has not been allocated *)
678 AddDebugInfo - adds string, s, to the list of allocated strings.
681 PROCEDURE AddDebugInfo (s
: String
) ;
697 ConcatContents - add the contents of string, a, where, h, is the
698 total length of, a. The offset is in, o.
701 PROCEDURE ConcatContents (VAR c
: Contents
; a
: ARRAY OF CHAR; h
, o
: CARDINAL) ;
706 WHILE (o
< h
) AND (i
< MaxBuf
) DO
718 contents.next
:= NIL ;
719 ConcatContents (contents
, a
, h
, o
)
721 AddDebugInfo (c.next
) ;
722 c.next
:= AssignDebug (c.next
, __FILE__
, __LINE__
, __FUNCTION__
)
730 InitString - creates and returns a String type object.
731 Initial contents are, a.
734 PROCEDURE InitString (a
: ARRAY OF CHAR) : String
;
744 ConcatContents (contents
, a
, StrLen (a
), 0) ;
747 charStarUsed
:= FALSE ;
750 charStarValid
:= FALSE ;
758 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
765 DeallocateCharStar - deallocates any charStar.
768 PROCEDURE DeallocateCharStar (s
: String
) ;
770 IF (s #
NIL) AND (s^.head #
NIL)
773 IF charStarUsed
AND (charStar #
NIL)
775 DEALLOCATE (charStar
, charStarSize
)
777 charStarUsed
:= FALSE ;
780 charStarValid
:= FALSE
783 END DeallocateCharStar
;
787 CheckPoisoned - checks for a poisoned string, s.
790 PROCEDURE CheckPoisoned (s
: String
) : String
;
792 IF PoisonOn
AND (s #
NIL) AND (s^.head #
NIL) AND (s^.head^.state
= poisoned
)
801 KillString - frees String, s, and its contents.
805 PROCEDURE KillString (s
: String
) : String
;
811 s
:= CheckPoisoned (s
)
820 ELSIF IsOnDeallocated (s
)
830 garbage
:= KillString (garbage
) ;
833 DeallocateCharStar (s
)
842 t
:= KillString (s^.contents.next
) ;
854 Fin - finishes with a string, it calls KillString with, s.
855 The purpose of the procedure is to provide a short cut
856 to calling KillString and then testing the return result.
859 PROCEDURE Fin (s
: String
) ;
861 IF KillString (s
) #
NIL
869 MarkInvalid - marks the char * version of String, s, as invalid.
872 PROCEDURE MarkInvalid (s
: String
) ;
876 s
:= CheckPoisoned (s
)
880 s^.head^.charStarValid
:= FALSE
886 ConcatContentsAddress - concatenate the string, a, where, h, is the
890 PROCEDURE ConcatContentsAddress (VAR c
: Contents
; a
: ADDRESS
; h
: CARDINAL) ;
892 p
: POINTER TO CHAR ;
898 WHILE (j
< h
) AND (i
< MaxBuf
) DO
911 contents.next
:= NIL ;
912 ConcatContentsAddress (contents
, p
, h
- j
)
914 AddDebugInfo (c.next
) ;
917 c.next
:= AssignDebug (c.next
, __FILE__
, __LINE__
, __FUNCTION__
)
923 END ConcatContentsAddress
;
927 InitStringCharStar - initializes and returns a String to contain the C string.
930 PROCEDURE InitStringCharStar (a
: ADDRESS
) : String
;
942 ConcatContentsAddress (contents
, a
, strlen (a
))
946 charStarUsed
:= FALSE ;
949 charStarValid
:= FALSE ;
957 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
960 END InitStringCharStar
;
964 InitStringChar - initializes and returns a String to contain the single character, ch.
967 PROCEDURE InitStringChar (ch
: CHAR) : String
;
969 a
: ARRAY [0.
.1] OF CHAR ;
974 s
:= InitString (a
) ;
977 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
984 Mark - marks String, s, ready for garbage collection.
987 PROCEDURE Mark (s
: String
) : String
;
991 s
:= CheckPoisoned (s
)
993 IF (s #
NIL) AND (s^.head^.state
= inuse
)
995 s^.head^.state
:= marked
1002 AddToGarbage - adds String, b, onto the garbage list of, a. Providing
1003 the state of b is marked. The state is then altered to
1004 onlist. String, a, is returned.
1007 PROCEDURE AddToGarbage (a
, b
: String
) : String
;
1013 a
:= CheckPoisoned (a
) ;
1014 b
:= CheckPoisoned (b
)
1017 IF (a#NIL) AND (a#b) AND (a^.head^.state=marked)
1019 writeString('warning trying to add to a marked string') ; writeLn
1022 IF (a # b
) AND (a #
NIL) AND (b #
NIL) AND (b^.head^.state
= marked
) AND (a^.head^.state
= inuse
)
1025 WHILE c^.head^.garbage #
NIL DO
1026 c
:= c^.head^.garbage
1028 c^.head^.garbage
:= b
;
1029 b^.head^.state
:= onlist
;
1040 IsOnGarbage - returns TRUE if, s, is on string, e, garbage list.
1043 PROCEDURE IsOnGarbage (e
, s
: String
) : BOOLEAN ;
1045 IF (e #
NIL) AND (s #
NIL)
1047 WHILE e^.head^.garbage #
NIL DO
1048 IF e^.head^.garbage
= s
1052 e
:= e^.head^.garbage
1061 Length - returns the length of the String, s.
1064 PROCEDURE Length (s
: String
) : CARDINAL ;
1070 RETURN s^.contents.len
+ Length (s^.contents.next
)
1076 ConCat - returns String, a, after the contents of, b, have been appended.
1079 PROCEDURE ConCat (a
, b
: String
) : String
;
1085 a
:= CheckPoisoned (a
) ;
1086 b
:= CheckPoisoned (b
)
1090 RETURN ConCat (a
, Mark (Dup (b
)))
1093 a
:= AddToGarbage (a
, b
) ;
1097 WHILE (t^.contents.len
= MaxBuf
) AND (t^.contents.next #
NIL) DO
1098 t
:= t^.contents.next
1100 ConcatContents (t^.contents
, b^.contents.buf
, b^.contents.len
, 0) ;
1101 b
:= b^.contents.next
1104 IF (a
= NIL) AND (b #
NIL)
1113 ConCatChar - returns String, a, after character, ch, has been appended.
1116 PROCEDURE ConCatChar (a
: String
; ch
: CHAR) : String
;
1118 b
: ARRAY [0.
.1] OF CHAR ;
1123 a
:= CheckPoisoned (a
)
1129 WHILE (t^.contents.len
= MaxBuf
) AND (t^.contents.next #
NIL) DO
1130 t
:= t^.contents.next
1132 ConcatContents (t^.contents
, b
, 1, 0) ;
1138 ReplaceChar - returns string s after it has changed all occurances of from to to.
1141 PROCEDURE ReplaceChar (s
: String
; from
, to
: CHAR) : String
;
1149 WHILE i
< t^.contents.len
DO
1150 IF t^.contents.buf
[i
] = from
1152 t^.contents.buf
[i
] := to
1156 t
:= t^.contents.next
1163 Assign - assigns the contents of, b, into, a.
1164 String, a, is returned.
1167 PROCEDURE Assign (a
, b
: String
) : String
;
1171 a
:= CheckPoisoned (a
) ;
1172 b
:= CheckPoisoned (b
)
1174 IF (a #
NIL) AND (b #
NIL)
1177 contents.next
:= KillString (contents.next
) ;
1181 RETURN ConCat (a
, b
)
1186 Dup - duplicate a String, s, returning the copy of s.
1189 PROCEDURE Dup (s
: String
) : String
;
1193 s
:= CheckPoisoned (s
)
1195 s
:= Assign (InitString (''), s
) ;
1198 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
1205 Add - returns a new String which contains the contents of a and b.
1208 PROCEDURE Add (a
, b
: String
) : String
;
1212 a
:= CheckPoisoned (a
) ;
1213 b
:= CheckPoisoned (b
)
1215 a
:= ConCat (ConCat (InitString (''), a
), b
) ;
1218 a
:= AssignDebug (a
, __FILE__
, __LINE__
, __FUNCTION__
)
1225 Equal - returns TRUE if String, a, and, b, are equal.
1228 PROCEDURE Equal (a
, b
: String
) : BOOLEAN ;
1234 a
:= CheckPoisoned (a
) ;
1235 b
:= CheckPoisoned (b
)
1237 IF Length (a
) = Length (b
)
1239 WHILE (a #
NIL) AND (b #
NIL) DO
1241 Assert (a^.contents.len
= b^.contents.len
) ;
1242 WHILE i
<a^.contents.len
DO
1243 IF a^.contents.buf
[i
] # b^.contents.buf
[i
]
1249 a
:= a^.contents.next
;
1250 b
:= b^.contents.next
1260 EqualCharStar - returns TRUE if contents of String, s, is the same as the
1264 PROCEDURE EqualCharStar (s
: String
; a
: ADDRESS
) : BOOLEAN ;
1270 s
:= CheckPoisoned (s
)
1272 t
:= InitStringCharStar (a
) ;
1275 t
:= AssignDebug (t
, __FILE__
, __LINE__
, __FUNCTION__
)
1277 t
:= AddToGarbage (t
, s
) ;
1280 t
:= KillString (t
) ;
1283 t
:= KillString (t
) ;
1290 EqualArray - returns TRUE if contents of String, s, is the same as the
1294 PROCEDURE EqualArray (s
: String
; a
: ARRAY OF CHAR) : BOOLEAN ;
1300 s
:= CheckPoisoned (s
)
1302 t
:= InitString (a
) ;
1305 t
:= AssignDebug (t
, __FILE__
, __LINE__
, __FUNCTION__
)
1307 t
:= AddToGarbage (t
, s
) ;
1310 t
:= KillString (t
) ;
1313 t
:= KillString (t
) ;
1320 Mult - returns a new string which is n concatenations of String, s.
1323 PROCEDURE Mult (s
: String
; n
: CARDINAL) : String
;
1327 s
:= CheckPoisoned (s
)
1331 s
:= AddToGarbage (InitString (''), s
)
1333 s
:= ConCat (Mult (s
, n
-1), s
)
1337 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
1344 Slice - returns a new string which contains the elements
1347 strings start at element 0
1348 Slice(s, 0, 2) will return elements 0, 1 but not 2
1349 Slice(s, 1, 3) will return elements 1, 2 but not 3
1350 Slice(s, 2, 0) will return elements 2..max
1351 Slice(s, 3, -1) will return elements 3..max-1
1352 Slice(s, 4, -2) will return elements 4..max-2
1355 PROCEDURE Slice (s
: String
; low
, high
: INTEGER) : String
;
1358 start
, stop
, o
: INTEGER ;
1362 s
:= CheckPoisoned (s
)
1366 low
:= VAL (INTEGER, Length (s
)) + low
1370 high
:= VAL (INTEGER, Length (s
)) + high
1372 (* make sure high is <= Length (s) *)
1373 high
:= Min (Length (s
), high
)
1375 d
:= InitString ('') ;
1376 d
:= AddToGarbage (d
, s
) ;
1380 IF low
< o
+ VAL (INTEGER, s^.contents.len
)
1386 (* found sliceable unit *)
1393 stop
:= Max (Min (MaxBuf
, high
- o
), 0) ;
1394 WHILE t^.contents.len
= MaxBuf
DO
1395 IF t^.contents.next
= NIL
1397 NEW (t^.contents.next
) ;
1398 WITH t^.contents.next^
DO
1402 AddDebugInfo (t^.contents.next
) ;
1405 t^.contents.next
:= AssignDebug (t^.contents.next
, __FILE__
, __LINE__
, __FUNCTION__
)
1408 t
:= t^.contents.next
1410 ConcatContentsAddress (t^.contents
,
1411 ADR (s^.contents.buf
[start
]), stop
- start
) ;
1412 INC (o
, s^.contents.len
) ;
1413 s
:= s^.contents.next
1416 INC (o
, s^.contents.len
) ;
1417 s
:= s^.contents.next
1422 d
:= AssignDebug (d
, __FILE__
, __LINE__
, __FUNCTION__
)
1429 Index - returns the indice of the first occurance of, ch, in
1430 String, s. -1 is returned if, ch, does not exist.
1431 The search starts at position, o.
1434 PROCEDURE Index (s
: String
; ch
: CHAR; o
: CARDINAL) : INTEGER ;
1440 s
:= CheckPoisoned (s
)
1445 IF k
+ contents.len
< o
1447 INC (k
, contents.len
)
1450 WHILE i
< contents.len
DO
1451 IF contents.buf
[i
] = ch
1461 s
:= s^.contents.next
1468 RIndex - returns the indice of the last occurance of, ch,
1469 in String, s. The search starts at position, o.
1470 -1 is returned if, ch, is not found. The search
1471 is performed left to right.
1474 PROCEDURE RIndex (s
: String
; ch
: CHAR; o
: CARDINAL) : INTEGER ;
1481 s
:= CheckPoisoned (s
)
1487 IF k
+ contents.len
< o
1489 INC (k
, contents.len
)
1497 WHILE i
< contents.len
DO
1498 IF contents.buf
[i
] = ch
1507 s
:= s^.contents.next
1514 ReverseIndex - returns the indice of the last occurance of ch
1515 in String s. The search starts at position o
1516 and searches from right to left. The start position
1517 may be indexed negatively from the right (-1 is the
1519 The return value if ch is found will always be positive.
1520 -1 is returned if ch is not found.
1523 PROCEDURE ReverseIndex (s
: String
; ch
: CHAR; o
: INTEGER) : INTEGER ;
1529 s
:= CheckPoisoned (s
)
1533 o
:= VAL (INTEGER, Length (s
)) + o
;
1539 IF VAL (CARDINAL, o
) < Length (s
)
1555 RemoveComment - assuming that, comment, is a comment delimiter
1556 which indicates anything to its right is a comment
1557 then strip off the comment and also any white space
1558 on the remaining right hand side.
1559 It leaves any white space on the left hand side alone.
1562 PROCEDURE RemoveComment (s
: String
; comment
: CHAR) : String
;
1566 i
:= Index (s
, comment
, 0) ;
1569 s
:= InitString ('')
1572 s
:= RemoveWhitePostfix (Slice (Mark (s
), 0, i
))
1576 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
1583 char - returns the character, ch, at position, i, in String, s.
1586 PROCEDURE char (s
: String
; i
: INTEGER) : CHAR ;
1592 s
:= CheckPoisoned (s
)
1596 c
:= VAL (CARDINAL, VAL (INTEGER, Length (s
)) + i
)
1600 WHILE (s #
NIL) AND (c
>= s^.contents.len
) DO
1601 DEC (c
, s^.contents.len
) ;
1602 s
:= s^.contents.next
1604 IF (s
= NIL) OR (c
>= s^.contents.len
)
1608 RETURN s^.contents.buf
[c
]
1614 string - returns the C style char * of String, s.
1617 PROCEDURE string (s
: String
) : ADDRESS
;
1621 p
: POINTER TO CHAR ;
1625 s
:= CheckPoisoned (s
)
1631 IF NOT s^.head^.charStarValid
1635 IF NOT (charStarUsed
AND (charStarSize
> l
))
1637 DeallocateCharStar (s
) ;
1638 ALLOCATE (charStar
, l
+1) ;
1639 charStarSize
:= l
+1 ;
1640 charStarUsed
:= TRUE
1647 WHILE i
< a^.contents.len
DO
1648 p^
:= a^.contents.buf
[i
] ;
1652 a
:= a^.contents.next
1655 s^.head^.charStarValid
:= TRUE
1657 RETURN s^.head^.charStar
1663 IsWhite - returns TRUE if, ch, is a space or a tab.
1666 PROCEDURE IsWhite (ch
: CHAR) : BOOLEAN ;
1668 RETURN (ch
= ' ') OR (ch
= tab
)
1673 RemoveWhitePrefix - removes any leading white space from String, s.
1674 A new string is returned.
1677 PROCEDURE RemoveWhitePrefix (s
: String
) : String
;
1682 WHILE IsWhite (char (s
, i
)) DO
1685 s
:= Slice (s
, INTEGER (i
), 0) ;
1688 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
1691 END RemoveWhitePrefix
;
1695 RemoveWhitePostfix - removes any leading white space from String, s.
1696 A new string is returned.
1699 PROCEDURE RemoveWhitePostfix (s
: String
) : String
;
1703 i
:= VAL(INTEGER, Length (s
)) - 1 ;
1704 WHILE (i
>= 0) AND IsWhite (char (s
, i
)) DO
1707 s
:= Slice (s
, 0, i
+1) ;
1710 s
:= AssignDebug (s
, __FILE__
, __LINE__
, __FUNCTION__
)
1713 END RemoveWhitePostfix
;
1717 ToUpper - returns string, s, after it has had its lower case characters
1718 replaced by upper case characters.
1719 The string, s, is not duplicated.
1722 PROCEDURE ToUpper (s
: String
) : String
;
1735 WHILE i
< contents.len
DO
1736 ch
:= contents.buf
[i
] ;
1737 IF (ch
>= 'a') AND (ch
<= 'z')
1739 contents.buf
[i
] := CHR (ORD (ch
) - ORD ('a') + ORD ('A'))
1744 t
:= t^.contents.next
1752 ToLower - returns string, s, after it has had its upper case characters
1753 replaced by lower case characters.
1754 The string, s, is not duplicated.
1757 PROCEDURE ToLower (s
: String
) : String
;
1770 WHILE i
< contents.len
DO
1771 ch
:= contents.buf
[i
] ;
1772 IF (ch
>= 'A') AND (ch
<= 'Z')
1774 contents.buf
[i
] := CHR (ORD (ch
) - ORD ('A') + ORD ('a'))
1779 t
:= t^.contents.next
1787 InitStringDB - the debug version of InitString.
1790 PROCEDURE InitStringDB (a
: ARRAY OF CHAR; file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1792 RETURN AssignDebug (InitString (a
), file
, line
, 'InitString')
1797 InitStringCharStarDB - the debug version of InitStringCharStar.
1800 PROCEDURE InitStringCharStarDB (a
: ADDRESS
; file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1802 RETURN AssignDebug (InitStringCharStar (a
), file
, line
, 'InitStringCharStar')
1803 END InitStringCharStarDB
;
1807 InitStringCharDB - the debug version of InitStringChar.
1810 PROCEDURE InitStringCharDB (ch
: CHAR; file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1812 RETURN AssignDebug (InitStringChar (ch
), file
, line
, 'InitStringChar')
1813 END InitStringCharDB
;
1817 MultDB - the debug version of MultDB.
1820 PROCEDURE MultDB (s
: String
; n
: CARDINAL; file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1822 RETURN AssignDebug (Mult (s
, n
), file
, line
, 'Mult')
1827 DupDB - the debug version of Dup.
1830 PROCEDURE DupDB (s
: String
; file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1832 RETURN AssignDebug (Dup (s
), file
, line
, 'Dup')
1837 SliceDB - debug version of Slice.
1840 PROCEDURE SliceDB (s
: String
; low
, high
: INTEGER;
1841 file
: ARRAY OF CHAR; line
: CARDINAL) : String
;
1844 s
:= AssignDebug (Slice (s
, low
, high
), file
, line
, 'Slice') ;
1854 PROCEDURE DumpState (s
: String
) ;
1856 CASE s^.head^.state
OF
1858 inuse
: writeString ("still in use (") ; writeCard (s^.contents.len
) ; writeString (") characters") |
1859 marked
: writeString ("marked") |
1860 onlist
: writeString ("on a garbage list") |
1861 poisoned
: writeString ("poisoned")
1864 writeString ("unknown state")
1870 DumpStringSynopsis -
1873 PROCEDURE DumpStringSynopsis (s
: String
) ;
1875 writeCstring (s^.debug.file
) ; writeString (':') ;
1876 writeCard (s^.debug.line
) ; writeString (':') ;
1877 writeCstring (s^.debug.proc
) ;
1878 writeString (' string ') ;
1882 IF IsOnAllocated (s
)
1884 writeString (' globally allocated')
1885 ELSIF IsOnDeallocated (s
)
1887 writeString (' globally deallocated')
1889 writeString (' globally unknown')
1892 END DumpStringSynopsis
;
1896 DumpString - displays the contents of string, s.
1899 PROCEDURE DumpString (s
: String
) ;
1905 DumpStringSynopsis (s
) ;
1906 IF (s^.head #
NIL) AND (s^.head^.garbage #
NIL)
1908 writeString ('display chained strings on the garbage list') ; writeLn
;
1909 t
:= s^.head^.garbage
;
1911 DumpStringSynopsis (t
) ;
1912 t
:= t^.head^.garbage
1920 Init - initialize the module.
1927 Initialized
:= TRUE ;
1935 Initialized
:= FALSE ;