UNSAFE MODULE PathName_ux EXPORTS PathName, PathName_ux; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) IMPORT Text; PROCEDURE DirSepCh(): CHAR RAISES {}= BEGIN RETURN DirSepChar; END DirSepCh; PROCEDURE CaseSensitive(): BOOLEAN RAISES {}= BEGIN RETURN CaseSensitivity; END CaseSensitive; PROCEDURE Current(): Text.T RAISES {}= BEGIN RETURN CurrentDirText; END Current; PROCEDURE NameSections( name: Text.T; VAR nameLwb, nameUpb, extUpb: CARDINAL) : BOOLEAN RAISES {} = (* after this call: nameLwb is the index of the first character of the last component of 'name' nameUpb is the index of the character separating the extension and name parts of the last component of 'name'. If there is no extension 'nameUpb = extUpb' extUpb is 'Length(name)' *) VAR pos: CARDINAL; ch: CHAR; BEGIN extUpb := Text.Length(name); pos := extUpb; nameUpb := extUpb; LOOP IF pos > 0 THEN DEC(pos); ch := Text.GetChar(name, pos); IF ch = DirSepChar THEN nameLwb := pos + 1; EXIT; ELSE IF ch = ExtSepChar THEN nameUpb := pos END; (* loop *) END; ELSE nameLwb := 0; EXIT; END; END; RETURN extUpb # nameUpb; END NameSections; PROCEDURE Tail(name: Text.T): Text.T RAISES {} = VAR nameLwb, nameUpb, extUpb: CARDINAL; BEGIN EVAL NameSections(name, nameLwb, nameUpb, extUpb); RETURN Text.Sub(name, nameLwb, extUpb - nameLwb + 1); END Tail; PROCEDURE Head(name: Text.T): Text.T RAISES {} = VAR nameLwb, nameUpb, extUpb: CARDINAL; BEGIN EVAL NameSections(name, nameLwb, nameUpb, extUpb); RETURN Text.Sub(name, 0, nameLwb); END Head; PROCEDURE Extension(name: Text.T): Text.T RAISES {} = VAR nameLwb, nameUpb, extUpb: CARDINAL; BEGIN IF NameSections(name, nameLwb, nameUpb, extUpb) THEN RETURN Text.Sub(name, nameUpb + 1, extUpb - nameUpb); ELSE RETURN ""; END; END Extension; PROCEDURE Name(name: Text.T): Text.T RAISES {} = VAR nameLwb, nameUpb, extUpb: CARDINAL; BEGIN EVAL NameSections(name, nameLwb, nameUpb, extUpb); RETURN Text.Sub(name, nameLwb, nameUpb - nameLwb); END Name; PROCEDURE Extend(name, ext: Text.T): Text.T RAISES {} = VAR nameLwb, nameUpb, extUpb: CARDINAL; result: Text.T; BEGIN IF NameSections(name, nameLwb, nameUpb, extUpb) THEN result := Text.Sub(name, 0, nameUpb); ELSE result := name; END; IF Text.Length(ext) = 0 THEN RETURN result; ELSE RETURN result & ExtSepText & ext; END; END Extend; PROCEDURE Concat(head, tail: Text.T): Text.T RAISES {}= BEGIN WITH lh = Text.Length(head) DO IF lh = 0 THEN RETURN tail END; IF Text.Length(tail) = 0 THEN RETURN head END; IF Text.GetChar(head, lh - 1) = DirSepChar THEN RETURN head & tail; ELSE RETURN head & DirSepText & tail; END; END; END Concat; PROCEDURE Valid(name: Text.T): BOOLEAN RAISES {}= VAR length := Text.Length(name); start, pos := 0; ch: CHAR; BEGIN IF length = 0 OR length > MaxLength THEN RETURN FALSE; END; LOOP IF pos >= length THEN ch := DirSepChar; ELSE ch := Text.GetChar(name, pos); END; IF ch = DirSepChar THEN WITH segmentLength = pos - start DO IF segmentLength > MaxComponentLength THEN (* segment is too long *) RETURN FALSE; ELSIF pos >= length THEN (* last segment *) RETURN TRUE ELSE start := pos + 1; END; END; END; INC(pos); END; END Valid; PROCEDURE Full(dir, relative: Text.T): Text.T RAISES {}= BEGIN IF Text.Length(dir) = 0 OR Text.Equal(dir, ".") OR Text.Equal(dir, "./") THEN RETURN relative; END; WITH length = Text.Length(relative) DO IF length = 0 THEN RETURN dir END; WITH ch = Text.GetChar(relative, 0) DO IF ch = DirSepChar THEN RETURN relative END; IF ch = '.' AND length = 1 THEN RETURN dir END; END; END; (* with *) RETURN Concat(dir, relative); END Full; EXCEPTION NotAbsolute; PROCEDURE Relative(name1, name2: Text.T): Text.T RAISES {}= VAR length1 := Text.Length(name1); length2 := Text.Length(name2); BEGIN IF length1 > 0 AND Text.GetChar(name1, 0) = DirSepChar AND length2 > 0 AND Text.GetChar(name2, 0) = DirSepChar THEN IF length1 = length2 AND Text.Equal(name1, name2) THEN RETURN "."; ELSIF length2 > length1 AND Text.GetChar(name2, length1) = DirSepChar AND Text.Equal(name1, Text.Sub(name2, 0, length1 - 1)) THEN VAR pos := length1 + 1; BEGIN LOOP IF pos >= length2 THEN RETURN "."; ELSIF Text.GetChar(name2, pos) # DirSepChar THEN RETURN Text.Sub(name2, pos, length2 - pos); ELSE INC(pos); END; END; END; ELSE RETURN name2; END; (* if *) ELSE RAISE NotAbsolute; <* CRASH *> END; (* if *) END Relative; BEGIN END PathName_ux.