MODULE; IMPORT ErrMsg, FileRd, OSError, Pathname, RCSDate, Rd, SupFileRec, SupFileRecSeq, SupMisc, Text, Thread, TokScan, UnixMisc, Uugid; CONST PreserveOptions = SupFileRec.Options{ (* Options set for "preserve". *) SupFileRec.Option.SetOwner, SupFileRec.Option.SetFlags }; PROCEDURE SupFile Parse (fileName: Pathname.T; override: SupFileRec.T := NIL; mask := SupFileRec.Options{}): SupFileRecSeq.T RAISES {Error} = <* FATAL Thread.Alerted *> VAR rd: Rd.T; collections: SupFileRecSeq.T; line, field: TEXT; ts, ts2: TokScan.T; coll: TEXT; default: SupFileRec.T := NIL; sfr: SupFileRec.T; name, value: TEXT; noCheckRCS: BOOLEAN; dfltNoCheckRCS := FALSE; BEGIN TRY rd := FileRd.Open(fileName); EXCEPT OSError.E(list) => RAISE Error("Cannot open \"" & fileName & "\": " & ErrMsg.StrError(list)); END; TRY collections := NEW(SupFileRecSeq.T).init(); LOOP TRY line := Rd.GetLine(rd); EXCEPT | Rd.EndOfFile => EXIT; | Rd.Failure(list) => RAISE Error("Read failure from \"" & fileName & "\": " & ErrMsg.StrError(list)); END; WITH pos = Text.FindChar(line, '#') DO IF pos >= 0 THEN line := Text.Sub(line, 0, pos) END; END; ts := TokScan.New(line); TRY IF ts.next(coll) THEN sfr := NEW(SupFileRec.T).init(default); sfr.collection := coll; noCheckRCS := dfltNoCheckRCS; WHILE ts.next(field) DO IF Text.FindChar(field, '=') >= 0 THEN ts2 := TokScan.New(field, SET OF CHAR{'='}); name := ts2.getToken("field name"); value := ts2.getToken("Field value"); IF TokScan.EqualFolded(name, "release") THEN IF NOT Pathname.Valid(value) THEN RAISE Error("Invalid release \"" & value & "\""); END; sfr.release := value; ELSIF TokScan.EqualFolded(name, "host") THEN sfr.serverHost := value; ELSIF TokScan.EqualFolded(name, "base") THEN IF NOT Pathname.Valid(value) THEN RAISE Error("Invalid base \"" & value & "\""); END; sfr.clientBase := value; ELSIF TokScan.EqualFolded(name, "prefix") THEN IF NOT Pathname.Valid(value) THEN RAISE Error("Invalid prefix \"" & value & "\""); END; sfr.clientPrefix := value; ELSIF TokScan.EqualFolded(name, "tag") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.CheckoutMode}; sfr.checkoutTag := value; ELSIF TokScan.EqualFolded(name, "date") THEN IF NOT Text.Equal(value, ".") AND NOT RCSDate.Valid(value) THEN RAISE Error("Invalid date \"" & value & "\""); END; sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.CheckoutMode}; sfr.checkoutDate := value; ELSIF TokScan.EqualFolded(name, "list") THEN IF Text.FindChar(value, SupMisc.SlashChar) >= 0 THEN RAISE Error("\"list\" suffix must not contain slashes"); END; sfr.listSuffix := value; ELSIF TokScan.EqualFolded(name, "umask") THEN sfr.umask := TokScan.AtoI(value, "umask value", 8); END; ELSE IF TokScan.EqualFolded(field, "backup") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.Backup}; ELSIF TokScan.EqualFolded(field, "delete") THEN sfr.options := sfr.options + SupFileRec.Options{ SupFileRec.Option.Delete, SupFileRec.Option.ExactRCS}; ELSIF TokScan.EqualFolded(field, "keep") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.Keep}; ELSIF TokScan.EqualFolded(field, "old") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.Old}; ELSIF TokScan.EqualFolded(field, "unlinkbusy") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.UnlinkBusy}; ELSIF TokScan.EqualFolded(field, "noupdate") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.NoUpdate}; ELSIF TokScan.EqualFolded(field, "compress") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.Compress}; ELSIF TokScan.EqualFolded(field, "use-rel-suffix") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.UseRelSuffix}; ELSIF TokScan.EqualFolded(field, "norsync") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.NoRsync}; ELSIF TokScan.EqualFolded(field, "norcs") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.NoRCS}; ELSIF TokScan.EqualFolded(field, "nocheckrcs") THEN noCheckRCS := TRUE; ELSIF TokScan.EqualFolded(field, "strictrcs") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.StrictCheckRCS}; ELSIF TokScan.EqualFolded(field, "execute") THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.Execute}; ELSIF TokScan.EqualFolded(field, "preserve") THEN sfr.options := sfr.options + PreserveOptions; END; END; END; IF Text.Equal(sfr.collection, "*default") THEN default := sfr; dfltNoCheckRCS := noCheckRCS; ELSE IF override # NIL THEN sfr.overrideFrom(override, mask); END; (* Determine whether to checksum RCS files or not. *) IF SupFileRec.Option.ExactRCS IN sfr.options AND NOT noCheckRCS THEN sfr.options := sfr.options + SupFileRec.Options{SupFileRec.Option.CheckRCS}; ELSE sfr.options := sfr.options - SupFileRec.Options{SupFileRec.Option.CheckRCS}; END; (* In recent versions, we always try to set the file modes. *) sfr.options := sfr.options + SupFileRec.Options{ SupFileRec.Option.SetMode}; (* Ignore "preserve" in checkout mode. *) IF SupFileRec.Option.CheckoutMode IN sfr.options THEN sfr.options := sfr.options - PreserveOptions; END; (* Preserve all mode bits if "preserve" is set. *) IF sfr.options >= PreserveOptions THEN sfr.umask := 0; END; (* If no umask was specified, default it to the OS umask. *) IF sfr.umask = -1 THEN sfr.umask := UnixMisc.GetUmask(); END; (* If the client is not the superuser, don't try to preserve the owner, group, and flags. FIXME - Unix specific. *) IF Uugid.geteuid() # 0 THEN (* FIXME - Unix specific. *) sfr.options := sfr.options - PreserveOptions; END; IF NOT Pathname.Valid(sfr.collection) THEN RAISE Error("Invalid collection \"" & sfr.collection & "\""); END; IF sfr.release = NIL THEN RAISE Error("Release not specified for collection \"" & sfr.collection & "\""); END; IF sfr.serverHost = NIL THEN RAISE Error("Host not specified for collection \"" & sfr.collection & "\""); END; IF collections.size() > 0 AND NOT TokScan.EqualFolded(sfr.serverHost, collections.gethi().serverHost) THEN RAISE Error("All \"host\" fields in the supfile must be the same"); END; IF sfr.clientBase = NIL THEN sfr.clientBase := SupMisc.DefaultClientBase; END; IF NOT SupMisc.IsDirectory(sfr.clientBase) THEN RAISE Error("Nonexistent base directory \"" & sfr.clientBase & "\" for collection \"" & sfr.collection & "\""); END; IF sfr.clientPrefix = NIL THEN sfr.clientPrefix := sfr.clientBase; ELSE sfr.clientPrefix := SupMisc.ResolvePath(sfr.clientBase, sfr.clientPrefix); END; IF sfr.clientCollDir = NIL THEN sfr.clientCollDir := SupMisc.DefaultClientCollDir; END; ParseRefuseFiles(sfr); collections.addhi(sfr); END; END; EXCEPT TokScan.Error(msg) => RAISE Error("Parse error in \"" & fileName & "\": " & msg); END; END; IF collections.size() = 0 THEN RAISE Error("Empty supfile"); END; FINALLY TRY Rd.Close(rd); EXCEPT Rd.Failure(list) => RAISE Error("Cannot close \"" & fileName & "\": " & ErrMsg.StrError(list)); END; END; RETURN collections; END Parse; PROCEDUREParseRefuseFile (sfr: SupFileRec.T; filename: Pathname.T) RAISES {Error} =
Parses arefuse
file, and records the relevant information insfr.refusals
. If the file does not exist, it is silently ignored.
<* FATAL Thread.Alerted *> VAR rd: Rd.T; line: TEXT; ts: TokScan.T; tok: TEXT; BEGIN TRY rd := FileRd.Open(filename); EXCEPT OSError.E => RETURN; END; TRY LOOP TRY line := Rd.GetLine(rd); EXCEPT | Rd.EndOfFile => EXIT; | Rd.Failure(list) => RAISE Error("Read failure from \"" & filename & "\": " & ErrMsg.StrError(list)); END; ts := TokScan.New(line); <* FATAL TokScan.Error *> BEGIN WHILE ts.next(tok) DO sfr.refusals.addhi(tok); END; END; END; FINALLY TRY Rd.Close(rd); EXCEPT Rd.Failure(list) => RAISE Error("Cannot close \"" & filename & "\": " & ErrMsg.StrError(list)); END; END; END ParseRefuseFile; PROCEDUREParseRefuseFiles (sfr: SupFileRec.T) RAISES {Error} = VAR supDir := SupMisc.ResolvePath(sfr.clientBase, sfr.clientCollDir); collStem := SupMisc.CatPath(supDir, SupMisc.CatPath(sfr.collection, "refuse")); BEGIN (* First the global refuse file that applies to all collections. *) ParseRefuseFile(sfr, SupMisc.CatPath(supDir, "refuse")); (* Next the per-collection refuse file that applies to all release/tag combinations. *) ParseRefuseFile(sfr, collStem); (* Finally, the per-release and per-tag refuse file. *) WITH suffix = SupMisc.StatusFileSuffix(sfr) DO IF NOT Text.Empty(suffix) THEN (* Different from per-collection. *) ParseRefuseFile(sfr, collStem & suffix); END; END; END ParseRefuseFiles; BEGIN END SupFile.