using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Perforce.P4 { /// /// Flags to define the base file type. /// [Flags] public enum BaseFileType { /// /// An unspecified base file type. /// Unspecified = 0x0000, /// /// Client Use: newlines translated. /// Server Storage: deltas in RCS format. /// Text = 0x0001, /// /// Client Use: raw bytes. /// Server Storage: compressed binary. /// Binary = 0x0002, /// /// Client Use: symbolic link. /// Server Storage: deltas in RCS format. /// Symlink = 0x0004, /// /// Client Use: Mac resource + data. /// Server Storage: compressed AppleSingle. /// Apple = 0x0008, /// /// Client Use: Mac resource fork. /// Server Storage: compressed binary. /// Resource = 0x0010, /// /// Client Use: newlines translated. /// Server Storage: deltas in RCS format /// stored as UTF-8. /// Unicode = 0x0020, /// /// Client Use: newlines translated /// client file UTF-16 /// Server Storage: deltas in RCS format /// stored as UTF-8 /// /// Files of type utf16 are stored in the depot in UTF-8. /// These files are in utf16 in the client workspace. /// The automatic type detection requires a BOM be present /// at the start of the file. Files without a BOM are /// assumed to be in client byte order. When utf16 files /// are written to a client, they are written with a BOM /// in client byte order. /// UTF16 = 0x0040 } /// /// Flags to specify file type modifiers. /// [Flags] public enum FileTypeModifier { /// /// No flags. /// None = 0x0000, /// /// +m always set modtime on client (overrides /// client's nomodtime). /// ModTime = 0x0001, /// /// +w always writable on client. /// Writable = 0x0002, /// /// +x exec bit set on client. /// Exec = 0x0004, /// /// +k $Keyword$ expansion of Id, Header, Author /// Date, DateTime, Change, File, Revision. /// KeywordsAll = 0x0008, /// /// +ko $Keyword$ expansion of ID, Header only. /// KeywordsLimited = 0x0010, /// /// +l exclusive open: disallow multiple opens. /// ExclusiveOpen = 0x0020, /// /// +C server stores compressed file per revision. /// CompressedFiles = 0x0040, /// /// +D server stores deltas in RCS format. /// RCSDeltaFiles = 0x0080, /// /// +F server stores full file per revision. /// FullRevisions = 0x0100, /// /// +S server stores only single head revision. /// HeadrevOnly = 0x0200, /// /// +S server stores number of revisions, where /// is a number 1-10 or 16,32,64,128,256,512. /// NRevsOnly = 0x0400, /// /// +X server runs archive trigger to access files /// ArchiveTrigger = 0x0800 } /// /// Specifies a Perforce file type for a managed file. /// public class FileType { public FileType (BaseFileType basetype, FileTypeModifier modifiers, int storedrevs) { if (((modifiers & FileTypeModifier.NRevsOnly) != 0) && ( storedrevs <1 | storedrevs >10 ) && (storedrevs != 16) && (storedrevs != 32) && (storedrevs != 64) && (storedrevs != 128) && (storedrevs != 256) && (storedrevs != 512)) { throw new ArgumentException("invalid number of revs to store"); } BaseType = basetype; Modifiers = modifiers; StoredRevs = storedrevs; } public FileType(BaseFileType basetype, FileTypeModifier modifiers) { if ((modifiers & FileTypeModifier.NRevsOnly) != 0) { throw new ArgumentException("need to specify number of revs to store"); } BaseType = basetype; Modifiers = modifiers; StoredRevs = 0; } public FileType (string spec) { Parse(spec); } private StringEnum _baseType; public BaseFileType BaseType { get { return _baseType; } set { _baseType = value; ;} } public FileTypeModifier Modifiers { get; set; } public int StoredRevs { get; set; } internal void Parse(string spec) { String flags = null; // check for historical filetypes if (spec == "ctempobj") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.HeadrevOnly|FileTypeModifier.Writable; return; } if (spec == "ctext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.CompressedFiles; return; } if (spec == "cxtext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.CompressedFiles|FileTypeModifier.Exec; return; } if (spec == "ktext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.KeywordsAll; return; } if (spec == "kxtext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.KeywordsAll|FileTypeModifier.Exec; return; } if (spec == "ltext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.FullRevisions; return; } if (spec == "tempobj") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.FullRevisions | FileTypeModifier.HeadrevOnly | FileTypeModifier.Writable; return; } if (spec == "ubinary") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.FullRevisions; return; } if (spec == "uresource") { this.BaseType = BaseFileType.Resource; this.Modifiers = FileTypeModifier.FullRevisions; return; } if (spec == "uxbinary") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.FullRevisions|FileTypeModifier.Exec; return; } if (spec == "xbinary") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.Exec; return; } if (spec == "xltext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.FullRevisions|FileTypeModifier.Exec; return; } if (spec == "xtext") { this.BaseType = BaseFileType.Text; this.Modifiers = FileTypeModifier.Exec; return; } if (spec == "xtempobj") { this.BaseType = BaseFileType.Binary; this.Modifiers = FileTypeModifier.FullRevisions|FileTypeModifier.HeadrevOnly|FileTypeModifier.Writable|FileTypeModifier.Exec; return; } if (spec == "xutf16") { this.BaseType = BaseFileType.UTF16; this.Modifiers = FileTypeModifier.Exec; return; } if (spec == "xunicode") { this.BaseType = BaseFileType.Unicode; this.Modifiers = FileTypeModifier.Exec; return; } if( spec.StartsWith( "+" ) ) { // no base type specified this.BaseType = BaseFileType.Unspecified; flags = spec.TrimStart('+'); } else { string[] parts = spec.Split('+'); try { this._baseType = parts[ 0 ]; } catch { this.BaseType = BaseFileType.Unspecified; } if (parts.Length > 1) flags = parts[1]; else return; // no flags, so we're done } for (int idx = 0; idx < flags.Length; idx++) { if ((idx+1 < flags.Length) && (flags[idx] == 'S')) { if (char.IsNumber(flags[idx + 1])) { this.Modifiers |= FileTypeModifier.NRevsOnly; int cnt = 0; while ((idx+cnt+1 < flags.Length) && char.IsNumber(flags[idx + cnt + 1 ])) { cnt++; } int revs = 1; int.TryParse(flags.Substring(idx+1, cnt), out revs); this.StoredRevs = revs; idx += cnt; } else { this.Modifiers |= FileTypeModifier.HeadrevOnly; } } else if ((idx + 1 < flags.Length) && (flags[idx] == 'k')) { if (flags[idx + 1] == 'o') { this.Modifiers |= FileTypeModifier.KeywordsLimited; idx++; } else this.Modifiers |= FileTypeModifier.KeywordsAll; } else { switch (flags[idx]) { case 'm': this.Modifiers |= FileTypeModifier.ModTime; break; case 'w': this.Modifiers |= FileTypeModifier.Writable; break; case 'x': this.Modifiers |= FileTypeModifier.Exec; break; case 'k': this.Modifiers |= FileTypeModifier.KeywordsAll; break; case 'l': this.Modifiers |= FileTypeModifier.ExclusiveOpen; break; case 'C': this.Modifiers |= FileTypeModifier.CompressedFiles; break; case 'D': this.Modifiers |= FileTypeModifier.RCSDeltaFiles; break; case 'F': this.Modifiers |= FileTypeModifier.FullRevisions; break; case 'S': this.Modifiers |= FileTypeModifier.HeadrevOnly; break; case 'X': this.Modifiers |= FileTypeModifier.ArchiveTrigger; break; default: //unknown flag break; } } } } public override string ToString() { String v = String.Empty; if (BaseType != BaseFileType.Unspecified) v = _baseType.ToString(StringEnumCase.Lower); if (Modifiers != FileTypeModifier.None) { v += "+"; if ((Modifiers & FileTypeModifier.ModTime) != 0) v += "m"; if ((Modifiers & FileTypeModifier.Writable) != 0) v += "w"; if ((Modifiers & FileTypeModifier.Exec) != 0) v += "x"; if ((Modifiers & FileTypeModifier.KeywordsAll) != 0) v += "k"; if ((Modifiers & FileTypeModifier.KeywordsLimited) != 0) v += "ko"; if ((Modifiers & FileTypeModifier.ExclusiveOpen) != 0) v += "l"; if ((Modifiers & FileTypeModifier.CompressedFiles) != 0) v += "C"; if ((Modifiers & FileTypeModifier.RCSDeltaFiles) != 0) v += "D"; if ((Modifiers & FileTypeModifier.FullRevisions) != 0) v += "F"; if ((Modifiers & FileTypeModifier.HeadrevOnly) != 0) v += "S"; if (((Modifiers & FileTypeModifier.NRevsOnly) != 0) && (StoredRevs >= 1)) v += String.Format("S{0}", StoredRevs); if ((Modifiers & FileTypeModifier.ArchiveTrigger) != 0) v += "X"; } return v; } } }