diff --git a/ide/mac/LgiIde.xcodeproj/project.pbxproj b/ide/mac/LgiIde.xcodeproj/project.pbxproj --- a/ide/mac/LgiIde.xcodeproj/project.pbxproj +++ b/ide/mac/LgiIde.xcodeproj/project.pbxproj @@ -1,709 +1,707 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ 34148AFC2349ECA200D5E495 /* LgiCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B20D1217968820079E3C9 /* LgiCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 341C7AC821A752DC0083C9CE /* About.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 341C7AC721A752DC0083C9CE /* About.cpp */; }; - 3432AF732ADFE2F40029F2D7 /* libpng16.15.29.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3414FDD82ADFE2D00092DA8F /* libpng16.15.29.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 3432AF762ADFE3120029F2D7 /* libz_local.1.2.5.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF742ADFE3080029F2D7 /* libz_local.1.2.5.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3432AF732ADFE2F40029F2D7 /* libpng16.16.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3414FDD82ADFE2D00092DA8F /* libpng16.16.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3432AF762ADFE3120029F2D7 /* libz.1.3.1.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF742ADFE3080029F2D7 /* libz.1.3.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 345B20D42179688E0079E3C9 /* LgiCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 345B20D1217968820079E3C9 /* LgiCocoa.framework */; }; 345B2101217969700079E3C9 /* PythonParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20D52179696B0079E3C9 /* PythonParser.cpp */; }; 345B2102217969700079E3C9 /* MemDumpViewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */; }; 345B2103217969700079E3C9 /* IdeFindInFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DB2179696B0079E3C9 /* IdeFindInFiles.cpp */; }; 345B2104217969700079E3C9 /* DocEditStyling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */; }; 345B2105217969700079E3C9 /* ProjectNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */; }; 345B2106217969700079E3C9 /* SysCharSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */; }; 345B2107217969700079E3C9 /* IdeDoc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E12179696C0079E3C9 /* IdeDoc.cpp */; }; 345B2108217969700079E3C9 /* SpaceTabConv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */; }; 345B2109217969700079E3C9 /* FindSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E32179696C0079E3C9 /* FindSymbol.cpp */; }; 345B210A217969700079E3C9 /* WebFldDlg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */; }; 345B210B217969700079E3C9 /* SimpleCppParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */; }; 345B210C217969700079E3C9 /* DocEdit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E72179696C0079E3C9 /* DocEdit.cpp */; }; 345B210D217969700079E3C9 /* AddFtpFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */; }; 345B210E217969700079E3C9 /* History.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EE2179696D0079E3C9 /* History.cpp */; }; 345B210F217969700079E3C9 /* IdeProject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EF2179696E0079E3C9 /* IdeProject.cpp */; }; 345B2112217969700079E3C9 /* LgiUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F52179696E0079E3C9 /* LgiUtils.cpp */; }; 345B2113217969700079E3C9 /* LgiIde.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F62179696E0079E3C9 /* LgiIde.cpp */; }; 345B2114217969700079E3C9 /* IdeCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F72179696F0079E3C9 /* IdeCommon.cpp */; }; 345B2115217969700079E3C9 /* FtpThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FB2179696F0079E3C9 /* FtpThread.cpp */; }; 345B2116217969700079E3C9 /* Debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FC2179696F0079E3C9 /* Debugger.cpp */; }; 345B2117217969700079E3C9 /* DebugContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FD2179696F0079E3C9 /* DebugContext.cpp */; }; 345B2118217969700079E3C9 /* IdeProjectSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */; }; 345B2119217969700079E3C9 /* MissingFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FF217969700079E3C9 /* MissingFiles.cpp */; }; 34C4FD2A22FD7EB800C4BECB /* icons.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211D217969D30079E3C9 /* icons.png */; }; 34C4FD2B22FD7EC100C4BECB /* LgiIde.lr8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211C217969D30079E3C9 /* LgiIde.lr8 */; }; 34C4FD2C22FD7EC700C4BECB /* mac-icon.icns in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211B217969D30079E3C9 /* mac-icon.icns */; }; 34C4FD3422FDB58B00C4BECB /* cmds-16px.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */; }; 34C4FD3522FDB58B00C4BECB /* cmds-32px.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */; }; 34C75D9323A0D89A006DE6D1 /* NewProjectFromTemplate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */; }; 34D21CD821797815003D1EA6 /* ExeCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CD721797815003D1EA6 /* ExeCheck.cpp */; }; 34D21CDA21797833003D1EA6 /* LexCpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CD921797833003D1EA6 /* LexCpp.cpp */; }; 34D21CDE21797856003D1EA6 /* OptionsFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */; }; 34D21CE021797856003D1EA6 /* Mdi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CDD21797856003D1EA6 /* Mdi.cpp */; }; 34D21CE42179788B003D1EA6 /* Ftp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE12179788B003D1EA6 /* Ftp.cpp */; }; 34D21CE52179788B003D1EA6 /* OpenSSLSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */; }; 34D21CE62179788B003D1EA6 /* Http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE32179788B003D1EA6 /* Http.cpp */; }; 34D21CE8217978C4003D1EA6 /* LgiMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */; }; 34D21CEA217978DA003D1EA6 /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE9217978DA003D1EA6 /* Base64.cpp */; }; 34D21CEE21798240003D1EA6 /* Png.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CED21798240003D1EA6 /* Png.cpp */; }; 34F566AB237BD55F0027133A /* JavascriptParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34F566A0237BD55F0027133A /* JavascriptParser.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 345B20D0217968820079E3C9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3477C2681CBF020F0028B84B; remoteInfo = LgiCocoa; }; 345B20D2217968890079E3C9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; proxyType = 1; remoteGlobalIDString = 3477C2671CBF020F0028B84B; remoteInfo = LgiCocoa; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 34148AF12349EC9500D5E495 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 3432AF762ADFE3120029F2D7 /* libz_local.1.2.5.dylib in CopyFiles */, - 3432AF732ADFE2F40029F2D7 /* libpng16.15.29.0.dylib in CopyFiles */, + 3432AF762ADFE3120029F2D7 /* libz.1.3.1.dylib in CopyFiles */, + 3432AF732ADFE2F40029F2D7 /* libpng16.16.dylib in CopyFiles */, 34148AFC2349ECA200D5E495 /* LgiCocoa.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; 34CAF47E22D3771500BE529A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 7; files = ( 34C4FD3422FDB58B00C4BECB /* cmds-16px.png in CopyFiles */, 34C4FD3522FDB58B00C4BECB /* cmds-32px.png in CopyFiles */, 34C4FD2C22FD7EC700C4BECB /* mac-icon.icns in CopyFiles */, 34C4FD2B22FD7EC100C4BECB /* LgiIde.lr8 in CopyFiles */, 34C4FD2A22FD7EB800C4BECB /* icons.png in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 3411DB712AB86D6700CB3837 /* PostBuildStep.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = PostBuildStep.py; sourceTree = ""; }; - 3414FDD82ADFE2D00092DA8F /* libpng16.15.29.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpng16.15.29.0.dylib; path = ../../../../../codelib/libpng/build/release/libpng16.15.29.0.dylib; sourceTree = ""; }; + 3414FDD82ADFE2D00092DA8F /* libpng16.16.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpng16.16.dylib; path = /opt/local/lib/libpng16.16.dylib; sourceTree = ""; }; 341C7AC721A752DC0083C9CE /* About.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = About.cpp; path = ../../src/common/Lgi/About.cpp; sourceTree = ""; }; - 3432AF742ADFE3080029F2D7 /* libz_local.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz_local.1.2.5.dylib; path = ../../../../../codelib/libpng/build/release/zlib_dir/libz_local.1.2.5.dylib; sourceTree = ""; }; + 3432AF742ADFE3080029F2D7 /* libz.1.3.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.3.1.dylib; path = ../../../../../../../opt/local/lib/libz.1.3.1.dylib; sourceTree = ""; }; 345B206C217965E80079E3C9 /* LgiIde.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LgiIde.app; sourceTree = BUILT_PRODUCTS_DIR; }; 345B2077217965EA0079E3C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 345B207A217965EA0079E3C9 /* LgiIde.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LgiIde.entitlements; sourceTree = ""; }; 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LgiCocoa.xcodeproj; path = ../../src/mac/cocoa/LgiCocoa.xcodeproj; sourceTree = ""; }; 345B20D52179696B0079E3C9 /* PythonParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonParser.cpp; path = ../src/PythonParser.cpp; sourceTree = ""; }; 345B20D62179696B0079E3C9 /* SpaceTabConv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpaceTabConv.h; path = ../src/SpaceTabConv.h; sourceTree = ""; }; 345B20D72179696B0079E3C9 /* IdeDocPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeDocPrivate.h; path = ../src/IdeDocPrivate.h; sourceTree = ""; }; 345B20D82179696B0079E3C9 /* FtpThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FtpThread.h; path = ../src/FtpThread.h; sourceTree = ""; }; 345B20D92179696B0079E3C9 /* WebFldDlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebFldDlg.h; path = ../src/WebFldDlg.h; sourceTree = ""; }; 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemDumpViewer.cpp; path = ../src/MemDumpViewer.cpp; sourceTree = ""; }; 345B20DB2179696B0079E3C9 /* IdeFindInFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeFindInFiles.cpp; path = ../src/IdeFindInFiles.cpp; sourceTree = ""; }; 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocEditStyling.cpp; path = ../src/DocEditStyling.cpp; sourceTree = ""; }; 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProjectNode.cpp; path = ../src/ProjectNode.cpp; sourceTree = ""; }; 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SysCharSupport.cpp; path = ../src/SysCharSupport.cpp; sourceTree = ""; }; 345B20DF2179696C0079E3C9 /* IdeProjectSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeProjectSettings.h; path = ../src/IdeProjectSettings.h; sourceTree = ""; }; 345B20E02179696C0079E3C9 /* FindSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FindSymbol.h; path = ../src/FindSymbol.h; sourceTree = ""; }; 345B20E12179696C0079E3C9 /* IdeDoc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeDoc.cpp; path = ../src/IdeDoc.cpp; sourceTree = ""; }; 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SpaceTabConv.cpp; path = ../src/SpaceTabConv.cpp; sourceTree = ""; }; 345B20E32179696C0079E3C9 /* FindSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FindSymbol.cpp; path = ../src/FindSymbol.cpp; sourceTree = ""; }; 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebFldDlg.cpp; path = ../src/WebFldDlg.cpp; sourceTree = ""; }; 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SimpleCppParser.cpp; path = ../src/SimpleCppParser.cpp; sourceTree = ""; }; 345B20E62179696C0079E3C9 /* IdeDoc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeDoc.h; path = ../src/IdeDoc.h; sourceTree = ""; }; 345B20E72179696C0079E3C9 /* DocEdit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocEdit.cpp; path = ../src/DocEdit.cpp; sourceTree = ""; }; 345B20E82179696C0079E3C9 /* DocEdit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DocEdit.h; path = ../src/DocEdit.h; sourceTree = ""; }; 345B20E92179696D0079E3C9 /* IdeFindInFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeFindInFiles.h; path = ../src/IdeFindInFiles.h; sourceTree = ""; }; 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddFtpFile.cpp; path = ../src/AddFtpFile.cpp; sourceTree = ""; }; 345B20EB2179696D0079E3C9 /* AddFtpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddFtpFile.h; path = ../src/AddFtpFile.h; sourceTree = ""; }; 345B20EC2179696D0079E3C9 /* FtpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FtpFile.h; path = ../src/FtpFile.h; sourceTree = ""; }; 345B20ED2179696D0079E3C9 /* Debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Debugger.h; path = ../src/Debugger.h; sourceTree = ""; }; 345B20EE2179696D0079E3C9 /* History.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = History.cpp; path = ../src/History.cpp; sourceTree = ""; }; 345B20EF2179696E0079E3C9 /* IdeProject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeProject.cpp; path = ../src/IdeProject.cpp; sourceTree = ""; }; 345B20F02179696E0079E3C9 /* DebugContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DebugContext.h; path = ../src/DebugContext.h; sourceTree = ""; }; 345B20F22179696E0079E3C9 /* History.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = History.h; path = ../src/History.h; sourceTree = ""; }; 345B20F52179696E0079E3C9 /* LgiUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiUtils.cpp; path = ../src/LgiUtils.cpp; sourceTree = ""; }; 345B20F62179696E0079E3C9 /* LgiIde.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = LgiIde.cpp; path = ../src/LgiIde.cpp; sourceTree = ""; }; 345B20F72179696F0079E3C9 /* IdeCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeCommon.cpp; path = ../src/IdeCommon.cpp; sourceTree = ""; }; 345B20F82179696F0079E3C9 /* IdeProject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeProject.h; path = ../src/IdeProject.h; sourceTree = ""; }; 345B20F92179696F0079E3C9 /* LgiIde.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiIde.h; path = ../src/LgiIde.h; sourceTree = ""; }; 345B20FA2179696F0079E3C9 /* ProjectNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProjectNode.h; path = ../src/ProjectNode.h; sourceTree = ""; }; 345B20FB2179696F0079E3C9 /* FtpThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FtpThread.cpp; path = ../src/FtpThread.cpp; sourceTree = ""; }; 345B20FC2179696F0079E3C9 /* Debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Debugger.cpp; path = ../src/Debugger.cpp; sourceTree = ""; }; 345B20FD2179696F0079E3C9 /* DebugContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DebugContext.cpp; path = ../src/DebugContext.cpp; sourceTree = ""; }; 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeProjectSettings.cpp; path = ../src/IdeProjectSettings.cpp; sourceTree = ""; }; 345B20FF217969700079E3C9 /* MissingFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MissingFiles.cpp; path = ../src/MissingFiles.cpp; sourceTree = ""; }; 345B2100217969700079E3C9 /* ParserCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ParserCommon.h; path = ../src/ParserCommon.h; sourceTree = ""; }; 345B211B217969D30079E3C9 /* mac-icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = "mac-icon.icns"; path = "../Resources/mac-icon.icns"; sourceTree = ""; }; 345B211C217969D30079E3C9 /* LgiIde.lr8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = LgiIde.lr8; path = ../Resources/LgiIde.lr8; sourceTree = ""; }; 345B211D217969D30079E3C9 /* icons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icons.png; path = ../Resources/icons.png; sourceTree = ""; }; 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cmds-16px.png"; sourceTree = ""; }; 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cmds-32px.png"; sourceTree = ""; }; 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NewProjectFromTemplate.cpp; path = ../src/NewProjectFromTemplate.cpp; sourceTree = ""; }; 34D21CD721797815003D1EA6 /* ExeCheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExeCheck.cpp; path = ../../src/common/General/ExeCheck.cpp; sourceTree = ""; }; 34D21CD921797833003D1EA6 /* LexCpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexCpp.cpp; path = ../../src/common/Coding/LexCpp.cpp; sourceTree = ""; }; 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionsFile.cpp; path = ../../src/common/Lgi/OptionsFile.cpp; sourceTree = ""; }; 34D21CDD21797856003D1EA6 /* Mdi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mdi.cpp; path = ../../src/common/Lgi/Mdi.cpp; sourceTree = ""; }; 34D21CE12179788B003D1EA6 /* Ftp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ftp.cpp; path = ../../src/common/Net/Ftp.cpp; sourceTree = ""; }; 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OpenSSLSocket.cpp; path = ../../src/common/Net/OpenSSLSocket.cpp; sourceTree = ""; }; 34D21CE32179788B003D1EA6 /* Http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Http.cpp; path = ../../src/common/Net/Http.cpp; sourceTree = ""; }; 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiMain.cpp; path = ../../src/common/Lgi/LgiMain.cpp; sourceTree = ""; }; 34D21CE9217978DA003D1EA6 /* Base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Base64.cpp; path = ../../src/common/Net/Base64.cpp; sourceTree = ""; }; 34D21CED21798240003D1EA6 /* Png.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Png.cpp; path = ../../src/common/Gdc2/Filters/Png.cpp; sourceTree = ""; }; 34F566A0237BD55F0027133A /* JavascriptParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JavascriptParser.cpp; path = ../src/JavascriptParser.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 345B2069217965E80079E3C9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 345B20D42179688E0079E3C9 /* LgiCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 345B2063217965E80079E3C9 = { isa = PBXGroup; children = ( 345B206E217965E80079E3C9 /* LgiIde */, 345B211A217969C50079E3C9 /* Resources */, 345B206D217965E80079E3C9 /* Products */, 345B20CB217967B70079E3C9 /* Frameworks */, ); sourceTree = ""; }; 345B206D217965E80079E3C9 /* Products */ = { isa = PBXGroup; children = ( 345B206C217965E80079E3C9 /* LgiIde.app */, ); name = Products; sourceTree = ""; }; 345B206E217965E80079E3C9 /* LgiIde */ = { isa = PBXGroup; children = ( 345B20F62179696E0079E3C9 /* LgiIde.cpp */, 345B20F52179696E0079E3C9 /* LgiUtils.cpp */, 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */, 345B20F92179696F0079E3C9 /* LgiIde.h */, 345B2077217965EA0079E3C9 /* Info.plist */, 3411DB712AB86D6700CB3837 /* PostBuildStep.py */, 345B212421796A1E0079E3C9 /* Debugging */, 345B212221796A040079E3C9 /* Docs */, 34D21CD621797809003D1EA6 /* Lgi */, 345B2121217969F10079E3C9 /* Projects */, 345B212521796A2D0079E3C9 /* Search */, 345B212321796A180079E3C9 /* Ui */, ); name = LgiIde; sourceTree = ""; }; 345B20CB217967B70079E3C9 /* Frameworks */ = { isa = PBXGroup; children = ( - 3414FDD82ADFE2D00092DA8F /* libpng16.15.29.0.dylib */, - 3432AF742ADFE3080029F2D7 /* libz_local.1.2.5.dylib */, + 3414FDD82ADFE2D00092DA8F /* libpng16.16.dylib */, + 3432AF742ADFE3080029F2D7 /* libz.1.3.1.dylib */, 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */, ); name = Frameworks; sourceTree = ""; }; 345B20CD217968820079E3C9 /* Products */ = { isa = PBXGroup; children = ( 345B20D1217968820079E3C9 /* LgiCocoa.framework */, ); name = Products; sourceTree = ""; }; 345B211A217969C50079E3C9 /* Resources */ = { isa = PBXGroup; children = ( 345B207A217965EA0079E3C9 /* LgiIde.entitlements */, 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */, 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */, 345B211D217969D30079E3C9 /* icons.png */, 345B211C217969D30079E3C9 /* LgiIde.lr8 */, 345B211B217969D30079E3C9 /* mac-icon.icns */, ); name = Resources; path = ../Resources; sourceTree = ""; }; 345B2121217969F10079E3C9 /* Projects */ = { isa = PBXGroup; children = ( 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */, 345B20FA2179696F0079E3C9 /* ProjectNode.h */, 345B20D72179696B0079E3C9 /* IdeDocPrivate.h */, 345B20EF2179696E0079E3C9 /* IdeProject.cpp */, 345B20F82179696F0079E3C9 /* IdeProject.h */, 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */, 345B20DF2179696C0079E3C9 /* IdeProjectSettings.h */, ); name = Projects; sourceTree = ""; }; 345B212221796A040079E3C9 /* Docs */ = { isa = PBXGroup; children = ( 345B20E12179696C0079E3C9 /* IdeDoc.cpp */, 345B20E62179696C0079E3C9 /* IdeDoc.h */, 345B20E72179696C0079E3C9 /* DocEdit.cpp */, 345B20E82179696C0079E3C9 /* DocEdit.h */, 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */, ); name = Docs; sourceTree = ""; }; 345B212321796A180079E3C9 /* Ui */ = { isa = PBXGroup; children = ( 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */, 345B20D92179696B0079E3C9 /* WebFldDlg.h */, 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */, 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */, 345B20D62179696B0079E3C9 /* SpaceTabConv.h */, 345B20EC2179696D0079E3C9 /* FtpFile.h */, 345B20FB2179696F0079E3C9 /* FtpThread.cpp */, 345B20D82179696B0079E3C9 /* FtpThread.h */, 345B20EE2179696D0079E3C9 /* History.cpp */, 345B20F22179696E0079E3C9 /* History.h */, 345B20F72179696F0079E3C9 /* IdeCommon.cpp */, 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */, 345B20FF217969700079E3C9 /* MissingFiles.cpp */, 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */, 345B20EB2179696D0079E3C9 /* AddFtpFile.h */, ); name = Ui; sourceTree = ""; }; 345B212421796A1E0079E3C9 /* Debugging */ = { isa = PBXGroup; children = ( 345B20FD2179696F0079E3C9 /* DebugContext.cpp */, 345B20F02179696E0079E3C9 /* DebugContext.h */, 345B20FC2179696F0079E3C9 /* Debugger.cpp */, 345B20ED2179696D0079E3C9 /* Debugger.h */, ); name = Debugging; sourceTree = ""; }; 345B212521796A2D0079E3C9 /* Search */ = { isa = PBXGroup; children = ( 34F566A0237BD55F0027133A /* JavascriptParser.cpp */, 34D21CD921797833003D1EA6 /* LexCpp.cpp */, 345B2100217969700079E3C9 /* ParserCommon.h */, 345B20D52179696B0079E3C9 /* PythonParser.cpp */, 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */, 345B20DB2179696B0079E3C9 /* IdeFindInFiles.cpp */, 345B20E92179696D0079E3C9 /* IdeFindInFiles.h */, 345B20E32179696C0079E3C9 /* FindSymbol.cpp */, 345B20E02179696C0079E3C9 /* FindSymbol.h */, ); name = Search; sourceTree = ""; }; 34D21CD621797809003D1EA6 /* Lgi */ = { isa = PBXGroup; children = ( 341C7AC721A752DC0083C9CE /* About.cpp */, 34D21CED21798240003D1EA6 /* Png.cpp */, 34D21CE9217978DA003D1EA6 /* Base64.cpp */, 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */, 34D21CE12179788B003D1EA6 /* Ftp.cpp */, 34D21CE32179788B003D1EA6 /* Http.cpp */, 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */, 34D21CDD21797856003D1EA6 /* Mdi.cpp */, 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */, 34D21CD721797815003D1EA6 /* ExeCheck.cpp */, ); name = Lgi; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 345B206B217965E80079E3C9 /* LgiIde */ = { isa = PBXNativeTarget; buildConfigurationList = 345B207D217965EA0079E3C9 /* Build configuration list for PBXNativeTarget "LgiIde" */; buildPhases = ( 345B2068217965E80079E3C9 /* Sources */, 345B2069217965E80079E3C9 /* Frameworks */, 34CAF47E22D3771500BE529A /* CopyFiles */, 34148AF12349EC9500D5E495 /* CopyFiles */, 3411DB672AB86D2700CB3837 /* ShellScript */, ); buildRules = ( ); dependencies = ( 345B20D3217968890079E3C9 /* PBXTargetDependency */, ); name = LgiIde; productName = LgiIde; productReference = 345B206C217965E80079E3C9 /* LgiIde.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 345B2064217965E80079E3C9 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1420; ORGANIZATIONNAME = "Matthew Allen"; TargetAttributes = { 345B206B217965E80079E3C9 = { CreatedOnToolsVersion = 9.3; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.Sandbox = { enabled = 0; }; }; }; }; }; buildConfigurationList = 345B2067217965E80079E3C9 /* Build configuration list for PBXProject "LgiIde" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 345B2063217965E80079E3C9; productRefGroup = 345B206D217965E80079E3C9 /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 345B20CD217968820079E3C9 /* Products */; ProjectRef = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; }, ); projectRoot = ""; targets = ( 345B206B217965E80079E3C9 /* LgiIde */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 345B20D1217968820079E3C9 /* LgiCocoa.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = LgiCocoa.framework; remoteRef = 345B20D0217968820079E3C9 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXShellScriptBuildPhase section */ 3411DB672AB86D2700CB3837 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\npython3 ${PROJECT_DIR}/PostBuildStep.py ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 345B2068217965E80079E3C9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 34D21CD821797815003D1EA6 /* ExeCheck.cpp in Sources */, 345B2115217969700079E3C9 /* FtpThread.cpp in Sources */, 345B210F217969700079E3C9 /* IdeProject.cpp in Sources */, 34D21CE62179788B003D1EA6 /* Http.cpp in Sources */, 345B210C217969700079E3C9 /* DocEdit.cpp in Sources */, 345B2107217969700079E3C9 /* IdeDoc.cpp in Sources */, 34D21CEE21798240003D1EA6 /* Png.cpp in Sources */, 345B2101217969700079E3C9 /* PythonParser.cpp in Sources */, 345B2119217969700079E3C9 /* MissingFiles.cpp in Sources */, 345B2114217969700079E3C9 /* IdeCommon.cpp in Sources */, 345B2108217969700079E3C9 /* SpaceTabConv.cpp in Sources */, 345B2104217969700079E3C9 /* DocEditStyling.cpp in Sources */, 345B210B217969700079E3C9 /* SimpleCppParser.cpp in Sources */, 34D21CE8217978C4003D1EA6 /* LgiMain.cpp in Sources */, 345B2116217969700079E3C9 /* Debugger.cpp in Sources */, 341C7AC821A752DC0083C9CE /* About.cpp in Sources */, 345B210E217969700079E3C9 /* History.cpp in Sources */, 345B2113217969700079E3C9 /* LgiIde.cpp in Sources */, 345B2112217969700079E3C9 /* LgiUtils.cpp in Sources */, 345B2103217969700079E3C9 /* IdeFindInFiles.cpp in Sources */, 34D21CEA217978DA003D1EA6 /* Base64.cpp in Sources */, 34D21CE021797856003D1EA6 /* Mdi.cpp in Sources */, 34D21CDA21797833003D1EA6 /* LexCpp.cpp in Sources */, 34F566AB237BD55F0027133A /* JavascriptParser.cpp in Sources */, 345B2118217969700079E3C9 /* IdeProjectSettings.cpp in Sources */, 345B2106217969700079E3C9 /* SysCharSupport.cpp in Sources */, 345B2109217969700079E3C9 /* FindSymbol.cpp in Sources */, 345B2102217969700079E3C9 /* MemDumpViewer.cpp in Sources */, 34C75D9323A0D89A006DE6D1 /* NewProjectFromTemplate.cpp in Sources */, 34D21CE42179788B003D1EA6 /* Ftp.cpp in Sources */, 345B2105217969700079E3C9 /* ProjectNode.cpp in Sources */, 34D21CDE21797856003D1EA6 /* OptionsFile.cpp in Sources */, 34D21CE52179788B003D1EA6 /* OpenSSLSocket.cpp in Sources */, 345B210D217969700079E3C9 /* AddFtpFile.cpp in Sources */, 345B210A217969700079E3C9 /* WebFldDlg.cpp in Sources */, 345B2117217969700079E3C9 /* DebugContext.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 345B20D3217968890079E3C9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = LgiCocoa; targetProxy = 345B20D2217968890079E3C9 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 345B207B217965EA0079E3C9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 345B207C217965EA0079E3C9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; name = Release; }; 345B207E217965EA0079E3C9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( ../src, ../resources, ../../include, ../../include/lgi/mac/cocoa, - ../../../../../codelib/libpng, - ../../../../../codelib/libpng/build/release, + /opt/local/include, /opt/local/libexec/openssl3/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CFLAGS = "-DNCURSES_UNCTRL_H_incl=1"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DMAC", "-DPOSIX", "-DLGI_COCOA", "-D_DEBUG", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiIde; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; VALID_ARCHS = x86_64; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Debug; }; 345B207F217965EA0079E3C9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( ../src, ../resources, ../../include, ../../include/lgi/mac/cocoa, - ../../../../../codelib/libpng, - ../../../../../codelib/libpng/build/release, + /opt/local/include, /opt/local/libexec/openssl3/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CFLAGS = "-DNCURSES_UNCTRL_H_incl=1"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DMAC", "-DPOSIX", "-DLGI_COCOA", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiIde; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; VALID_ARCHS = x86_64; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 345B2067217965E80079E3C9 /* Build configuration list for PBXProject "LgiIde" */ = { isa = XCConfigurationList; buildConfigurations = ( 345B207B217965EA0079E3C9 /* Debug */, 345B207C217965EA0079E3C9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 345B207D217965EA0079E3C9 /* Build configuration list for PBXNativeTarget "LgiIde" */ = { isa = XCConfigurationList; buildConfigurations = ( 345B207E217965EA0079E3C9 /* Debug */, 345B207F217965EA0079E3C9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 345B2064217965E80079E3C9 /* Project object */; } diff --git a/lvc/src/VcFolder.cpp b/lvc/src/VcFolder.cpp --- a/lvc/src/VcFolder.cpp +++ b/lvc/src/VcFolder.cpp @@ -1,5155 +1,5156 @@ #include "Lvc.h" #include "lgi/common/Combo.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/Json.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/IniFile.h" #include "resdefs.h" #ifndef CALL_MEMBER_FN #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) #endif #define MAX_AUTO_RESIZE_ITEMS 2000 #define PROFILE_FN 0 #if PROFILE_FN #define PROF(s) Prof.Add(s) #else #define PROF(s) #endif class TmpFile : public LFile { int Status; LString Hint; public: TmpFile(const char *hint = NULL) { Status = 0; if (hint) Hint = hint; else Hint = "_lvc"; } LFile &Create() { LFile::Path p(LSP_TEMP); p += Hint; do { char s[256]; sprintf_s(s, sizeof(s), "../%s%i.tmp", Hint.Get(), LRand()); p += s; } while (p.Exists()); Status = LFile::Open(p.GetFull(), O_READWRITE); return *this; } }; bool TerminalAt(LString Path) { #if defined(MAC) const char *Locations[] = { "/System/Applications/Utilities/Terminal.app", "/Applications/Utilities/Terminal.app", NULL }; for (size_t i=0; Locations[i]; i++) { if (LFileExists(Locations[i])) { LString term; term.Printf("%s/Contents/MacOS/Terminal", Locations[i]); return LExecute(term, Path); } } #elif defined(WINDOWS) TCHAR w[MAX_PATH_LEN]; auto r = GetWindowsDirectory(w, CountOf(w)); if (r > 0) { LFile::Path p = LString(w); p += "system32\\cmd.exe"; FileDev->SetCurrentFolder(Path); return LExecute(p); } #elif defined(LINUX) LExecute("gnome-terminal", NULL, Path); #endif return false; } int Ver2Int(LString v) { auto p = v.Split("."); int i = 0; for (auto s : p) { auto Int = s.Int(); if (Int < 256) { i <<= 8; i |= (uint8_t)Int; } else { LAssert(0); return 0; } } return i; } int ToolVersion[VcMax] = {0}; #define DEBUG_READER_THREAD 0 #if DEBUG_READER_THREAD #define LOG_READER(...) printf(__VA_ARGS__) #else #define LOG_READER(...) #endif ReaderThread::ReaderThread(VersionCtrl vcs, LAutoPtr p, LStream *out) : LThread("ReaderThread") { Vcs = vcs; Process = p; Out = out; Result = -1; FilterCount = 0; // We don't start this thread immediately... because the number of threads is scaled to the system // resources, particularly CPU cores. } ReaderThread::~ReaderThread() { Out = NULL; while (!IsExited()) LSleep(1); } const char *HgFilter = "We\'re removing Mercurial support"; const char *CvsKill = "No such file or directory"; int ReaderThread::OnLine(char *s, ssize_t len) { switch (Vcs) { case VcHg: { if (strnistr(s, HgFilter, len)) FilterCount = 4; if (FilterCount > 0) { FilterCount--; return 0; } else if (LString(s, len).Strip().Equals("remote:")) { return 0; } break; } case VcCvs: { if (strnistr(s, CvsKill, len)) return -1; break; } default: break; } return 1; } bool ReaderThread::OnData(char *Buf, ssize_t &r) { LOG_READER("OnData %i\n", (int)r); #if 1 char *Start = Buf; for (char *c = Buf; c < Buf + r;) { bool nl = *c == '\n'; c++; if (nl) { int Result = OnLine(Start, c - Start); if (Result < 0) { // Kill process and exit thread. Process->Kill(); return false; } if (Result == 0) { ssize_t LineLen = c - Start; ssize_t NextLine = c - Buf; ssize_t Remain = r - NextLine; if (Remain > 0) memmove(Start, Buf + NextLine, Remain); r -= LineLen; c = Start; } else Start = c; } } #endif Out->Write(Buf, r); return true; } int ReaderThread::Main() { bool b = Process->Start(true, false); if (!b) { LString s("Process->Start failed.\n"); Out->Write(s.Get(), s.Length(), ErrSubProcessFailed); return ErrSubProcessFailed; } char Buf[1024]; ssize_t r; LOG_READER("%s:%i - starting reader loop, pid=%i\n", _FL, Process->Handle()); while (Process->IsRunning()) { if (Out) { LOG_READER("%s:%i - starting read.\n", _FL); r = Process->Read(Buf, sizeof(Buf)); LOG_READER("%s:%i - read=%i.\n", _FL, (int)r); if (r > 0) { if (!OnData(Buf, r)) return -1; } } else { Process->Kill(); return -1; break; } } LOG_READER("%s:%i - process loop done.\n", _FL); if (Out) { while ((r = Process->Read(Buf, sizeof(Buf))) > 0) OnData(Buf, r); } LOG_READER("%s:%i - loop done.\n", _FL); Result = (int) Process->GetExitValue(); #if _DEBUG if (Result) printf("%s:%i - Process err: %i 0x%x\n", _FL, Result, Result); #endif return Result; } ///////////////////////////////////////////////////////////////////////////////////////////// int VcFolder::CmdMaxThreads = 0; int VcFolder::CmdActiveThreads = 0; void VcFolder::Init(AppPriv *priv) { if (!CmdMaxThreads) CmdMaxThreads = LAppInst->GetCpuCount(); d = priv; Expanded(false); Insert(Tmp = new LTreeItem); Tmp->SetText("Loading..."); LAssert(d != NULL); } VcFolder::VcFolder(AppPriv *priv, const char *uri) { Init(priv); Uri.Set(uri); GetType(); } VcFolder::VcFolder(AppPriv *priv, LXmlTag *t) { Init(priv); Serialize(t, false); } VcFolder::~VcFolder() { if (d->CurFolder == this) d->CurFolder = NULL; Log.DeleteObjects(); } VersionCtrl VcFolder::GetType() { if (Type == VcNone) Type = d->DetectVcs(this); return Type; } bool VcFolder::IsLocal() { return Uri.IsProtocol("file"); } const char *VcFolder::LocalPath() { if (!Uri.IsProtocol("file") || Uri.sPath.IsEmpty()) { LAssert(!"Shouldn't call this if not a file path."); return NULL; } auto c = Uri.sPath.Get(); #ifdef WINDOWS if (*c == '/') c++; #endif return c; } const char *VcFolder::GetText(int Col) { switch (Col) { case 0: { if (Uri.IsFile()) Cache = LocalPath(); else Cache.Printf("%s%s", Uri.sHost.Get(), Uri.sPath.Get()); if (Cmds.Length()) Cache += " (...)"; return Cache; } case 1: { CountCache.Printf("%i/%i", Unpulled, Unpushed); CountCache = CountCache.Replace("-1", "--"); return CountCache; } } return NULL; } bool VcFolder::Serialize(LXmlTag *t, bool Write) { if (Write) t->SetContent(Uri.ToString()); else { LString s = t->GetContent(); bool isUri = s.Find("://") >= 0; if (isUri) Uri.Set(s); else Uri.SetFile(s); } return true; } LXmlTag *VcFolder::Save() { LXmlTag *t = new LXmlTag(OPT_Folder); if (t) Serialize(t, true); return t; } const char *VcFolder::GetVcName() { if (!VcCmd) VcCmd = d->GetVcName(GetType()); return VcCmd; } char VcFolder::GetPathSep() { if (Uri.IsFile()) return DIR_CHAR; return '/'; // FIXME: Assumption is that the remote system is unix based. } bool VcFolder::RunCmd(const char *Args, LoggingType Logging, std::function Callback) { Result Ret; Ret.Code = -1; const char *Exe = GetVcName(); if (!Exe || CmdErrors > 2) return false; if (Uri.IsFile()) { new ProcessCallback(Exe, Args, LocalPath(), Logging == LogNone ? d->Log : NULL, GetTree()->GetWindow(), Callback); } else { LAssert(!"Impl me."); return false; } return true; } #if HAS_LIBSSH SshConnection::LoggingType Convert(LoggingType t) { switch (t) { case LogNormal: case LogSilo: return SshConnection::LogInfo; case LogDebug: return SshConnection::LogDebug; } return SshConnection::LogNone; } #endif bool VcFolder::StartCmd(const char *Args, ParseFn Parser, ParseParams *Params, LoggingType Logging) { const char *Exe = GetVcName(); if (!Exe) return false; if (CmdErrors > 2) return false; if (Uri.IsFile()) { if (d->Log && Logging != LogSilo) d->Log->Print("%s %s\n", Exe, Args); LAutoPtr Process(new LSubProcess(Exe, Args)); if (!Process) return false; Process->SetInitFolder(Params && Params->AltInitPath ? Params->AltInitPath.Get() : LocalPath()); #if 0//def MAC // Mac GUI apps don't share the terminal path, so this overrides that and make it work auto Path = LGetPath(); if (Path.Length()) { LString Tmp = LString(LGI_PATH_SEPARATOR).Join(Path); printf("Tmp='%s'\n", Tmp.Get()); Process->SetEnvironment("PATH", Tmp); } #endif LString::Array Ctx; Ctx.SetFixedLength(false); Ctx.Add(LocalPath()); Ctx.Add(Exe); Ctx.Add(Args); LAutoPtr c(new Cmd(Ctx, Logging, d->Log)); if (!c) return false; c->PostOp = Parser; c->Params.Reset(Params); c->Rd.Reset(new ReaderThread(GetType(), Process, c)); Cmds.Add(c.Release()); } else { #if HAS_LIBSSH auto c = d->GetConnection(Uri.ToString()); if (!c) return false; if (!c->Command(this, Exe, Args, Parser, Params, Convert(Logging))) return false; #endif } Update(); return true; } int LogDateCmp(LListItem *a, LListItem *b, NativeInt Data) { VcCommit *A = dynamic_cast(a); VcCommit *B = dynamic_cast(b); if ((A != NULL) ^ (B != NULL)) { // This handles keeping the "working folder" list item at the top return (A != NULL) - (B != NULL); } // Sort the by date from most recent to least return -A->GetTs().Compare(&B->GetTs()); } void VcFolder::AddGitName(LString Hash, LString Name) { if (!Hash || !Name) { LAssert(!"Param error"); return; } LString Existing = GitNames.Find(Hash); if (Existing) GitNames.Add(Hash, Existing + "," + Name); else GitNames.Add(Hash, Name); } LString VcFolder::GetGitNames(LString Hash) { LString Short = Hash(0, 11); return GitNames.Find(Short); } bool VcFolder::ParseBranches(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { LString::Array a = s.SplitDelimit("\r\n"); for (auto &l: a) { LString::Array c; char *s = l.Get(); while (*s && IsWhite(*s)) s++; bool IsCur = *s == '*'; if (IsCur) s++; while (*s && IsWhite(*s)) s++; if (*s == '(') { s++; auto e = strchr(s, ')'); if (e) { c.New().Set(s, e - s); e++; c += LString(e).SplitDelimit(" \t"); } } else { c = LString(s).SplitDelimit(" \t"); } if (c.Length() < 1) { d->Log->Print("%s:%i - Too few parts in line '%s'\n", _FL, l.Get()); continue; } if (IsCur) SetCurrentBranch(c[0]); AddGitName(c[1], c[0]); Branches.Add(c[0], new VcBranch(c[0], c[1])); } break; } case VcHg: { auto a = s.SplitDelimit("\r\n"); for (auto b: a) { if (b.Find("inactive") > 0) continue; auto name = b(0, 28).Strip(); auto refs = b(28, -1).SplitDelimit()[0].SplitDelimit(":"); auto branch = Branches.Find(name); if (branch) branch->Hash = refs.Last(); else Branches.Add(name, new VcBranch(name, refs.Last())); } if (Params && Params->Str.Equals("CountToTip")) CountToTip(); break; } default: { break; } } IsBranches = Result ? StatusError : StatusNone; OnBranchesChange(); return false; } void VcFolder::GetRemoteUrl(std::function Callback) { LAutoPtr p(new ParseParams); p->Callback = Callback; switch (GetType()) { case VcGit: { StartCmd("config --get remote.origin.url", NULL, p.Release()); break; } case VcSvn: { StartCmd("info --show-item=url", NULL, p.Release()); break; } case VcHg: { StartCmd("paths default", NULL, p.Release()); break; } default: break; } } void VcFolder::SelectCommit(LWindow *Parent, LString Commit, LString Path) { bool requireFullMatch = true; if (GetType() == VcGit) requireFullMatch = false; // This function find the given commit and selects it such that the diffs are displayed in the file list VcCommit *ExistingMatch = NULL; for (auto c: Log) { char *rev = c->GetRev(); bool match = requireFullMatch ? Commit.Equals(rev) : Strstr(rev, Commit.Get()) != NULL; if (match) { ExistingMatch = c; break; } } FileToSelect = Path; if (ExistingMatch) { ExistingMatch->Select(true); } else { // If the commit isn't there, it's likely that the log item limit was reached before the commit was // found. In which case we should go get just that commit and add it: d->Files->Empty(); // Diff just that ref: LString a; switch (GetType()) { case VcGit: { a.Printf("diff %s~ %s", Commit.Get(), Commit.Get()); StartCmd(a, &VcFolder::ParseSelectCommit); break; } case VcHg: { a.Printf("log -p -r %s", Commit.Get()); StartCmd(a, &VcFolder::ParseSelectCommit); break; } default: { NoImplementation(_FL); break; } } // if (Parent) LgiMsg(Parent, "The commit '%s' wasn't found", AppName, MB_OK, Commit.Get()); } } bool VcFolder::ParseSelectCommit(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: case VcHg: case VcSvn: case VcCvs: { ParseDiff(Result, s, Params); break; } default: { NoImplementation(_FL); break; } } return false; } void VcFolder::OnBranchesChange() { auto *w = d->Tree->GetWindow(); if (!w || !LTreeItem::Select()) return; if (Branches.Length()) { // Set the colours up LString Default; for (auto b: Branches) { if (!stricmp(b.key, "default") || !stricmp(b.key, "trunk")) Default = b.key; /* else printf("Other=%s\n", b.key); */ } int Idx = 1; for (auto b: Branches) { if (!b.value->Colour.IsValid()) { if (Default && !stricmp(b.key, Default)) b.value->Colour = GetPaletteColour(0); else b.value->Colour = GetPaletteColour(Idx++); } } } UpdateBranchUi(); } void VcFolder::DefaultFields() { if (Fields.Length() == 0) { switch (GetType()) { case VcHg: { Fields.Add(LGraph); Fields.Add(LIndex); Fields.Add(LRevision); Fields.Add(LBranch); Fields.Add(LAuthor); Fields.Add(LTime); Fields.Add(LMessageTxt); break; } case VcGit: { Fields.Add(LGraph); Fields.Add(LRevision); Fields.Add(LBranch); Fields.Add(LAuthor); Fields.Add(LTime); Fields.Add(LMessageTxt); break; } default: { Fields.Add(LGraph); Fields.Add(LRevision); Fields.Add(LAuthor); Fields.Add(LTime); Fields.Add(LMessageTxt); break; } } } } int VcFolder::IndexOfCommitField(CommitField fld) { return (int)Fields.IndexOf(fld); } void VcFolder::UpdateColumns(LList *lst) { if (!lst) lst = d->Commits; lst->EmptyColumns(); for (auto c: Fields) { switch (c) { case LGraph: lst->AddColumn("---", 60); break; case LIndex: lst->AddColumn("Index", 60); break; case LBranch: lst->AddColumn("Branch", 60); break; case LRevision: lst->AddColumn("Revision", 60); break; case LAuthor: lst->AddColumn("Author", 240); break; case LTime: lst->AddColumn("Date", 130); break; case LMessageTxt: lst->AddColumn("Message", 700); break; default: LAssert(0); break; } } } void VcFolder::FilterCurrentFiles() { LArray All; d->Files->GetAll(All); // Update the display property for (auto i: All) { auto fn = i->GetText(COL_FILENAME); bool vis = !d->FileFilter || Stristr(fn, d->FileFilter.Get()); i->GetCss(true)->Display(vis ? LCss::DispBlock : LCss::DispNone); // LgiTrace("Filter '%s' by '%s' = %i\n", fn, d->FileFilter.Get(), vis); } d->Files->Sort(0); d->Files->UpdateAllItems(); d->Files->ResizeColumnsToContent(); } void VcFolder::UpdateAuthorUi() { if (AuthorLocal) d->Wnd()->SetCtrlName(IDC_AUTHOR, AuthorLocal.ToString()); else if (AuthorGlobal) d->Wnd()->SetCtrlName(IDC_AUTHOR, AuthorGlobal.ToString()); } LString VcFolder::GetConfigFile(bool local) { switch (GetType()) { case VcHg: { LFile::Path p; if (local) { p = LFile::Path(LocalPath()) / ".hg" / "hgrc"; } else { p = LFile::Path(LSP_HOME) / ".hgrc"; if (!p.Exists()) p = LFile::Path(LSP_HOME) / "mercurial.ini"; } d->Log->Print("%s: %i\n", p.GetFull().Get(), p.Exists()); if (p.Exists()) return p.GetFull(); break; } default: { NoImplementation(_FL); return false; } } return LString(); } bool VcFolder::GetAuthor(bool local, std::function callback) { auto scope = local ? "--local" : "--global"; auto target = local ? &AuthorLocal : &AuthorGlobal; switch (GetType()) { case VcGit: { if (target->InProgress) return true; auto params = new ParseParams; params->Callback = [this, callback, target](auto code, auto s) { for (auto ln: s.Strip().SplitDelimit("\r\n")) { auto parts = ln.SplitDelimit("=", 1); if (parts.Length() == 2) { if (parts[0].Equals("user.email")) target->email = parts[1]; else if (parts[0].Equals("user.name")) target->name = parts[1]; } } target->InProgress = false; if (callback) callback(target->name, target->email); }; auto args = LString::Fmt("-P config -l %s", scope); target->InProgress = StartCmd(args, NULL, params); break; } case VcHg: { auto config = GetConfigFile(local); if (!config) return false; LIniFile data(config); auto author = data.Get("ui", "username"); auto start = author.Find("<"); auto end = author.Find(">", start); if (start >= 0 && end >= start) { target->name = author(0, start).Strip(); target->email = author(start + 1, end).Strip(); } IsGettingAuthor = false; callback(target->name, target->email); break; } default: { NoImplementation(_FL); return false; } } return true; } bool VcFolder::SetAuthor(bool local, LString name, LString email) { auto scope = local ? "--local" : "--global"; auto target = local ? &AuthorLocal : &AuthorGlobal; target->name = name; target->email = email; switch (GetType()) { case VcGit: { auto args = LString::Fmt("config %s user.name \"%s\"", scope, name.Get()); StartCmd(args); args = LString::Fmt("config %s user.email \"%s\"", scope, email.Get()); StartCmd(args); break; } case VcHg: { auto config = GetConfigFile(local); if (!config) return false; LString author; author.Printf("%s <%s>", name.Get(), email.Get()); LIniFile data(config); data.Set("ui", "username", author); return data.Write(); } default: { NoImplementation(_FL); return false; } } return true; } void VcFolder::Select(bool b) { #if PROFILE_FN LProfile Prof("Select"); #endif if (!b) { auto *w = d->Tree->GetWindow(); w->SetCtrlName(IDC_BRANCH, NULL); } PROF("Parent.Select"); LTreeItem::Select(b); if (b) { if (Uri.IsFile() && !LDirExists(LocalPath())) return; PROF("DefaultFields"); DefaultFields(); if (!AuthorLocal) GetAuthor(true, [this](auto name, auto email) { UpdateAuthorUi(); }); if (!AuthorGlobal) GetAuthor(false, [this](auto name, auto email) { UpdateAuthorUi(); }); UpdateAuthorUi(); PROF("Type Change"); if (GetType() != d->PrevType) { d->PrevType = GetType(); UpdateColumns(); } PROF("UpdateCommitList"); if ((Log.Length() == 0 || CommitListDirty) && !IsLogging) { switch (GetType()) { case VcGit: { LVariant Limit; d->Opts.GetValue("git-limit", Limit); LString cmd = "rev-list --all --header --timestamp --author-date-order", s; if (Limit.CastInt32() > 0) { s.Printf(" -n %i", Limit.CastInt32()); cmd += s; } IsLogging = StartCmd(cmd, &VcFolder::ParseRevList); break; } case VcSvn: { LVariant Limit; d->Opts.GetValue("svn-limit", Limit); if (CommitListDirty) { IsLogging = StartCmd("up", &VcFolder::ParsePull, new ParseParams("log")); break; } LString s; if (Limit.CastInt32() > 0) s.Printf("log --limit %i", Limit.CastInt32()); else s = "log"; IsLogging = StartCmd(s, &VcFolder::ParseLog); break; } case VcHg: { IsLogging = StartCmd("log", &VcFolder::ParseLog); StartCmd("resolve -l", &VcFolder::ParseResolveList); break; } case VcPending: { break; } default: { IsLogging = StartCmd("log", &VcFolder::ParseLog); break; } } CommitListDirty = false; } PROF("GetBranches"); if (GetBranches()) OnBranchesChange(); if (d->CurFolder != this) { PROF("RemoveAll"); d->CurFolder = this; d->Commits->RemoveAll(); } PROF("Uncommit"); if (!Uncommit) Uncommit.Reset(new UncommitedItem(d)); d->Commits->Insert(Uncommit, 0); PROF("Log Loop"); int64 CurRev = Atoi(CurrentCommit.Get()); List Ls; for (auto l: Log) { if (CurrentCommit && l->GetRev()) { switch (GetType()) { case VcSvn: { int64 LogRev = Atoi(l->GetRev()); if (CurRev >= 0 && CurRev >= LogRev) { CurRev = -1; l->SetCurrent(true); } else { l->SetCurrent(false); } break; } default: l->SetCurrent(!_stricmp(CurrentCommit, l->GetRev())); break; } } LList *CurOwner = l->GetList(); if (!CurOwner) Ls.Insert(l); } PROF("Ls Ins"); d->Commits->Insert(Ls); if (d->Resort >= 0) { PROF("Resort"); d->Commits->Sort(LstCmp, d->Resort); d->Resort = -1; } PROF("ColSizing"); if (d->Commits->Length() > MAX_AUTO_RESIZE_ITEMS) { int i = 0; if (GetType() == VcHg && d->Commits->GetColumns() >= 7) { d->Commits->ColumnAt(i++)->Width(60); // LGraph d->Commits->ColumnAt(i++)->Width(40); // LIndex d->Commits->ColumnAt(i++)->Width(100); // LRevision d->Commits->ColumnAt(i++)->Width(60); // LBranch d->Commits->ColumnAt(i++)->Width(240); // LAuthor d->Commits->ColumnAt(i++)->Width(130); // LTimeStamp d->Commits->ColumnAt(i++)->Width(400); // LMessage } else if (d->Commits->GetColumns() >= 5) { d->Commits->ColumnAt(i++)->Width(40); // LGraph d->Commits->ColumnAt(i++)->Width(270); // LRevision d->Commits->ColumnAt(i++)->Width(240); // LAuthor d->Commits->ColumnAt(i++)->Width(130); // LTimeStamp d->Commits->ColumnAt(i++)->Width(400); // LMessage } } else d->Commits->ResizeColumnsToContent(); PROF("UpdateAll"); d->Commits->UpdateAllItems(); PROF("GetCur"); GetCurrentRevision(); } } int CommitRevCmp(VcCommit **a, VcCommit **b) { int64 arev = Atoi((*a)->GetRev()); int64 brev = Atoi((*b)->GetRev()); int64 diff = (int64)brev - arev; if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } int CommitIndexCmp(VcCommit **a, VcCommit **b) { auto ai = (*a)->GetIndex(); auto bi = (*b)->GetIndex(); auto diff = (int64)bi - ai; if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } int CommitDateCmp(VcCommit **a, VcCommit **b) { LTimeStamp ats, bts; (*a)->GetTs().Get(ats); (*b)->GetTs().Get(bts); int64 diff = (int64)bts.Get() - ats.Get(); if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } void VcFolder::GetCurrentRevision(ParseParams *Params) { if (CurrentCommit || IsIdent != StatusNone) return; switch (GetType()) { case VcGit: if (StartCmd("rev-parse HEAD", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcSvn: if (StartCmd("info", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcHg: if (StartCmd("id -i -n", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcCvs: break; default: break; } } bool VcFolder::GetBranches(ParseParams *Params) { if (Branches.Length() > 0 || IsBranches != StatusNone) return true; switch (GetType()) { case VcGit: if (StartCmd("-P branch -v", &VcFolder::ParseBranches, Params)) IsBranches = StatusActive; break; case VcSvn: Branches.Add("trunk", new VcBranch("trunk")); OnBranchesChange(); break; case VcHg: { if (StartCmd("branches", &VcFolder::ParseBranches, Params)) IsBranches = StatusActive; auto p = new ParseParams; p->Callback = [this](auto code, auto str) { SetCurrentBranch(str.Strip()); }; StartCmd("branch", NULL, p); break; } case VcCvs: break; default: break; } return false; } bool VcFolder::ParseRevList(int Result, LString s, ParseParams *Params) { Log.DeleteObjects(); int Errors = 0; switch (GetType()) { case VcGit: { LString::Array Commits; Commits.SetFixedLength(false); // Split on the NULL chars... char *c = s.Get(); char *e = c + s.Length(); while (c < e) { char *nul = c; while (nul < e && *nul) nul++; if (nul <= c) break; Commits.New().Set(c, nul-c); if (nul >= e) break; c = nul + 1; } for (auto Commit: Commits) { LAutoPtr Rev(new VcCommit(d, this)); if (Rev->GitParse(Commit, true)) { Log.Add(Rev.Release()); } else { // LAssert(!"Parse failed."); LgiTrace("%s:%i - Failed:\n%s\n\n", _FL, Commit.Get()); Errors++; } } LinkParents(); break; } default: LAssert(!"Impl me."); break; } IsLogging = false; return Errors == 0; } LString VcFolder::GetFilePart(const char *uri) { LUri u(uri); LString File = u.IsFile() ? u.DecodeStr(u.LocalPath()) : u.sPath(Uri.sPath.Length(), -1).LStrip("/"); return File; } void VcFolder::ClearLog() { Uncommit.Reset(); Log.DeleteObjects(); } void VcFolder::LogFilter(const char *Filter) { if (!Filter) { LAssert(!"No filter."); return; } switch (GetType()) { case VcGit: { // See if 'Filter' is a commit id? LString args; args.Printf("-P show %s", Filter); ParseParams *params = new ParseParams; params->Callback = [this, Filter=LString(Filter)](auto code, auto str) { ClearLog(); if (code == 0 && str.Find(Filter) >= 0) { // Found the commit... d->Commits->Empty(); CurrentCommit.Empty(); ParseLog(code, str, NULL); d->Commits->Insert(Log); } else { // Not a commit ref...? LString args; args.Printf("log --grep \"%s\"", Filter.Get()); IsLogging = StartCmd(args, &VcFolder::ParseLog); } }; StartCmd(args, NULL, params); break; } default: { NoImplementation(_FL); break; } } } void VcFolder::LogFile(const char *uri) { LString Args; if (IsLogging) { d->Log->Print("%s:%i - already logging.\n", _FL); return; } const char *Page = ""; switch (GetType()) { case VcGit: Page = "-P "; // fall through case VcSvn: case VcHg: { FileToSelect = GetFilePart(uri); if (IsLocal() && !LFileExists(FileToSelect)) { LFile::Path Abs(LocalPath()); Abs += FileToSelect; if (Abs.Exists()) FileToSelect = Abs; } ParseParams *Params = new ParseParams(uri); Args.Printf("%slog \"%s\"", Page, FileToSelect.Get()); IsLogging = StartCmd(Args, &VcFolder::ParseLog, Params, LogNormal); break; } default: NoImplementation(_FL); break; } } VcLeaf *VcFolder::FindLeaf(const char *Path, bool OpenTree) { VcLeaf *r = NULL; if (OpenTree) DoExpand(); for (auto n = GetChild(); !r && n; n = n->GetNext()) { auto l = dynamic_cast(n); if (l) r = l->FindLeaf(Path, OpenTree); } return r; } bool VcFolder::ParseLog(int Result, LString s, ParseParams *Params) { int Skipped = 0, Errors = 0; bool LoggingFile = Params ? Params->Str != NULL : false; VcLeaf *File = LoggingFile ? FindLeaf(Params->Str, true) : NULL; // This may be NULL even if we are logging a file... LArray *Out, BrowseLog; if (File) Out = &File->Log; else if (LoggingFile) Out = &BrowseLog; else Out = &Log; LHashTbl, VcCommit*> Map; for (auto pc: *Out) Map.Add(pc->GetRev(), pc); if (File) { for (auto Leaf = File; Leaf; Leaf = dynamic_cast(Leaf->GetParent())) Leaf->OnExpand(true); File->Select(true); File->ScrollTo(); } switch (GetType()) { case VcGit: { LString::Array c; c.SetFixedLength(false); char *prev = s.Get(); #if 0 LFile::Path outPath("~/code/dump.txt"); LFile out(outPath.Absolute(), O_WRITE); out.Write(s); #endif if (!s) { OnCmdError(s, "No output from command."); return false; } char *i = s.Get(); while (*i) { if (!strnicmp(i, "commit ", 7)) { if (i > prev) { c.New().Set(prev, i - prev); // LgiTrace("commit=%i\n", (int)(i - prev)); } prev = i; } while (*i) { if (*i++ == '\n') break; } } if (prev && i > prev) { // Last one... c.New().Set(prev, i - prev); } for (auto txt: c) { LAutoPtr Rev(new VcCommit(d, this)); if (Rev->GitParse(txt, false)) { if (!Map.Find(Rev->GetRev())) Out->Add(Rev.Release()); else Skipped++; } else { LgiTrace("%s:%i - Failed:\n%s\n\n", _FL, txt.Get()); Errors++; } } Out->Sort(CommitDateCmp); break; } case VcSvn: { LString::Array c = s.Split("------------------------------------------------------------------------"); for (unsigned i=0; i Rev(new VcCommit(d, this)); LString Raw = c[i].Strip(); if (Rev->SvnParse(Raw)) { if (File || !Map.Find(Rev->GetRev())) Out->Add(Rev.Release()); else Skipped++; } else if (Raw) { OnCmdError(Raw, "ParseLog Failed"); Errors++; } } Out->Sort(CommitRevCmp); break; } case VcHg: { LString::Array c = s.Split("\n\n"); LHashTbl, VcCommit*> Idx; for (auto &Commit: c) { LAutoPtr Rev(new VcCommit(d, this)); if (Rev->HgParse(Commit)) { auto Existing = File ? NULL : Map.Find(Rev->GetRev()); if (!Existing) Out->Add(Existing = Rev.Release()); if (Existing->GetIndex() >= 0) Idx.Add(Existing->GetIndex(), Existing); } } if (!File) { // Patch all the trivial parents... for (auto c: Log) { if (c->GetParents()->Length() > 0) continue; auto CIdx = c->GetIndex(); if (CIdx <= 0) continue; auto Par = Idx.Find(CIdx - 1); if (Par) c->GetParents()->Add(Par->GetRev()); } } Out->Sort(CommitIndexCmp); if (!File) LinkParents(); d->Resort = 1; break; } case VcCvs: { if (Result) { OnCmdError(s, "Cvs command failed."); break; } LHashTbl, VcCommit*> Map; LString::Array c = s.Split("============================================================================="); for (auto &Commit: c) { if (Commit.Strip().Length()) { LString Head, File; LString::Array Versions = Commit.Split("----------------------------"); LString::Array Lines = Versions[0].SplitDelimit("\r\n"); for (auto &Line: Lines) { LString::Array p = Line.Split(":", 1); if (p.Length() == 2) { // LgiTrace("Line: %s\n", Line->Get()); LString Var = p[0].Strip().Lower(); LString Val = p[1].Strip(); if (Var.Equals("branch")) { if (Val.Length()) Branches.Add(Val, new VcBranch(Val)); } else if (Var.Equals("head")) { Head = Val; } else if (Var.Equals("rcs file")) { LString::Array f = Val.SplitDelimit(","); File = f.First(); } } } // LgiTrace("%s\n", Commit->Get()); for (unsigned i=1; i= 3) { LString Ver = Lines[0].Split(" ").Last(); LString::Array a = Lines[1].SplitDelimit(";"); LString Date = a[0].Split(":", 1).Last().Strip(); LString Author = a[1].Split(":", 1).Last().Strip(); LString Id = a[2].Split(":", 1).Last().Strip(); LString Msg = Lines[2]; LDateTime Dt; if (Dt.Parse(Date)) { LTimeStamp Ts; if (Dt.Get(Ts)) { VcCommit *Cc = Map.Find(Ts.Get()); if (!Cc) { Map.Add(Ts.Get(), Cc = new VcCommit(d, this)); Out->Add(Cc); Cc->CvsParse(Dt, Author, Msg); } Cc->Files.Add(File.Get()); } else LAssert(!"NO ts for date."); } else LAssert(!"Date parsing failed."); } } } } break; } default: LAssert(!"Impl me."); break; } if (File) { File->ShowLog(); } else if (LoggingFile) { if (auto ui = new BrowseUi(BrowseUi::TLog, d, this, Params->Str)) ui->ParseLog(BrowseLog, s); } // LgiTrace("%s:%i - ParseLog: Skip=%i, Error=%i\n", _FL, Skipped, Errors); IsLogging = false; return !Result; } void VcFolder::LinkParents() { #if PROFILE_FN LProfile Prof("LinkParents"); #endif LHashTbl,VcCommit*> Map; // Index all the commits int i = 0; for (auto c:Log) { c->Idx = i++; c->NodeIdx = -1; Map.Add(c->GetRev(), c); } // Create all the edges... PROF("Create edges."); for (auto c:Log) { auto *Par = c->GetParents(); for (auto &pRev : *Par) { auto *p = Map.Find(pRev); if (p) new VcEdge(p, c); #if 0 else return; #endif } } // Map the edges to positions PROF("Map edges."); typedef LArray EdgeArr; LArray Active; for (auto c:Log) { for (unsigned i=0; c->NodeIdx<0 && iParent == c) { c->NodeIdx = i; break; } } } // Add starting edges to active set for (auto e:c->Edges) { if (e->Child == c) { if (c->NodeIdx < 0) c->NodeIdx = (int)Active.Length(); e->Idx = c->NodeIdx; c->Pos.Add(e, e->Idx); Active[e->Idx].Add(e); } } // Now for all active edges... assign positions for (unsigned i=0; iLength(); n++) { LAssert(Active.PtrCheck(Edges)); VcEdge *e = (*Edges)[n]; if (c == e->Child || c == e->Parent) { LAssert(c->NodeIdx >= 0); c->Pos.Add(e, c->NodeIdx); } else { // May need to untangle edges with different parents here bool Diff = false; for (auto edge: *Edges) { if (edge != e && edge->Child != c && edge->Parent != e->Parent) { Diff = true; break; } } if (Diff) { int NewIndex = -1; // Look through existing indexes for a parent match for (unsigned ii=0; iiParent? bool Match = true; for (auto ee: Active[ii]) { if (ee->Parent != e->Parent) { Match = false; break; } } if (Match) NewIndex = ii; } if (NewIndex < 0) // Create new index for this parent NewIndex = (int)Active.Length(); Edges->Delete(e); auto &NewEdges = Active[NewIndex]; NewEdges.Add(e); Edges = &Active[i]; // The 'Add' above can invalidate the object 'Edges' refers to e->Idx = NewIndex; c->Pos.Add(e, NewIndex); n--; } else { LAssert(e->Idx == i); c->Pos.Add(e, i); } } } } // Process terminating edges for (auto e: c->Edges) { if (e->Parent == c) { if (e->Idx < 0) { // This happens with out of order commits..? continue; } int i = e->Idx; if (c->NodeIdx < 0) c->NodeIdx = i; if (Active[i].HasItem(e)) Active[i].Delete(e); else LgiTrace("%s:%i - Warning: Active doesn't have 'e'.\n", _FL); } } // Collapse any empty active columns for (unsigned i=0; iIdx > 0); edge->Idx--; c->Pos.Add(edge, edge->Idx); } } i--; } } } } void VcFolder::UpdateBranchUi() { auto w = d->Wnd(); DropDownBtn *dd; if (w->GetViewById(IDC_BRANCH_DROPDOWN, dd)) { LString::Array a; for (auto b: Branches) a.Add(b.key); dd->SetList(IDC_BRANCH, a); } LViewI *b; if (Branches.Length() > 0 && w->GetViewById(IDC_BRANCH, b)) { if (CurrentBranch) { b->Name(CurrentBranch); } else { auto it = Branches.begin(); if (it != Branches.end()) b->Name((*it).key); } } LCombo *Cbo; if (w->GetViewById(IDC_BRANCHES, Cbo)) { Cbo->Empty(); int64 select = -1; for (auto b: Branches) { if (CurrentBranch && CurrentBranch == b.key) select = Cbo->Length(); Cbo->Insert(b.key); } if (select >= 0) Cbo->Value(select); Cbo->SendNotify(LNotifyTableLayoutRefresh); // LgiTrace("%s:%i - Branches len=%i->%i\n", _FL, (int)Branches.Length(), (int)Cbo->Length()); } } VcFile *AppPriv::FindFile(const char *Path) { if (!Path) return NULL; LArray files; if (Files->GetAll(files)) { LString p = Path; p = p.Replace(DIR_STR, "/"); for (auto f : files) { auto Fn = f->GetFileName(); if (p.Equals(Fn)) return f; } } return NULL; } VcFile *VcFolder::FindFile(const char *Path) { return d->FindFile(Path); } void VcFolder::NoImplementation(const char* file, int line) { LString s; s.Printf("%s, uri=%s, type=%s (%s:%i)", LLoadString(IDS_ERR_NO_IMPL_FOR_TYPE), Uri.ToString().Get(), toString(GetType()), file, line); OnCmdError(LString(), s); } void VcFolder::OnCmdError(LString Output, const char *Msg) { if (!CmdErrors) { if (Output.Length()) d->Log->Write(Output, Output.Length()); auto vc_name = GetVcName(); if (vc_name) { LString::Array a = GetProgramsInPath(GetVcName()); d->Log->Print("'%s' executables in the path:\n", GetVcName()); for (auto Bin : a) d->Log->Print(" %s\n", Bin.Get()); } else if (Msg) { d->Log->Print("%s\n", Msg); } } CmdErrors++; d->Tabs->Value(1); GetCss(true)->Color(LColour::Red); Update(); } void VcFolder::ClearError() { GetCss(true)->Color(LCss::ColorInherit); } bool VcFolder::ParseInfo(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: case VcHg: { auto p = s.Strip().SplitDelimit(); CurrentCommit = p[0].Strip(" \t\r\n+"); if (p.Length() > 1) CurrentCommitIdx = p[1].Int(); else CurrentCommitIdx = -1; if (Params && Params->Str.Equals("CountToTip")) CountToTip(); break; } case VcSvn: { if (s.Find("client is too old") >= 0) { OnCmdError(s, "Client too old"); break; } LString::Array c = s.Split("\n"); for (unsigned i=0; iStr.Equals("Branch")) SetCurrentBranch(NewRev); else CurrentCommit = NewRev; } NewRev.Empty(); IsUpdate = false; return true; } bool VcFolder::ParseWorking(int Result, LString s, ParseParams *Params) { IsListingWorking = false; switch (GetType()) { case VcSvn: case VcHg: { ParseParams Local; if (!Params) Params = &Local; Params->IsWorking = true; ParseStatus(Result, s, Params); break; } case VcCvs: { bool Untracked = d->IsMenuChecked(IDM_UNTRACKED); if (Untracked) { auto Lines = s.SplitDelimit("\n"); for (auto Ln: Lines) { auto p = Ln.SplitDelimit(" \t", 1); if (p.Length() > 1) { auto f = new VcFile(d, this, LString(), true); f->SetText(p[0], COL_STATE); f->SetText(p[1], COL_FILENAME); f->GetStatus(); d->Files->Insert(f); } } } // else fall thru } default: { ParseDiffs(s, LString(), true); break; } } FilterCurrentFiles(); d->Files->ResizeColumnsToContent(); if (GetType() == VcSvn) { Unpushed = d->Files->Length() > 0 ? 1 : 0; Update(); } return false; } void VcFolder::DiffRange(const char *FromRev, const char *ToRev) { if (!FromRev || !ToRev) return; switch (GetType()) { case VcSvn: { ParseParams *p = new ParseParams; p->IsWorking = false; p->Str = LString(FromRev) + ":" + ToRev; LString a; a.Printf("diff -r%s:%s", FromRev, ToRev); StartCmd(a, &VcFolder::ParseDiff, p); break; } case VcGit: { ParseParams *p = new ParseParams; p->IsWorking = false; p->Str = LString(FromRev) + ":" + ToRev; LString a; a.Printf("-P diff %s..%s", FromRev, ToRev); StartCmd(a, &VcFolder::ParseDiff, p); break; } case VcCvs: case VcHg: default: LAssert(!"Impl me."); break; } } bool VcFolder::ParseDiff(int Result, LString s, ParseParams *Params) { if (Params) ParseDiffs(s, Params->Str, Params->IsWorking); else ParseDiffs(s, LString(), true); return false; } void VcFolder::Diff(VcFile *file) { auto Fn = file->GetFileName(); if (!Fn || !Stricmp(Fn, ".") || !Stricmp(Fn, "..")) return; const char *Prefix = ""; switch (GetType()) { case VcGit: Prefix = "-P "; // fall through case VcHg: { LString a; auto rev = file->GetRevision(); if (rev) a.Printf("%sdiff %s \"%s\"", Prefix, rev, Fn); else a.Printf("%sdiff \"%s\"", Prefix, Fn); StartCmd(a, &VcFolder::ParseDiff); break; } case VcSvn: { LString a; if (file->GetRevision()) a.Printf("diff -r %s \"%s\"", file->GetRevision(), Fn); else a.Printf("diff \"%s\"", Fn); StartCmd(a, &VcFolder::ParseDiff); break; } case VcCvs: break; default: LAssert(!"Impl me."); break; } } void VcFolder::InsertFiles(List &files) { d->Files->Insert(files); if (FileToSelect) { LListItem *scroll = NULL; for (auto f: files) { // Convert to an absolute path: bool match = false; auto relPath = f->GetText(COL_FILENAME); if (IsLocal()) { LFile::Path p(LocalPath()); p += relPath; match = p.GetFull().Equals(FileToSelect); } else { match = !Stricmp(FileToSelect.Get(), relPath); } f->Select(match); if (match) scroll = f; } if (scroll) scroll->ScrollTo(); } } bool VcFolder::ParseDiffs(LString s, LString Rev, bool IsWorking) { LAssert(IsWorking || Rev.Get() != NULL); switch (GetType()) { case VcGit: { List Files; LString::Array a = s.Split("\n"); LString Diff; VcFile *f = NULL; for (unsigned i=0; iSetDiff(Diff); Diff.Empty(); auto Bits = a[i].SplitDelimit(); LString Fn, State = "M"; if (Bits[1].Equals("--cc")) { Fn = Bits.Last(); State = "C"; } else Fn = Bits.Last()(2,-1); // LgiTrace("%s\n", a[i].Get()); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(State, COL_STATE); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); f->GetStatus(); Files.Insert(f); } else if (!_strnicmp(Ln, "new file", 8)) { if (f) f->SetText("A", COL_STATE); } else if (!_strnicmp(Ln, "deleted file", 12)) { if (f) f->SetText("D", COL_STATE); } else if (!_strnicmp(Ln, "index", 5) || !_strnicmp(Ln, "commit", 6) || !_strnicmp(Ln, "Author:", 7) || !_strnicmp(Ln, "Date:", 5) || !_strnicmp(Ln, "+++", 3) || !_strnicmp(Ln, "---", 3)) { // Ignore } else { if (Diff) Diff += "\n"; Diff += a[i]; } } if (f && Diff) { f->SetDiff(Diff); Diff.Empty(); } InsertFiles(Files); break; } case VcHg: { LString Sep("\n"); LString::Array a = s.Split(Sep); LString::Array Diffs; VcFile *f = NULL; List Files; LProgressDlg Prog(GetTree(), 1000); Prog.SetDescription("Reading diff lines..."); Prog.SetRange(a.Length()); // Prog.SetYieldTime(300); for (unsigned i=0; iSetDiff(Sep.Join(Diffs)); Diffs.Empty(); auto MainParts = a[i].Split(" -r "); auto FileParts = MainParts.Last().Split(" ",1); LString Fn = FileParts.Last(); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); // f->SetText(Status, COL_STATE); Files.Insert(f); } else if (!_strnicmp(Ln, "index", 5) || !_strnicmp(Ln, "commit", 6) || !_strnicmp(Ln, "Author:", 7) || !_strnicmp(Ln, "Date:", 5) || !_strnicmp(Ln, "+++", 3) || !_strnicmp(Ln, "---", 3)) { // Ignore } else { Diffs.Add(a[i]); } Prog.Value(i); if (Prog.IsCancelled()) break; } if (f && Diffs.Length()) { f->SetDiff(Sep.Join(Diffs)); Diffs.Empty(); } InsertFiles(Files); break; } case VcSvn: { List Files; LString::Array a = s.Replace("\r").Split("\n"); LString Diff; VcFile *f = NULL; bool InPreamble = false; bool InDiff = false; for (unsigned i=0; iSetDiff(Diff); f->Select(false); } Diff.Empty(); InDiff = false; InPreamble = false; LString Fn = a[i].Split(":", 1).Last().Strip(); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); f->SetText("M", COL_STATE); f->GetStatus(); Files.Insert(f); } else if (!_strnicmp(Ln, "------", 6)) { InPreamble = !InPreamble; } else if (!_strnicmp(Ln, "======", 6)) { InPreamble = false; InDiff = true; } else if (InDiff) { if (!strncmp(Ln, "--- ", 4) || !strncmp(Ln, "+++ ", 4)) { } else { if (Diff) Diff += "\n"; Diff += a[i]; } } } InsertFiles(Files); if (f && Diff) { f->SetDiff(Diff); Diff.Empty(); } break; } case VcCvs: { break; } default: { LAssert(!"Impl me."); break; } } FilterCurrentFiles(); return true; } bool VcFolder::ParseFiles(int Result, LString s, ParseParams *Params) { d->ClearFiles(); ParseDiffs(s, Params->Str, false); IsFilesCmd = false; FilterCurrentFiles(); return false; } #if HAS_LIBSSH void VcFolder::OnSshCmd(SshParams *p) { if (!p || !p->f) { LAssert(!"Param error."); return; } LString s = p->Output; int Result = p->ExitCode; if (Result == ErrSubProcessFailed) { CmdErrors++; } else if (p->Parser) { bool Reselect = CALL_MEMBER_FN(*this, p->Parser)(Result, s, p->Params); if (Reselect) { if (LTreeItem::Select()) Select(true); } } if (p->Params && p->Params->Callback) { p->Params->Callback(Result, s); } } #endif void VcFolder::OnPulse() { bool Reselect = false, CmdsChanged = false; static bool Processing = false; if (!Processing) { Processing = true; // Lock out processing, if it puts up a dialog or something... // bad things happen if we try and re-process something. // printf("Cmds.Len=%i\n", (int)Cmds.Length()); for (unsigned i=0; iRd->GetState()); if (c->Rd->GetState() == LThread::THREAD_INIT) { if (CmdActiveThreads < CmdMaxThreads) { c->Rd->Run(); CmdActiveThreads++; // printf("CmdActiveThreads++ = %i\n", CmdActiveThreads); } // else printf("Too many active threads.\n"); } else if (c->Rd->IsExited()) { CmdActiveThreads--; // printf("CmdActiveThreads-- = %i\n", CmdActiveThreads); LString s = c->GetBuf(); int Result = c->Rd->ExitCode(); if (Result == ErrSubProcessFailed) { if (!CmdErrors) d->Log->Print("Error: Can't run '%s'\n", GetVcName()); CmdErrors++; } else if (c->PostOp) { if (s.Length() == 18 && s.Equals("LSUBPROCESS_ERROR\n")) { OnCmdError(s, "Sub process failed."); } else { Reselect |= CALL_MEMBER_FN(*this, c->PostOp)(Result, s, c->Params); } } if (c->Params && c->Params->Callback) { c->Params->Callback(Result, s); } Cmds.DeleteAt(i--, true); delete c; CmdsChanged = true; } // else printf("Not exited.\n"); } Processing = false; } if (Reselect) { if (LTreeItem::Select()) Select(true); } if (CmdsChanged) { Update(); } if (CmdErrors) { d->Tabs->Value(1); CmdErrors = false; } } void VcFolder::OnRemove() { LXmlTag *t = d->Opts.LockTag(OPT_Folders, _FL); if (t) { Uncommit.Reset(); if (LTreeItem::Select()) { d->Files->Empty(); d->Commits->RemoveAll(); } bool Found = false; auto u = Uri.ToString(); for (auto c: t->Children) { if (!c->IsTag(OPT_Folder)) printf("%s:%i - Wrong tag: %s, %s\n", _FL, c->GetTag(), OPT_Folder); else if (!c->GetContent()) printf("%s:%i - No content.\n", _FL); else { auto Content = c->GetContent(); if (!_stricmp(Content, u)) { c->RemoveTag(); delete c; Found = true; break; } } } LAssert(Found); d->Opts.Unlock(); } } void VcFolder::Empty() { Type = VcNone; IsCommit = false; IsLogging = false; IsUpdate = false; IsFilesCmd = false; CommitListDirty = false; IsUpdatingCounts = false; IsBranches = StatusNone; IsIdent = StatusNone; Unpushed = Unpulled = -1; CmdErrors = 0; CurrentCommitIdx = -1; CurrentCommit.Empty(); RepoUrl.Empty(); VcCmd.Empty(); Uncommit.Reset(); Log.DeleteObjects(); d->Commits->Empty(); d->Files->Empty(); if (!Uri.IsFile()) GetCss(true)->Color(LColour::Blue); } void VcFolder::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu s; s.AppendItem("Browse To", IDM_BROWSE_FOLDER, Uri.IsFile()); s.AppendItem( #ifdef WINDOWS "Command Prompt At", #else "Terminal At", #endif IDM_TERMINAL, Uri.IsFile()); s.AppendItem("Clean", IDM_CLEAN); s.AppendSeparator(); s.AppendItem("Pull", IDM_PULL); s.AppendItem("Status", IDM_STATUS); s.AppendItem("Push", IDM_PUSH); s.AppendItem("Update Subs", IDM_UPDATE_SUBS, GetType() == VcGit); s.AppendSeparator(); s.AppendItem("Remove", IDM_REMOVE); s.AppendItem("Remote URL", IDM_REMOTE_URL); if (!Uri.IsFile()) { s.AppendSeparator(); s.AppendItem("Edit Location", IDM_EDIT); } int Cmd = s.Float(GetTree(), m); switch (Cmd) { case IDM_BROWSE_FOLDER: { LBrowseToFile(LocalPath()); break; } case IDM_TERMINAL: { - TerminalAt(LocalPath()); + auto p = LocalPath(); + TerminalAt(p); break; } case IDM_CLEAN: { Clean(); break; } case IDM_PULL: { Pull(); break; } case IDM_STATUS: { FolderStatus(); break; } case IDM_PUSH: { Push(); break; } case IDM_UPDATE_SUBS: { UpdateSubs(); break; } case IDM_REMOVE: { OnRemove(); delete this; break; } case IDM_EDIT: { auto Dlg = new LInput(GetTree(), Uri.ToString(), "URI:", "Remote Folder Location"); Dlg->DoModal([this, Dlg](auto dlg, auto ctrlId) { if (ctrlId) { Uri.Set(Dlg->GetStr()); Empty(); Select(true); } delete dlg; }); break; } case IDM_REMOTE_URL: { GetRemoteUrl([this](auto code, auto str) { LString Url = str.Strip(); if (Url) { auto a = new LAlert(GetTree(), "Remote Url", Url, "Copy", "Ok"); a->DoModal([this, Url](auto dlg, auto code) { if (code == 1) { LClipBoard c(GetTree()); c.Text(Url); } delete dlg; }); } }); break; } default: break; } } } LString &VcFolder::GetCurrentBranch() { return CurrentBranch; } void VcFolder::SetCurrentBranch(LString name) { if (CurrentBranch != name) { CurrentBranch = name; UpdateBranchUi(); } } void VcFolder::Checkout(const char *Rev, bool isBranch) { if (!Rev || IsUpdate) return; LString Args; LAutoPtr params(new ParseParams(isBranch ? "Branch" : "Rev")); NewRev = Rev; switch (GetType()) { case VcGit: Args.Printf("checkout %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseCheckout, params.Release(), LogNormal); break; case VcSvn: Args.Printf("up -r %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseCheckout, params.Release(), LogNormal); break; case VcHg: Args.Printf("update -r %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseCheckout, params.Release(), LogNormal); break; default: { NoImplementation(_FL); break; } } } /////////////////////////////////////////////////////////////////////////////////////// int FolderCompare(LTreeItem *a, LTreeItem *b, NativeInt UserData) { VcLeaf *A = dynamic_cast(a); VcLeaf *B = dynamic_cast(b); if (!A || !B) return 0; return A->Compare(B); } struct SshFindEntry { LString Flags, Name, User, Group; uint64_t Size; LDateTime Modified, Access; SshFindEntry &operator =(const LString &s) { auto p = s.SplitDelimit("/"); if (p.Length() == 7) { Flags = p[0]; Group = p[1]; User = p[2]; Access.Set((uint64_t) p[3].Int()); Modified.Set((uint64_t) p[4].Int()); Size = p[5].Int(); Name = p[6]; } return *this; } bool IsDir() { return Flags(0) == 'd'; } bool IsHidden() { return Name(0) == '.'; } const char *GetName() { return Name; } static int Compare(SshFindEntry *a, SshFindEntry *b) { return Stricmp(a->Name.Get(), b->Name.Get()); } }; bool VcFolder::ParseRemoteFind(int Result, LString s, ParseParams *Params) { if (!Params || !s) return false; auto Parent = Params->Leaf ? static_cast(Params->Leaf) : static_cast(this); LUri u(Params->Str); auto Lines = s.SplitDelimit("\r\n"); LArray Entries; for (size_t i=1; iStr, Dir.GetName(), true); } } else if (!Dir.IsHidden()) { char *Ext = LGetExtension(Dir.GetName()); if (!Ext) continue; if (!stricmp(Ext, "c") || !stricmp(Ext, "cpp") || !stricmp(Ext, "h")) { LUri Path = u; Path += Dir.GetName(); new VcLeaf(this, Parent, Params->Str, Dir.GetName(), false); } } } return false; } void VcFolder::ReadDir(LTreeItem *Parent, const char *ReadUri) { LUri u(ReadUri); if (u.IsFile()) { // Read child items LDirectory Dir; for (int b = Dir.First(u.LocalPath()); b; b = Dir.Next()) { auto name = Dir.GetName(); if (Dir.IsHidden()) continue; LUri Path = u; Path += name; new VcLeaf(this, Parent, u.ToString(), name, Dir.IsDir()); } } #if HAS_LIBSSH else { auto c = d->GetConnection(ReadUri); if (!c) return; LString Path = u.sPath(Uri.sPath.Length(), -1).LStrip("/"); LString Args; Args.Printf("\"%s\" -maxdepth 1 -printf \"%%M/%%g/%%u/%%A@/%%T@/%%s/%%P\n\"", Path ? Path.Get() : "."); auto *Params = new ParseParams(ReadUri); Params->Leaf = dynamic_cast(Parent); c->Command(this, "find", Args, &VcFolder::ParseRemoteFind, Params, SshConnection::LogNone); return; } #endif Parent->Sort(FolderCompare); } void VcFolder::OnVcsType(LString errorMsg) { if (!d) { LAssert(!"No priv instance"); return; } #if HAS_LIBSSH auto c = d->GetConnection(Uri.ToString(), false); if (c) { auto NewType = c->Types.Find(Uri.sPath); if (NewType && NewType != Type) { if (NewType == VcError) { OnCmdError(LString(), errorMsg); } else { Type = NewType; ClearError(); Update(); if (LTreeItem::Select()) Select(true); for (auto &e: OnVcsTypeEvents) e(); OnVcsTypeEvents.Empty(); } } } #endif } void VcFolder::DoExpand() { if (Tmp) { Tmp->Remove(); DeleteObj(Tmp); ReadDir(this, Uri.ToString()); } } void VcFolder::OnExpand(bool b) { if (b) DoExpand(); } void VcFolder::ListCommit(VcCommit *c) { if (!IsFilesCmd) { LString Args; switch (GetType()) { case VcGit: Args.Printf("-P show %s^..%s", c->GetRev(), c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; case VcSvn: Args.Printf("log --verbose --diff -r %s", c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; case VcCvs: { d->ClearFiles(); for (unsigned i=0; iFiles.Length(); i++) { VcFile *f = new VcFile(d, this, c->GetRev(), false); if (f) { f->SetText(c->Files[i], COL_FILENAME); d->Files->Insert(f); } } FilterCurrentFiles(); break; } case VcHg: { Args.Printf("diff --change %s", c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; } default: LAssert(!"Impl me."); break; } if (IsFilesCmd) d->ClearFiles(); } } LString ConvertUPlus(LString s) { LArray c; LUtf8Ptr p(s); int32 ch; while ((ch = p)) { if (ch == '{') { auto n = p.GetPtr(); if (n[1] == 'U' && n[2] == '+') { // Convert unicode code point p += 3; ch = (int32)htoi(p.GetPtr()); c.Add(ch); while ((ch = p) != '}') p++; } else c.Add(ch); } else c.Add(ch); p++; } c.Add(0); #ifdef LINUX return LString((char16*)c.AddressOf()); #else return LString(c.AddressOf()); #endif } bool VcFolder::ParseStatus(int Result, LString s, ParseParams *Params) { bool ShowUntracked = d->Wnd()->GetCtrlValue(IDC_UNTRACKED) != 0; bool IsWorking = Params ? Params->IsWorking : false; List Ins; switch (GetType()) { case VcCvs: { LHashTbl,VcFile*> Map; for (auto i: *d->Files) { VcFile *f = dynamic_cast(i); if (f) Map.Add(f->GetText(COL_FILENAME), f); } #if 0 LFile Tmp("C:\\tmp\\output.txt", O_WRITE); Tmp.Write(s); Tmp.Close(); #endif LString::Array a = s.Split("==================================================================="); for (auto i : a) { LString::Array Lines = i.SplitDelimit("\r\n"); if (Lines.Length() == 0) continue; LString f = Lines[0].Strip(); if (f.Find("File:") == 0) { LString::Array Parts = f.SplitDelimit("\t"); LString File = Parts[0].Split(": ").Last().Strip(); LString Status = Parts[1].Split(": ").Last(); LString WorkingRev; for (auto l : Lines) { LString::Array p = l.Strip().Split(":", 1); if (p.Length() > 1 && p[0].Strip().Equals("Working revision")) { WorkingRev = p[1].Strip(); } } VcFile *f = Map.Find(File); if (!f) { if ((f = new VcFile(d, this, WorkingRev, IsWorking))) Ins.Insert(f); } if (f) { f->SetText(Status, COL_STATE); f->SetText(File, COL_FILENAME); f->Update(); } } else if (f(0) == '?' && ShowUntracked) { LString File = f(2, -1); VcFile *f = Map.Find(File); if (!f) { if ((f = new VcFile(d, this, LString(), IsWorking))) Ins.Insert(f); } if (f) { f->SetText("?", COL_STATE); f->SetText(File, COL_FILENAME); f->Update(); } } } for (auto i: *d->Files) { VcFile *f = dynamic_cast(i); if (f) { if (f->GetStatus() == VcFile::SUnknown) f->SetStatus(VcFile::SUntracked); } } break; } case VcGit: { auto Lines = s.SplitDelimit("\r\n"); int Fmt = ToolVersion[VcGit] >= Ver2Int("2.8.0") ? 2 : 1; for (auto Ln : Lines) { auto Type = Ln(0); if (Ln.Lower().Find("error:") >= 0) { } else if (Ln.Find("usage: git") >= 0) { // It's probably complaining about the --porcelain=2 parameter OnCmdError(s, "Args error"); } else if (Type != '?') { VcFile *f = NULL; if (Fmt == 2) { LString::Array p = Ln.SplitDelimit(" ", 8); if (p.Length() < 7) d->Log->Print("%s:%i - Error: not enough tokens: '%s'\n", _FL, Ln.Get()); else { auto path = p[6]; f = new VcFile(d, this, path, IsWorking); auto state = p[1].Strip("."); auto pos = p[1].Find(state); d->Log->Print("%s state='%s' pos=%i\n", path.Get(), state.Get(), (int)pos); f->SetText(state, COL_STATE); f->SetText(p.Last(), COL_FILENAME); f->SetStaged(pos == 0); } } else if (Fmt == 1) { LString::Array p = Ln.SplitDelimit(" "); f = new VcFile(d, this, LString(), IsWorking); f->SetText(p[0], COL_STATE); f->SetText(p.Last(), COL_FILENAME); } if (f) Ins.Insert(f); } else if (ShowUntracked) { VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText("?", COL_STATE); f->SetText(Ln(2,-1), COL_FILENAME); Ins.Insert(f); } } break; } case VcHg: case VcSvn: { if (s.Find("failed to import") >= 0) { OnCmdError(s, "Tool error."); return false; } LString::Array Lines = s.SplitDelimit("\r\n"); for (auto Ln : Lines) { char Type = Ln(0); if (Ln.Lower().Find("error:") >= 0) { } else if (Ln.Find("client is too old") >= 0) { OnCmdError(s, "Client too old."); return false; } else if (Strchr(" \t", Type) || Ln.Find("Summary of conflicts") >= 0) { // Ignore } else if (Type != '?') { LString::Array p = Ln.SplitDelimit(" ", 1); if (p.Length() == 2) { LString File; if (GetType() == VcSvn) File = ConvertUPlus(p.Last()); else File = p.Last(); if (GetType() == VcSvn && File.Find("+ ") == 0) { File = File(5, -1); } VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText(p[0], COL_STATE); f->SetText(File.Replace("\\","/"), COL_FILENAME); f->GetStatus(); Ins.Insert(f); } else LAssert(!"What happen?"); } else if (ShowUntracked) { VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText("?", COL_STATE); f->SetText(Ln(2,-1), COL_FILENAME); Ins.Insert(f); } } break; } default: { LAssert(!"Impl me."); break; } } if ((Unpushed = Ins.Length() > 0)) { if (CmdErrors == 0) GetCss(true)->Color(LColour(255, 128, 0)); } else if (Unpulled == 0) { GetCss(true)->Color(LCss::ColorInherit); } Update(); if (LTreeItem::Select()) { d->Files->Insert(Ins); FilterCurrentFiles(); } else { Ins.DeleteObjects(); } if (Params && Params->Leaf) Params->Leaf->AfterBrowse(); return false; // Don't refresh list } // Clone/checkout any sub-repositries. bool VcFolder::UpdateSubs() { LString Arg; switch (GetType()) { default: case VcSvn: case VcHg: case VcCvs: return false; case VcGit: Arg = "submodule update --init --recursive"; break; } return StartCmd(Arg, &VcFolder::ParseUpdateSubs, NULL, LogNormal); } bool VcFolder::ParseUpdateSubs(int Result, LString s, ParseParams *Params) { switch (GetType()) { default: case VcSvn: case VcHg: case VcCvs: return false; case VcGit: break; } return false; } void VcFolder::FolderStatus(const char *uri, VcLeaf *Notify) { LUri Uri(uri); if (Uri.IsFile() && Uri.sPath) { LFile::Path p(Uri.sPath(1,-1)); if (!p.IsFolder()) { LAssert(!"Needs to be a folder."); return; } } if (LTreeItem::Select()) d->ClearFiles(); LString Arg; switch (GetType()) { case VcSvn: case VcHg: Arg = "status"; break; case VcCvs: Arg = "status -l"; break; case VcGit: if (!ToolVersion[VcGit]) LAssert(!"Where is the version?"); // What version did =2 become available? It's definitely not in v2.5.4 // Not in v2.7.4 either... if (ToolVersion[VcGit] >= Ver2Int("2.8.0")) Arg = "-P status --porcelain=2"; else Arg = "-P status --porcelain"; break; default: return; } ParseParams *p = new ParseParams; if (uri && Notify) { p->AltInitPath = uri; p->Leaf = Notify; } else { p->IsWorking = true; } StartCmd(Arg, &VcFolder::ParseStatus, p); switch (GetType()) { case VcHg: CountToTip(); break; default: break; } } void VcFolder::CountToTip() { // if (Path.Equals("C:\\Users\\matthew\\Code\\Lgi\\trunk")) { // LgiTrace("%s: CountToTip, br=%s, idx=%i\n", Path.Get(), CurrentBranch.Get(), (int)CurrentCommitIdx); if (!CurrentBranch) GetBranches(new ParseParams("CountToTip")); else if (CurrentCommitIdx < 0) GetCurrentRevision(new ParseParams("CountToTip")); else { LString Arg; Arg.Printf("id -n -r %s", CurrentBranch.Get()); StartCmd(Arg, &VcFolder::ParseCountToTip); } } } bool VcFolder::ParseCountToTip(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: if (CurrentCommitIdx >= 0) { auto p = s.Strip(); auto idx = p.Int(); if (idx >= CurrentCommitIdx) { Unpulled = (int) (idx - CurrentCommitIdx); Update(); } } break; default: break; } return false; } void VcFolder::ListWorkingFolder() { if (IsListingWorking) return; d->ClearFiles(); bool Untracked = d->IsMenuChecked(IDM_UNTRACKED); LString Arg; switch (GetType()) { case VcPending: OnVcsTypeEvents.Add([this]() { ListWorkingFolder(); }); break; case VcCvs: if (Untracked) Arg = "-qn update"; else Arg = "-q diff --brief"; break; case VcSvn: Arg = "status"; break; case VcGit: #if 1 Arg = "-P status -vv"; #else Arg = "-P diff --diff-filter=CMRTU --cached"; #endif break; case VcHg: Arg = "status -mard"; break; default: return; } IsListingWorking = StartCmd(Arg, &VcFolder::ParseWorking); } void VcFolder::GitAdd() { if (!PostAdd) return; LString Args; if (PostAdd->Files.Length() == 0) { LString m(PostAdd->Msg); m = m.Replace("\"", "\\\""); Args.Printf("commit -m \"%s\"", m.Get()); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, PostAdd->Param, LogNormal); PostAdd.Reset(); } else { char NativeSep[] = {GetPathSep(), 0}; LString Last = PostAdd->Files.Last(); Args.Printf("add \"%s\"", Last.Replace("\"", "\\\"").Replace("/", NativeSep).Get()); PostAdd->Files.PopLast(); StartCmd(Args, &VcFolder::ParseGitAdd, NULL, LogNormal); } } bool VcFolder::ParseGitAdd(int Result, LString s, ParseParams *Params) { if (Result) { OnCmdError(s, "add failed."); } else { GitAdd(); } return false; } bool VcFolder::ParseCommit(int Result, LString s, ParseParams *Params) { if (LTreeItem::Select()) Select(true); CommitListDirty = Result == 0; CurrentCommit.Empty(); IsCommit = false; if (Result) { switch (GetType()) { case VcGit: { if (s.Find("Please tell me who you are") >= 0) { auto i = new LInput(GetTree(), "", "Git user name:", AppName); i->DoModal([this, i](auto dlg, auto ctrlId) { if (ctrlId) { LString Args; Args.Printf("config --global user.name \"%s\"", i->GetStr().Get()); StartCmd(Args); auto inp = new LInput(GetTree(), "", "Git user email:", AppName); i->DoModal([this, inp](auto dlg, auto ctrlId) { if (ctrlId) { LString Args; Args.Printf("config --global user.email \"%s\"", inp->GetStr().Get()); StartCmd(Args); } delete dlg; }); } delete dlg; }); } break; } default: break; } return false; } if (Result == 0 && LTreeItem::Select()) { d->ClearFiles(); auto *w = d->Diff ? d->Diff->GetWindow() : NULL; if (w) w->SetCtrlName(IDC_MSG, NULL); } switch (GetType()) { case VcGit: { Unpushed++; CommitListDirty = true; Update(); if (Params && Params->Str.Find("Push") >= 0) Push(); break; } case VcSvn: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); GetCss(true)->Color(LColour::Green); } break; } case VcHg: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); if (Params && Params->Str.Find("Push") >= 0) Push(); else GetCss(true)->Color(LColour::Green); } break; } case VcCvs: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); GetCss(true)->Color(LColour::Green); } break; } default: { LAssert(!"Impl me."); break; } } return true; } void VcFolder::Commit(const char *Msg, const char *Branch, bool AndPush) { LArray Add; bool Partial = false; for (auto fp: *d->Files) { VcFile *f = dynamic_cast(fp); if (f) { int c = f->Checked(); if (c > 0) Add.Add(f); else Partial = true; } } if (CurrentBranch && Branch && !CurrentBranch.Equals(Branch)) { int Response = LgiMsg(GetTree(), "Do you want to start a new branch?", AppName, MB_YESNO); if (Response != IDYES) return; LJson j; j.Set("Command", "commit"); j.Set("Msg", Msg); j.Set("AndPush", (int64_t)AndPush); StartBranch(Branch, j.GetJson()); return; } if (!IsCommit) { LString Args; ParseParams *Param = AndPush ? new ParseParams("Push") : NULL; switch (GetType()) { case VcGit: { if (Add.Length() == 0) { break; } else if (Partial) { if (PostAdd.Reset(new GitCommit)) { PostAdd->Files.SetFixedLength(false); for (auto f : Add) PostAdd->Files.Add(f->GetFileName()); PostAdd->Msg = Msg; PostAdd->Branch = Branch; PostAdd->Param = Param; GitAdd(); } } else { LString m(Msg); m = m.Replace("\"", "\\\""); Args.Printf("commit -am \"%s\"", m.Get()); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); } break; } case VcSvn: { LString::Array a; a.New().Printf("commit -m \"%s\"", Msg); for (auto pf: Add) { LString s = pf->GetFileName(); if (s.Find(" ") >= 0) a.New().Printf("\"%s\"", s.Get()); else a.New() = s; } Args = LString(" ").Join(a); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); if (d->Tabs && IsCommit) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } break; } case VcHg: { LString::Array a; LString CommitMsg = Msg; TmpFile Tmp; if (CommitMsg.Find("\n") >= 0) { Tmp.Create().Write(Msg); a.New().Printf("commit -l \"%s\"", Tmp.GetName()); } else { a.New().Printf("commit -m \"%s\"", Msg); } if (Partial) { for (auto pf: Add) { LString s = pf->GetFileName(); if (s.Find(" ") >= 0) a.New().Printf("\"%s\"", s.Get()); else a.New() = s; } } Args = LString(" ").Join(a); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); if (d->Tabs && IsCommit) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } break; } case VcCvs: { LString a; a.Printf("commit -m \"%s\"", Msg); IsCommit = StartCmd(a, &VcFolder::ParseCommit, NULL, LogNormal); break; } default: { OnCmdError(LString(), "No commit impl for type."); break; } } } } bool VcFolder::ParseStartBranch(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: { if (Result == 0 && Params && Params->Str) { LJson j(Params->Str); auto cmd = j.Get("Command"); if (cmd.Equals("commit")) { auto Msg = j.Get("Msg"); auto AndPush = j.Get("AndPush").Int(); if (Msg) { Commit(Msg, NULL, AndPush > 0); } } } break; } default: { OnCmdError(LString(), "No commit impl for type."); break; } } return true; } void VcFolder::StartBranch(const char *BranchName, const char *OnCreated) { if (!BranchName) return; switch (GetType()) { case VcHg: { LString a; a.Printf("branch \"%s\"", BranchName); StartCmd(a, &VcFolder::ParseStartBranch, OnCreated ? new ParseParams(OnCreated) : NULL); break; } default: { NoImplementation(_FL); break; } } } void VcFolder::Push(bool NewBranchOk) { LString Args; bool Working = false; switch (GetType()) { case VcHg: { auto args = NewBranchOk ? "push --new-branch" : "push"; Working = StartCmd(args, &VcFolder::ParsePush, NULL, LogNormal); break; } case VcGit: { LString args; if (NewBranchOk) { if (CurrentBranch) { args.Printf("push --set-upstream origin %s", CurrentBranch.Get()); } else { OnCmdError(LString(), "Don't have the current branch?"); return; } } else { args = "push"; } Working = StartCmd(args, &VcFolder::ParsePush, NULL, LogNormal); break; } case VcSvn: { // Nothing to do here.. the commit pushed the data already break; } default: { OnCmdError(LString(), "No push impl for type."); break; } } if (d->Tabs && Working) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } } bool VcFolder::ParsePush(int Result, LString s, ParseParams *Params) { bool Status = false; if (Result) { bool needsNewBranchPerm = false; switch (GetType()) { case VcHg: { needsNewBranchPerm = s.Find("push creates new remote branches") >= 0; break; } case VcGit: { needsNewBranchPerm = s.Find("The current branch") >= 0 && s.Find("has no upstream branch") >= 0; break; } } if (needsNewBranchPerm && LgiMsg(GetTree(), LLoadString(IDS_CREATE_NEW_BRANCH), AppName, MB_YESNO) == IDYES) { Push(true); return false; } OnCmdError(s, "Push failed."); } else { switch (GetType()) { case VcGit: break; case VcSvn: break; default: break; } Unpushed = 0; GetCss(true)->Color(LColour::Green); Update(); Status = true; } GetTree()->SendNotify((LNotifyType)LvcCommandEnd); return Status; // no reselect } void VcFolder::Pull(int AndUpdate, LoggingType Logging) { bool Status = false; if (AndUpdate < 0) AndUpdate = GetTree()->GetWindow()->GetCtrlValue(IDC_UPDATE) != 0; switch (GetType()) { case VcNone: return; case VcHg: Status = StartCmd(AndUpdate ? "pull -u" : "pull", &VcFolder::ParsePull, NULL, Logging); break; case VcGit: Status = StartCmd(AndUpdate ? "pull" : "fetch", &VcFolder::ParsePull, NULL, Logging); break; case VcSvn: Status = StartCmd("up", &VcFolder::ParsePull, NULL, Logging); break; case VcPending: OnVcsTypeEvents.New() = [this, AndUpdate, Logging]() { Pull(AndUpdate, Logging); }; break; default: NoImplementation(_FL); break; } if (d->Tabs && Status) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } } bool VcFolder::ParsePull(int Result, LString s, ParseParams *Params) { GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (Result) { OnCmdError(s, "Pull failed."); return false; } else ClearError(); switch (GetType()) { case VcGit: { // Git does a merge by default, so the current commit changes... CurrentCommit.Empty(); break; } case VcHg: { CurrentCommit.Empty(); auto Lines = s.SplitDelimit("\n"); bool HasUpdates = false; for (auto Ln: Lines) { if (Ln.Find("files updated") < 0) continue; auto Parts = Ln.Split(","); for (auto p: Parts) { auto n = p.Strip().Split(" ", 1); if (n.Length() == 2) { if (n[0].Int() > 0) HasUpdates = true; } } } if (HasUpdates) GetCss(true)->Color(LColour::Green); else GetCss(true)->Color(LCss::ColorInherit); break; } case VcSvn: { // Svn also does a merge by default and can update our current position... CurrentCommit.Empty(); LString::Array a = s.SplitDelimit("\r\n"); for (auto &Ln: a) { if (Ln.Find("At revision") >= 0) { LString::Array p = Ln.SplitDelimit(" ."); CurrentCommit = p.Last(); break; } else if (Ln.Find("svn cleanup") >= 0) { OnCmdError(s, "Needs cleanup"); break; } } if (Params && Params->Str.Equals("log")) { LVariant Limit; d->Opts.GetValue("svn-limit", Limit); LString Args; if (Limit.CastInt32() > 0) Args.Printf("log --limit %i", Limit.CastInt32()); else Args = "log"; IsLogging = StartCmd(Args, &VcFolder::ParseLog); return false; } break; } default: break; } CommitListDirty = true; return true; // Yes - reselect and update } void VcFolder::MergeToLocal(LString Rev) { switch (GetType()) { case VcGit: { LString Args; Args.Printf("merge -m \"Merge with %s\" %s", Rev.Get(), Rev.Get()); StartCmd(Args, &VcFolder::ParseMerge, NULL, LogNormal); break; } case VcHg: { LString Args; Args.Printf("merge -r %s", Rev.Get()); StartCmd(Args, &VcFolder::ParseMerge, NULL, LogNormal); break; } default: LgiMsg(GetTree(), LLoadString(IDS_ERR_NO_IMPL_FOR_TYPE), AppName); break; } } bool VcFolder::ParseMerge(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: case VcHg: if (Result == 0) CommitListDirty = true; else OnCmdError(s, LLoadString(IDS_ERR_MERGE_FAILED)); break; default: LAssert(!"Impl me."); break; } return true; } void VcFolder::Refresh() { CommitListDirty = true; CurrentCommit.Empty(); GitNames.Empty(); Branches.DeleteObjects(); if (Uncommit && Uncommit->LListItem::Select()) Uncommit->Select(true); Select(true); } void VcFolder::Clean() { switch (GetType()) { case VcSvn: StartCmd("cleanup", &VcFolder::ParseClean, NULL, LogNormal); break; default: LgiMsg(GetTree(), LLoadString(IDS_ERR_NO_IMPL_FOR_TYPE), AppName); break; } } bool VcFolder::ParseClean(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcSvn: if (Result == 0) GetCss(true)->Color(LCss::ColorInherit); break; default: LAssert(!"Impl me."); break; } return false; } LColour VcFolder::BranchColour(const char *Name) { if (!Name) return GetPaletteColour(0); auto b = Branches.Find(Name); if (!b) // Must be a new one? { int i = 1; for (auto b: Branches) { auto &v = b.value; if (!v->Colour.IsValid()) { if (v->Default) v->Colour = GetPaletteColour(0); else v->Colour = GetPaletteColour(i++); } } Branches.Add(Name, b = new VcBranch(Name)); b->Colour = GetPaletteColour((int)Branches.Length()); } return b ? b->Colour : GetPaletteColour(0); } void VcFolder::CurrentRev(std::function Callback) { LString Cmd; Cmd.Printf("id -i"); RunCmd(Cmd, LogNormal, [Callback](auto r) { if (r.Code == 0) Callback(r.Out.Strip()); }); } bool VcFolder::RenameBranch(LString NewName, LArray &Revs) { switch (GetType()) { case VcHg: { // Update to the ancestor of the commits LHashTbl,int> Refs(0, -1); for (auto c: Revs) { for (auto p:*c->GetParents()) if (Refs.Find(p) < 0) Refs.Add(p, 0); if (Refs.Find(c->GetRev()) >= 0) Refs.Add(c->GetRev(), 1); } LString::Array Ans; for (auto i:Refs) { if (i.value == 0) Ans.Add(i.key); } LArray Ancestors = d->GetRevs(Ans); if (Ans.Length() != 1) { // We should only have one ancestor LString s, m; s.Printf("Wrong number of ancestors: " LPrintfInt64 ".\n", Ans.Length()); for (auto i: Ancestors) { m.Printf("\t%s\n", i->GetRev()); s += m; } LgiMsg(GetTree(), s, AppName, MB_OK); break; } LArray Top; for (auto c:Revs) { for (auto p:*c->GetParents()) if (Refs.Find(p) == 0) Top.Add(c); } if (Top.Length() != 1) { d->Log->Print("Error: Can't find top most commit. (%s:%i)\n", _FL); return false; } // Create the new branch... auto First = Ancestors.First(); LString Cmd; Cmd.Printf("update -r " LPrintfInt64, First->GetIndex()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } Cmd.Printf("branch \"%s\"", NewName.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } // Commit it to get a revision point to rebase to Cmd.Printf("commit -m \"Branch: %s\"", NewName.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } CurrentRev([this, &Cmd, NewName, &Top](auto BranchNode) { // Rebase the old tree to this point Cmd.Printf("rebase -s %s -d %s", Top.First()->GetRev(), BranchNode.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } CommitListDirty = true; d->Log->Print("Finished rename.\n", _FL); }); }); }); }); }); break; } default: { LgiMsg(GetTree(), LLoadString(IDS_ERR_NO_IMPL_FOR_TYPE), AppName); break; } } return true; } bool VcFolder::ParseAddFile(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcCvs: { if (Result) { d->Tabs->Value(1); OnCmdError(s, LLoadString(IDS_ERR_ADD_FAILED)); } else ClearError(); break; } default: break; } return false; } bool VcFolder::AddFile(const char *Path, bool AsBinary) { if (!Path) return false; switch (GetType()) { case VcCvs: { auto p = LString(Path).RSplit(DIR_STR, 1); ParseParams *params = NULL; if (p.Length() >= 2) { if ((params = new ParseParams)) params->AltInitPath = p[0]; } LString a; a.Printf("add%s \"%s\"", AsBinary ? " -kb" : "", p.Length() > 1 ? p.Last().Get() : Path); return StartCmd(a, &VcFolder::ParseAddFile, params); break; } default: { NoImplementation(_FL); break; } } return false; } bool VcFolder::ParseRevert(int Result, LString s, ParseParams *Params) { if (GetType() == VcSvn) { if (s.Find("Skipped ") >= 0) Result = 1; // Stupid svn... *sigh* } if (Result) { OnCmdError(s, LLoadString(IDS_ERR_REVERT_FAILED)); } ListWorkingFolder(); return false; } bool VcFolder::Revert(LString::Array &Uris, const char *Revision) { if (Uris.Length() == 0) return false; switch (GetType()) { case VcGit: { LStringPipe cmd, paths; LAutoPtr params; if (Revision) { cmd.Print("checkout %s", Revision); } else { // Unstage the file... cmd.Print("reset"); } for (auto u: Uris) { auto Path = GetFilePart(u); paths.Print(" \"%s\"", Path.Get()); } auto p = paths.NewLStr(); cmd.Write(p); if (!Revision) { if (params.Reset(new ParseParams)) { params->Callback = [this, p](auto code, auto str) { LString c; c.Printf("checkout %s", p.Get()); StartCmd(c, &VcFolder::ParseRevert); }; } } return StartCmd(cmd.NewLStr(), &VcFolder::ParseRevert, params.Release()); break; } case VcHg: case VcSvn: { LStringPipe p; if (Revision) p.Print("up -r %s", Revision); else p.Print("revert"); for (auto u: Uris) { auto Path = GetFilePart(u); p.Print(" \"%s\"", Path.Get()); } auto a = p.NewLStr(); return StartCmd(a, &VcFolder::ParseRevert); break; } default: { NoImplementation(_FL); break; } } return false; } bool VcFolder::ParseResolveList(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: { auto lines = s.Replace("\r").Split("\n"); for (auto &ln: lines) { auto p = ln.Split(" ", 1); if (p.Length() == 2) { if (p[0].Equals("U")) { auto f = new VcFile(d, this, LString(), true); f->SetText(p[0], COL_STATE); f->SetText(p[1], COL_FILENAME); f->GetStatus(); d->Files->Insert(f); } } } break; } default: { NoImplementation(_FL); break; } } return true; } bool VcFolder::ParseResolve(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { break; } case VcHg: { d->Log->Print("Resolve: %s\n", s.Get()); break; } default: { NoImplementation(_FL); break; } } return true; } bool VcFolder::Resolve(const char *Path, LvcResolve Type) { if (!Path) return false; switch (GetType()) { case VcGit: { LString a; auto local = GetFilePart(Path); LAutoPtr params(new ParseParams(Path)); switch (Type) { case ResolveIncoming: a.Printf("checkout --theirs \"%s\"", local.Get()); break; case ResolveLocal: a.Printf("checkout --ours \"%s\"", local.Get()); break; case ResolveMark: a.Printf("add \"%s\"", local.Get()); break; default: OnCmdError(Path, "No resolve type implemented."); return false; } if (Type == ResolveIncoming || Type == ResolveLocal) { // Add the file after the resolution: params->Callback = [this, local](auto code, auto str) { LString a; a.Printf("add \"%s\"", local.Get()); StartCmd(a, &VcFolder::ParseAddFile); Refresh(); }; } return StartCmd(a, &VcFolder::ParseResolve, params.Release()); } case VcHg: { LString a; auto local = GetFilePart(Path); switch (Type) { case ResolveMark: a.Printf("resolve -m \"%s\"", local.Get()); break; case ResolveUnmark: a.Printf("resolve -u \"%s\"", local.Get()); break; case ResolveLocal: a.Printf("resolve -t internal:local \"%s\"", local.Get()); break; case ResolveIncoming: a.Printf("resolve -t internal:other \"%s\"", local.Get()); break; default: break; } if (a) return StartCmd(a, &VcFolder::ParseResolve, new ParseParams(Path)); break; } case VcSvn: case VcCvs: default: { NoImplementation(_FL); break; } } return false; } bool BlameLine::Parse(VersionCtrl type, LArray &out, LString in) { auto lines = in.SplitDelimit("\n", -1, false); switch (type) { case VcGit: { for (auto &ln: lines) { auto s = ln.Get(); auto open = ln.Find("("); auto close = ln.Find(")", open); if (open > 0 && close > open) { auto eRef = ln(0, open-1); auto fields = ln(open + 1, close); auto parts = fields.SplitDelimit(); auto &o = out.New(); o.ref = eRef; o.line = parts.Last(); parts.PopLast(); LString::Array name; LDateTime dt; for (auto p: parts) { auto first = p(0); if (IsDigit(first)) { if (p.Find("-") > 0) dt.SetDate(p); else if (p.Find(":") > 0) dt.SetTime(p); } else if (first == '+') dt.SetTimeZone((int)p.Int(), false); else name.Add(p); } o.user = LString(" ").Join(name); o.date = dt.Get(); o.src = ln(close + 1, -1); } else if (ln.Length() > 0) { int asd=0; } } break; } case VcHg: { for (auto &ln: lines) { auto s = ln.Get(); auto eUser = strchr(s, ' '); if (!eUser) continue; auto eRef = strchr(eUser, ':'); if (!eRef) continue; auto &o = out.New(); o.user.Set(s, eUser++ - s); o.ref.Set(eUser, eRef - eUser); o.src = eRef + 1; } break; } /* case VcSvn: { break; } */ default: { LAssert(0); return false; } } return true; } bool VcFolder::ParseBlame(int Result, LString s, ParseParams *Params) { if (!Params) { LAssert(!"Need the path in the params."); return false; } LArray lines; if (BlameLine::Parse(GetType(), lines, s)) { if (auto ui = new BrowseUi(BrowseUi::TBlame, d, this, Params->Str)) ui->ParseBlame(lines, s); } else NoImplementation(_FL); return false; } bool VcFolder::Blame(const char *Path) { if (!Path) return false; auto file = GetFilePart(Path); LAutoPtr Params(new ParseParams(file)); LUri u(Path); switch (GetType()) { case VcGit: { LString a; a.Printf("-P blame \"%s\"", file.Get()); return StartCmd(a, &VcFolder::ParseBlame, Params.Release()); break; } case VcHg: { LString a; a.Printf("annotate -un \"%s\"", file.Get()); return StartCmd(a, &VcFolder::ParseBlame, Params.Release()); break; } case VcSvn: { LString a; a.Printf("blame \"%s\"", file.Get()); return StartCmd(a, &VcFolder::ParseBlame, Params.Release()); break; } default: { NoImplementation(_FL); break; } } return true; } bool VcFolder::SaveFileAs(const char *Path, const char *Revision) { if (!Path || !Revision) return false; return true; } bool VcFolder::ParseSaveAs(int Result, LString s, ParseParams *Params) { return false; } bool VcFolder::ParseCounts(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { Unpushed = (int) s.Strip().Split("\n").Length(); break; } case VcSvn: { int64 ServerRev = 0; bool HasUpdate = false; LString::Array c = s.Split("\n"); for (unsigned i=0; i 1 && a[0].Equals("Status")) ServerRev = a.Last().Int(); else if (a[0].Equals("*")) HasUpdate = true; } if (ServerRev > 0 && HasUpdate) { int64 CurRev = CurrentCommit.Int(); Unpulled = (int) (ServerRev - CurRev); } else Unpulled = 0; Update(); break; } default: { LAssert(!"Impl me."); break; } } IsUpdatingCounts = false; Update(); return false; // No re-select } void VcFolder::SetEol(const char *Path, int Type) { if (!Path) return; switch (Type) { case IDM_EOL_LF: { ConvertEol(Path, false); break; } case IDM_EOL_CRLF: { ConvertEol(Path, true); break; } case IDM_EOL_AUTO: { #ifdef WINDOWS ConvertEol(Path, true); #else ConvertEol(Path, false); #endif break; } } } void VcFolder::UncommitedItem::Select(bool b) { LListItem::Select(b); if (b) { LTreeItem *i = d->Tree->Selection(); VcFolder *f = dynamic_cast(i); if (f) f->ListWorkingFolder(); if (d->Msg) { d->Msg->Name(NULL); auto *w = d->Msg->GetWindow(); if (w) { w->SetCtrlEnabled(IDC_COMMIT, true); w->SetCtrlEnabled(IDC_COMMIT_AND_PUSH, true); } } } } void VcFolder::UncommitedItem::OnPaint(LItem::ItemPaintCtx &Ctx) { LFont *f = GetList()->GetFont(); f->Transparent(false); f->Colour(Ctx.Fore, Ctx.Back); LDisplayString ds(f, "(working folder)"); ds.Draw(Ctx.pDC, Ctx.x1 + ((Ctx.X() - ds.X()) / 2), Ctx.y1 + ((Ctx.Y() - ds.Y()) / 2), &Ctx); } ////////////////////////////////////////////////////////////////////////////////////////// VcLeaf::VcLeaf(VcFolder *parent, LTreeItem *Item, LString uri, LString leaf, bool folder) { Parent = parent; d = Parent->GetPriv(); LAssert(uri.Find("://") >= 0); // Is URI Uri.Set(uri); LAssert(Uri); Leaf = leaf; Folder = folder; Item->Insert(this); if (Folder) { Insert(Tmp = new LTreeItem); Tmp->SetText("Loading..."); } } VcLeaf::~VcLeaf() { for (auto l: Log) { if (!l->GetList()) delete l; } } LString VcLeaf::Full() { LUri u = Uri; u += Leaf; return u.ToString(); } void VcLeaf::OnBrowse() { auto full = Full(); LList *Files = d->Files; Files->Empty(); LDirectory Dir; for (int b = Dir.First(full); b; b = Dir.Next()) { if (Dir.IsDir()) continue; VcFile *f = new VcFile(d, Parent, LString(), true); if (f) { f->SetUri(LString("file://") + full); f->SetText(Dir.GetName(), COL_FILENAME); Files->Insert(f); } } Files->ResizeColumnsToContent(); if (Folder) Parent->FolderStatus(full, this); } void VcLeaf::AfterBrowse() { } VcLeaf *VcLeaf::FindLeaf(const char *Path, bool OpenTree) { if (!Stricmp(Path, Full().Get())) return this; if (OpenTree) DoExpand(); VcLeaf *r = NULL; for (auto n = GetChild(); !r && n; n = n->GetNext()) { auto l = dynamic_cast(n); if (l) r = l->FindLeaf(Path, OpenTree); } return r; } void VcLeaf::DoExpand() { if (Tmp) { Tmp->Remove(); DeleteObj(Tmp); Parent->ReadDir(this, Full()); } } void VcLeaf::OnExpand(bool b) { if (b) DoExpand(); } const char *VcLeaf::GetText(int Col) { if (Col == 0) return Leaf; return NULL; } int VcLeaf::GetImage(int Flags) { return Folder ? IcoFolder : IcoFile; } int VcLeaf::Compare(VcLeaf *b) { // Sort folders to the top... if (Folder ^ b->Folder) return (int)b->Folder - (int)Folder; // Then alphabetical return Stricmp(Leaf.Get(), b->Leaf.Get()); } bool VcLeaf::Select() { return LTreeItem::Select(); } void VcLeaf::Select(bool b) { LTreeItem::Select(b); if (b) { d->Commits->RemoveAll(); OnBrowse(); ShowLog(); } } void VcLeaf::ShowLog() { if (!Log.Length()) return; d->Commits->RemoveAll(); Parent->DefaultFields(); Parent->UpdateColumns(); for (auto i: Log) // We make a copy of the commit here so that the LList owns the copied object, // and this object still owns 'i'. d->Commits->Insert(new VcCommit(*i), -1, false); d->Commits->UpdateAllItems(); d->Commits->ResizeColumnsToContent(); } void VcLeaf::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu s; s.AppendItem("Log", IDM_LOG); s.AppendItem("Blame", IDM_BLAME, !Folder); s.AppendSeparator(); s.AppendItem("Browse To", IDM_BROWSE_FOLDER); s.AppendItem("Terminal At", IDM_TERMINAL); int Cmd = s.Float(GetTree(), m - _ScrollPos()); switch (Cmd) { case IDM_LOG: { Parent->LogFile(Full()); break; } case IDM_BLAME: { Parent->Blame(Full()); break; } case IDM_BROWSE_FOLDER: { LBrowseToFile(Full()); break; } case IDM_TERMINAL: { TerminalAt(Full()); break; } } } } ///////////////////////////////////////////////////////////////////////////////////////// ProcessCallback::ProcessCallback(LString exe, LString args, LString localPath, LTextLog *log, LView *view, std::function callback) : Log(log), View(view), Callback(callback), LThread("ProcessCallback.Thread"), LSubProcess(exe, args) { SetInitFolder(localPath); if (Log) Log->Print("%s %s\n", exe.Get(), args.Get()); Run(); } int ProcessCallback::Main() { if (!Start()) { Ret.Out.Printf("Process failed with %i", GetErrorCode()); Callback(Ret); } else { while (IsRunning()) { auto Rd = Read(); if (Rd.Length()) { Ret.Out += Rd; if (Log) Log->Write(Rd.Get(), Rd.Length()); } } auto Rd = Read(); if (Rd.Length()) { Ret.Out += Rd; if (Log) Log->Write(Rd.Get(), Rd.Length()); } Ret.Code = GetExitValue(); } View->PostEvent(M_HANDLE_CALLBACK, (LMessage::Param)this); return 0; } void ProcessCallback::OnComplete() // Called in the GUI thread... { Callback(Ret); } diff --git a/resourceEditor/mac/LgiRes.xcodeproj/project.pbxproj b/resourceEditor/mac/LgiRes.xcodeproj/project.pbxproj --- a/resourceEditor/mac/LgiRes.xcodeproj/project.pbxproj +++ b/resourceEditor/mac/LgiRes.xcodeproj/project.pbxproj @@ -1,551 +1,549 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ - 3432AF7D2ADFE35C0029F2D7 /* libpng16.15.29.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF7B2ADFE34B0029F2D7 /* libpng16.15.29.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 3432AF7E2ADFE3610029F2D7 /* libz_local.1.2.5.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF792ADFE3450029F2D7 /* libz_local.1.2.5.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3432AF7D2ADFE35C0029F2D7 /* libpng16.16.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF7B2ADFE34B0029F2D7 /* libpng16.16.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3432AF7E2ADFE3610029F2D7 /* libz.1.3.1.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3432AF792ADFE3450029F2D7 /* libz.1.3.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 343FB8952386621600797ABC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 343FB8942386621600797ABC /* Assets.xcassets */; }; 343FB8AB238662C500797ABC /* LgiMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 343FB8AA238662C500797ABC /* LgiMain.cpp */; }; 343FB8C62386638C00797ABC /* LgiCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343FB8A92386627A00797ABC /* LgiCocoa.framework */; }; 343FB8C82386639C00797ABC /* LgiCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 343FB8A92386627A00797ABC /* LgiCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 343FB8DC2386642A00797ABC /* OptionsFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 343FB8DB2386642A00797ABC /* OptionsFile.cpp */; }; 34CC002823AAD2A100CCAAEA /* Search.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC001B23AAD2A100CCAAEA /* Search.cpp */; }; 34CC002923AAD2A100CCAAEA /* LgiRes_ControlTree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC001C23AAD2A100CCAAEA /* LgiRes_ControlTree.cpp */; }; 34CC002A23AAD2A100CCAAEA /* LgiRes_TableLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC001E23AAD2A100CCAAEA /* LgiRes_TableLayout.cpp */; }; 34CC002B23AAD2A100CCAAEA /* LgiRes_Menu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002023AAD2A100CCAAEA /* LgiRes_Menu.cpp */; }; 34CC002C23AAD2A100CCAAEA /* LgiRes_Css.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002123AAD2A100CCAAEA /* LgiRes_Css.cpp */; }; 34CC002D23AAD2A100CCAAEA /* LgiResApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002323AAD2A100CCAAEA /* LgiResApp.cpp */; }; 34CC002E23AAD2A100CCAAEA /* ShowLanguages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002423AAD2A100CCAAEA /* ShowLanguages.cpp */; }; 34CC002F23AAD2A100CCAAEA /* LgiRes_Dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002623AAD2A100CCAAEA /* LgiRes_Dialog.cpp */; }; 34CC003023AAD2A100CCAAEA /* LgiRes_String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC002723AAD2A100CCAAEA /* LgiRes_String.cpp */; }; 34CC003223AAD2C100CCAAEA /* StatusBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC003123AAD2C100CCAAEA /* StatusBar.cpp */; }; 34CC003423AAD2E700CCAAEA /* Mru.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC003323AAD2E700CCAAEA /* Mru.cpp */; }; 34CC003723AAD2FE00CCAAEA /* DocApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC003523AAD2FD00CCAAEA /* DocApp.cpp */; }; 34CC003823AAD2FE00CCAAEA /* About.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC003623AAD2FE00CCAAEA /* About.cpp */; }; 34CC003C23AAD33500CCAAEA /* mac-icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 34CC003A23AAD33500CCAAEA /* mac-icon.icns */; }; 34CC003D23AAD33500CCAAEA /* lgires.lr8 in Resources */ = {isa = PBXBuildFile; fileRef = 34CC003B23AAD33500CCAAEA /* lgires.lr8 */; }; 34CC003F23AAD34600CCAAEA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34CC003E23AAD34600CCAAEA /* Cocoa.framework */; }; 34CC004223AAD54200CCAAEA /* Png.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC004123AAD54200CCAAEA /* Png.cpp */; }; 34CC006623AAD5DB00CCAAEA /* _StringIcons.gif in Resources */ = {isa = PBXBuildFile; fileRef = 34CC006223AAD5DB00CCAAEA /* _StringIcons.gif */; }; 34CC006723AAD5DB00CCAAEA /* _DialogIcons.gif in Resources */ = {isa = PBXBuildFile; fileRef = 34CC006323AAD5DB00CCAAEA /* _DialogIcons.gif */; }; 34CC006823AAD5DB00CCAAEA /* _MenuIcons.gif in Resources */ = {isa = PBXBuildFile; fileRef = 34CC006423AAD5DB00CCAAEA /* _MenuIcons.gif */; }; 34CC006923AAD5DB00CCAAEA /* _Icons.gif in Resources */ = {isa = PBXBuildFile; fileRef = 34CC006523AAD5DB00CCAAEA /* _Icons.gif */; }; 34CC006C23AAD5FC00CCAAEA /* Gif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC006A23AAD5FC00CCAAEA /* Gif.cpp */; }; 34CC006D23AAD5FC00CCAAEA /* Lzw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34CC006B23AAD5FC00CCAAEA /* Lzw.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 343FB8A82386627A00797ABC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3477C2681CBF020F0028B84B; remoteInfo = LgiCocoa; }; 343FB8C32386638800797ABC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; proxyType = 1; remoteGlobalIDString = 3477C2671CBF020F0028B84B; remoteInfo = LgiCocoa; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 343FB8C72386639100797ABC /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 3432AF7E2ADFE3610029F2D7 /* libz_local.1.2.5.dylib in CopyFiles */, - 3432AF7D2ADFE35C0029F2D7 /* libpng16.15.29.0.dylib in CopyFiles */, + 3432AF7E2ADFE3610029F2D7 /* libz.1.3.1.dylib in CopyFiles */, + 3432AF7D2ADFE35C0029F2D7 /* libpng16.16.dylib in CopyFiles */, 343FB8C82386639C00797ABC /* LgiCocoa.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3432AF792ADFE3450029F2D7 /* libz_local.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz_local.1.2.5.dylib; path = ../../../../../codelib/libpng/build/release/zlib_dir/libz_local.1.2.5.dylib; sourceTree = ""; }; - 3432AF7B2ADFE34B0029F2D7 /* libpng16.15.29.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpng16.15.29.0.dylib; path = ../../../../../codelib/libpng/build/release/libpng16.15.29.0.dylib; sourceTree = ""; }; + 3432AF792ADFE3450029F2D7 /* libz.1.3.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.3.1.dylib; path = ../../../../../../../opt/local/lib/libz.1.3.1.dylib; sourceTree = ""; }; + 3432AF7B2ADFE34B0029F2D7 /* libpng16.16.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpng16.16.dylib; path = /opt/local/lib/libpng16.16.dylib; sourceTree = ""; }; 343FB88E2386621400797ABC /* LgiRes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LgiRes.app; sourceTree = BUILT_PRODUCTS_DIR; }; 343FB8942386621600797ABC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 343FB8992386621600797ABC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 343FB89C2386621600797ABC /* LgiRes.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LgiRes.entitlements; sourceTree = ""; }; 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LgiCocoa.xcodeproj; path = ../../src/mac/cocoa/LgiCocoa.xcodeproj; sourceTree = ""; }; 343FB8AA238662C500797ABC /* LgiMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiMain.cpp; path = ../../src/common/Lgi/LgiMain.cpp; sourceTree = ""; }; 343FB8DB2386642A00797ABC /* OptionsFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionsFile.cpp; path = ../../src/common/Lgi/OptionsFile.cpp; sourceTree = ""; }; 34CC001B23AAD2A100CCAAEA /* Search.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Search.cpp; path = ../src/Search.cpp; sourceTree = ""; }; 34CC001C23AAD2A100CCAAEA /* LgiRes_ControlTree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_ControlTree.cpp; path = ../src/LgiRes_ControlTree.cpp; sourceTree = ""; }; 34CC001D23AAD2A100CCAAEA /* LgiRes_Dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiRes_Dialog.h; path = ../src/LgiRes_Dialog.h; sourceTree = ""; }; 34CC001E23AAD2A100CCAAEA /* LgiRes_TableLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_TableLayout.cpp; path = ../src/LgiRes_TableLayout.cpp; sourceTree = ""; }; 34CC001F23AAD2A100CCAAEA /* LgiResEdit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiResEdit.h; path = ../src/LgiResEdit.h; sourceTree = ""; }; 34CC002023AAD2A100CCAAEA /* LgiRes_Menu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_Menu.cpp; path = ../src/LgiRes_Menu.cpp; sourceTree = ""; }; 34CC002123AAD2A100CCAAEA /* LgiRes_Css.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_Css.cpp; path = ../src/LgiRes_Css.cpp; sourceTree = ""; }; 34CC002223AAD2A100CCAAEA /* LgiRes_String.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiRes_String.h; path = ../src/LgiRes_String.h; sourceTree = ""; }; 34CC002323AAD2A100CCAAEA /* LgiResApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiResApp.cpp; path = ../src/LgiResApp.cpp; sourceTree = ""; }; 34CC002423AAD2A100CCAAEA /* ShowLanguages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ShowLanguages.cpp; path = ../src/ShowLanguages.cpp; sourceTree = ""; }; 34CC002523AAD2A100CCAAEA /* LgiRes_Menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiRes_Menu.h; path = ../src/LgiRes_Menu.h; sourceTree = ""; }; 34CC002623AAD2A100CCAAEA /* LgiRes_Dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_Dialog.cpp; path = ../src/LgiRes_Dialog.cpp; sourceTree = ""; }; 34CC002723AAD2A100CCAAEA /* LgiRes_String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes_String.cpp; path = ../src/LgiRes_String.cpp; sourceTree = ""; }; 34CC003123AAD2C100CCAAEA /* StatusBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StatusBar.cpp; path = ../../src/common/Widgets/StatusBar.cpp; sourceTree = ""; }; 34CC003323AAD2E700CCAAEA /* Mru.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mru.cpp; path = ../../src/common/Lgi/Mru.cpp; sourceTree = ""; }; 34CC003523AAD2FD00CCAAEA /* DocApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocApp.cpp; path = ../../src/common/Lgi/DocApp.cpp; sourceTree = ""; }; 34CC003623AAD2FE00CCAAEA /* About.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = About.cpp; path = ../../src/common/Lgi/About.cpp; sourceTree = ""; }; 34CC003A23AAD33500CCAAEA /* mac-icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = "mac-icon.icns"; path = "../Resources/mac-icon.icns"; sourceTree = ""; }; 34CC003B23AAD33500CCAAEA /* lgires.lr8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = lgires.lr8; path = ../Resources/lgires.lr8; sourceTree = ""; }; 34CC003E23AAD34600CCAAEA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 34CC004123AAD54200CCAAEA /* Png.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Png.cpp; path = ../../src/common/Gdc2/Filters/Png.cpp; sourceTree = ""; }; 34CC006223AAD5DB00CCAAEA /* _StringIcons.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = _StringIcons.gif; path = ../resources/_StringIcons.gif; sourceTree = ""; }; 34CC006323AAD5DB00CCAAEA /* _DialogIcons.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = _DialogIcons.gif; path = ../resources/_DialogIcons.gif; sourceTree = ""; }; 34CC006423AAD5DB00CCAAEA /* _MenuIcons.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = _MenuIcons.gif; path = ../resources/_MenuIcons.gif; sourceTree = ""; }; 34CC006523AAD5DB00CCAAEA /* _Icons.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = _Icons.gif; path = ../resources/_Icons.gif; sourceTree = ""; }; 34CC006A23AAD5FC00CCAAEA /* Gif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gif.cpp; path = ../../src/common/Gdc2/Filters/Gif.cpp; sourceTree = ""; }; 34CC006B23AAD5FC00CCAAEA /* Lzw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Lzw.cpp; path = ../../src/common/Gdc2/Filters/Lzw.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 343FB88B2386621400797ABC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 34CC003F23AAD34600CCAAEA /* Cocoa.framework in Frameworks */, 343FB8C62386638C00797ABC /* LgiCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 343FB8852386621400797ABC = { isa = PBXGroup; children = ( 343FB8A22386625100797ABC /* Cocoa */, 343FB8B9238662DB00797ABC /* Code */, 343FB8C52386638C00797ABC /* Frameworks */, 343FB8A32386626700797ABC /* Lgi */, 343FB88F2386621400797ABC /* Products */, 34CC003923AAD32200CCAAEA /* Resources */, ); sourceTree = ""; }; 343FB88F2386621400797ABC /* Products */ = { isa = PBXGroup; children = ( 343FB88E2386621400797ABC /* LgiRes.app */, ); name = Products; sourceTree = ""; }; 343FB8A22386625100797ABC /* Cocoa */ = { isa = PBXGroup; children = ( 343FB8942386621600797ABC /* Assets.xcassets */, 343FB8992386621600797ABC /* Info.plist */, 343FB89C2386621600797ABC /* LgiRes.entitlements */, ); name = Cocoa; sourceTree = ""; }; 343FB8A32386626700797ABC /* Lgi */ = { isa = PBXGroup; children = ( 34CC006A23AAD5FC00CCAAEA /* Gif.cpp */, 34CC006B23AAD5FC00CCAAEA /* Lzw.cpp */, 34CC003623AAD2FE00CCAAEA /* About.cpp */, 34CC003523AAD2FD00CCAAEA /* DocApp.cpp */, 34CC003323AAD2E700CCAAEA /* Mru.cpp */, 343FB8DB2386642A00797ABC /* OptionsFile.cpp */, 34CC003123AAD2C100CCAAEA /* StatusBar.cpp */, 343FB8AA238662C500797ABC /* LgiMain.cpp */, 34CC004123AAD54200CCAAEA /* Png.cpp */, 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */, ); name = Lgi; sourceTree = ""; }; 343FB8A52386627A00797ABC /* Products */ = { isa = PBXGroup; children = ( 343FB8A92386627A00797ABC /* LgiCocoa.framework */, ); name = Products; sourceTree = ""; }; 343FB8B9238662DB00797ABC /* Code */ = { isa = PBXGroup; children = ( 34CC001C23AAD2A100CCAAEA /* LgiRes_ControlTree.cpp */, 34CC002123AAD2A100CCAAEA /* LgiRes_Css.cpp */, 34CC002623AAD2A100CCAAEA /* LgiRes_Dialog.cpp */, 34CC001D23AAD2A100CCAAEA /* LgiRes_Dialog.h */, 34CC002023AAD2A100CCAAEA /* LgiRes_Menu.cpp */, 34CC002523AAD2A100CCAAEA /* LgiRes_Menu.h */, 34CC002723AAD2A100CCAAEA /* LgiRes_String.cpp */, 34CC002223AAD2A100CCAAEA /* LgiRes_String.h */, 34CC001E23AAD2A100CCAAEA /* LgiRes_TableLayout.cpp */, 34CC002323AAD2A100CCAAEA /* LgiResApp.cpp */, 34CC001F23AAD2A100CCAAEA /* LgiResEdit.h */, 34CC001B23AAD2A100CCAAEA /* Search.cpp */, 34CC002423AAD2A100CCAAEA /* ShowLanguages.cpp */, ); name = Code; sourceTree = ""; }; 343FB8C52386638C00797ABC /* Frameworks */ = { isa = PBXGroup; children = ( - 3432AF7B2ADFE34B0029F2D7 /* libpng16.15.29.0.dylib */, - 3432AF792ADFE3450029F2D7 /* libz_local.1.2.5.dylib */, + 3432AF7B2ADFE34B0029F2D7 /* libpng16.16.dylib */, + 3432AF792ADFE3450029F2D7 /* libz.1.3.1.dylib */, 34CC003E23AAD34600CCAAEA /* Cocoa.framework */, ); name = Frameworks; sourceTree = ""; }; 34CC003923AAD32200CCAAEA /* Resources */ = { isa = PBXGroup; children = ( 34CC006323AAD5DB00CCAAEA /* _DialogIcons.gif */, 34CC006523AAD5DB00CCAAEA /* _Icons.gif */, 34CC006423AAD5DB00CCAAEA /* _MenuIcons.gif */, 34CC006223AAD5DB00CCAAEA /* _StringIcons.gif */, 34CC003B23AAD33500CCAAEA /* lgires.lr8 */, 34CC003A23AAD33500CCAAEA /* mac-icon.icns */, ); name = Resources; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 343FB88D2386621400797ABC /* LgiRes */ = { isa = PBXNativeTarget; buildConfigurationList = 343FB89F2386621600797ABC /* Build configuration list for PBXNativeTarget "LgiRes" */; buildPhases = ( 343FB88A2386621400797ABC /* Sources */, 343FB88B2386621400797ABC /* Frameworks */, 343FB88C2386621400797ABC /* Resources */, 343FB8C72386639100797ABC /* CopyFiles */, ); buildRules = ( ); dependencies = ( 343FB8C42386638800797ABC /* PBXTargetDependency */, ); name = LgiRes; productName = LgiRes; productReference = 343FB88E2386621400797ABC /* LgiRes.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 343FB8862386621400797ABC /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1420; ORGANIZATIONNAME = Memecode; TargetAttributes = { 343FB88D2386621400797ABC = { CreatedOnToolsVersion = 10.1; ProvisioningStyle = Manual; }; }; }; buildConfigurationList = 343FB8892386621400797ABC /* Build configuration list for PBXProject "LgiRes" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 343FB8852386621400797ABC; productRefGroup = 343FB88F2386621400797ABC /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 343FB8A52386627A00797ABC /* Products */; ProjectRef = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; }, ); projectRoot = ""; targets = ( 343FB88D2386621400797ABC /* LgiRes */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 343FB8A92386627A00797ABC /* LgiCocoa.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = LgiCocoa.framework; remoteRef = 343FB8A82386627A00797ABC /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 343FB88C2386621400797ABC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 343FB8952386621600797ABC /* Assets.xcassets in Resources */, 34CC003C23AAD33500CCAAEA /* mac-icon.icns in Resources */, 34CC006623AAD5DB00CCAAEA /* _StringIcons.gif in Resources */, 34CC006723AAD5DB00CCAAEA /* _DialogIcons.gif in Resources */, 34CC006823AAD5DB00CCAAEA /* _MenuIcons.gif in Resources */, 34CC006923AAD5DB00CCAAEA /* _Icons.gif in Resources */, 34CC003D23AAD33500CCAAEA /* lgires.lr8 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 343FB88A2386621400797ABC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 34CC002B23AAD2A100CCAAEA /* LgiRes_Menu.cpp in Sources */, 34CC002C23AAD2A100CCAAEA /* LgiRes_Css.cpp in Sources */, 343FB8DC2386642A00797ABC /* OptionsFile.cpp in Sources */, 34CC002F23AAD2A100CCAAEA /* LgiRes_Dialog.cpp in Sources */, 34CC003723AAD2FE00CCAAEA /* DocApp.cpp in Sources */, 34CC003823AAD2FE00CCAAEA /* About.cpp in Sources */, 34CC002E23AAD2A100CCAAEA /* ShowLanguages.cpp in Sources */, 34CC002923AAD2A100CCAAEA /* LgiRes_ControlTree.cpp in Sources */, 34CC003023AAD2A100CCAAEA /* LgiRes_String.cpp in Sources */, 34CC006D23AAD5FC00CCAAEA /* Lzw.cpp in Sources */, 34CC002823AAD2A100CCAAEA /* Search.cpp in Sources */, 34CC006C23AAD5FC00CCAAEA /* Gif.cpp in Sources */, 34CC002D23AAD2A100CCAAEA /* LgiResApp.cpp in Sources */, 34CC003423AAD2E700CCAAEA /* Mru.cpp in Sources */, 34CC002A23AAD2A100CCAAEA /* LgiRes_TableLayout.cpp in Sources */, 34CC004223AAD54200CCAAEA /* Png.cpp in Sources */, 343FB8AB238662C500797ABC /* LgiMain.cpp in Sources */, 34CC003223AAD2C100CCAAEA /* StatusBar.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 343FB8C42386638800797ABC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = LgiCocoa; targetProxy = 343FB8C32386638800797ABC /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 343FB89D2386621600797ABC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 343FB89E2386621600797ABC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; }; name = Release; }; 343FB8A02386621600797ABC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 2AV9WN2LD8; HEADER_SEARCH_PATHS = ( + ../src, + ../resources, ../../include, ../../include/lgi/mac/cocoa, - ../src, - ../resources, - ../../../../../CodeLib/libpng/build/release, - ../../../../../CodeLib/libpng, + /opt/local/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CFLAGS = ( "-DMAC", "-DLGI_COCOA", "-D_DEBUG", "-Wno-nullability-completeness", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiRes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; }; name = Debug; }; 343FB8A12386621600797ABC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 2AV9WN2LD8; HEADER_SEARCH_PATHS = ( + ../src, + ../resources, ../../include, ../../include/lgi/mac/cocoa, - ../src, - ../resources, - ../../../../../CodeLib/libpng/build/release, - ../../../../../CodeLib/libpng, + /opt/local/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CFLAGS = ( "-DMAC", "-DLGI_COCOA", "-Wno-nullability-completeness", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiRes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 343FB8892386621400797ABC /* Build configuration list for PBXProject "LgiRes" */ = { isa = XCConfigurationList; buildConfigurations = ( 343FB89D2386621600797ABC /* Debug */, 343FB89E2386621600797ABC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 343FB89F2386621600797ABC /* Build configuration list for PBXNativeTarget "LgiRes" */ = { isa = XCConfigurationList; buildConfigurations = ( 343FB8A02386621600797ABC /* Debug */, 343FB8A12386621600797ABC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 343FB8862386621400797ABC /* Project object */; } diff --git a/src/common/General/DateTime.cpp b/src/common/General/DateTime.cpp --- a/src/common/General/DateTime.cpp +++ b/src/common/General/DateTime.cpp @@ -1,2585 +1,2590 @@ /* ** FILE: LDateTime.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Scribe Date Time Object ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _DEFAULT_SOURCE #include #include #include #include #include #if defined(MAC) #include #endif #ifdef WINDOWS #include #define timegm _mkgmtime #endif #include "lgi/common/Lgi.h" #include "lgi/common/DateTime.h" #include "lgi/common/DocView.h" constexpr const char *LDateTime::WeekdaysShort[7]; constexpr const char *LDateTime::WeekdaysLong[7]; constexpr const char *LDateTime::MonthsShort[12]; constexpr const char *LDateTime::MonthsLong[12]; #if !defined(WINDOWS) #define MIN_YEAR 1800 #endif #if defined(LINUX) #define USE_ZDUMP 1 #elif defined(HAIKU) #include "lgi/common/TimeZoneInfo.h" #endif #define DEBUG_DST_INFO 0 ////////////////////////////////////////////////////////////////////////////// uint16 LDateTime::DefaultFormat = GDTF_DEFAULT; char LDateTime::DefaultSeparator = '/'; uint16 LDateTime::GetDefaultFormat() { if (DefaultFormat == GDTF_DEFAULT) { #ifdef WIN32 TCHAR s[80] = _T("1"); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s)); switch (_tstoi(s)) { case 0: DefaultFormat = GDTF_MONTH_DAY_YEAR; break; default: case 1: DefaultFormat = GDTF_DAY_MONTH_YEAR; break; case 2: DefaultFormat = GDTF_YEAR_MONTH_DAY; break; } GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s)); if (_tstoi(s) == 1) { DefaultFormat |= GDTF_24HOUR; } else { DefaultFormat |= GDTF_12HOUR; } if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s))) DefaultSeparator = (char)s[0]; if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s))) { char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 }; LString Str = s; auto t = Str.SplitDelimit(Sep); for (int i=0; i= low && (v) <= high) bool LDateTime::IsValid() const { return InRange(_Day, 1, 31 ) && InRange(_Year, 1600, 2100) && InRange(_Thousands, 0, 999 ) && InRange(_Month, 1, 12 ) && InRange(_Seconds, 0, 59 ) && InRange(_Minutes, 0, 59 ) && InRange(_Hours, 0, 23 ) && InRange(_Tz, -780, 780 ); } void LDateTime::SetTimeZone(int NewTz, bool ConvertTime) { if (ConvertTime && NewTz != _Tz) AddMinutes(NewTz - _Tz); _Tz = NewTz; } LDateTime::operator struct tm() const { struct tm t = {}; t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; return t; } #ifdef WINDOWS LDateTime::operator SYSTEMTIME() const { SYSTEMTIME System = {}; System.wYear = _Year; System.wMonth = limit(_Month, 1, 12); System.wDay = limit(_Day, 1, 31); System.wHour = limit(_Hours, 0, 23); System.wMinute = limit(_Minutes, 0, 59); System.wSecond = limit(_Seconds, 0, 59); System.wMilliseconds = limit(_Thousands, 0, 999); System.wDayOfWeek = DayOfWeek(); return System; } LDateTime &LDateTime::operator =(const SYSTEMTIME &st) { _Year = st.wYear; _Month = st.wMonth; _Day = st.wDay; _Hours = st.wHour; _Minutes = st.wMinute; _Seconds = st.wSecond; _Thousands = st.wMilliseconds; return *this; } #endif bool LDateTime::InferTimeZone(bool ConvertTime) { #ifdef WINDOWS SYSTEMTIME System = *this; // Compare the non-year parts of the SYSTEMTIMEs auto SysCmp = [](SYSTEMTIME &a, SYSTEMTIME &b) { #define CMP(name) if (auto d = a.name - b.name) return d; CMP(wMonth); CMP(wDay); CMP(wHour); CMP(wMinute); CMP(wSecond); CMP(wMilliseconds); return 0; }; auto MakeAbsolute = [this](SYSTEMTIME &s) { if (s.wYear == 0) { // Convert from relative to absolute... LDateTime dt = *this; dt.Month(s.wMonth); int days = dt.DaysInMonth(); if (s.wDay == 5) { // Use the final day of the month for (int day = days; day > 0; day--) { dt.Day(day); if (dt.DayOfWeek() == s.wDayOfWeek) break; } s.wDay = dt.Day(); } else if (s.wDay > 0) { // Find the 'wDay'th instance of 'wDayOfWeek' int week = 1; for (int day = 1; day <= days; day++) { dt.Day(day); if (dt.DayOfWeek() == s.wDayOfWeek) { if (week++ == s.wDay) break; } } s.wDay = dt.Day(); } else LAssert(!"unexpected relative date"); } }; // 'System' is currently in 'UTC' but we want the local version of the time, // not in the _Tz timezone, but the effective timezone for that // actual moment in question, which could have a different DST offset. TIME_ZONE_INFORMATION tzi = {}; if (!GetTimeZoneInformationForYear(_Year, NULL, &tzi)) return false; MakeAbsolute(tzi.StandardDate); MakeAbsolute(tzi.DaylightDate); auto order = SysCmp(tzi.DaylightDate, tzi.StandardDate); // order > 0 = DaylightDate is after StandardDate // order < 0 = DaylightDate is before StandardDate LAssert(order != 0); auto a = SysCmp(System, tzi.StandardDate); // a > 0 = System is after StandardDate // a < 0 = System is before StandardDate auto b = SysCmp(System, tzi.DaylightDate); // b > 0 = System is after DaylightDate // b < 0 = System is before DaylightDate int tz = (int16)-tzi.Bias; if (order > 0) { // year is: DST -> Normal -> DST if (a < 0 || b > 0) tz -= (int16)tzi.DaylightBias; } else { // year is: Normal -> DST -> Normal if (a < 0 && b > 0) tz -= (int16)tzi.DaylightBias; } if (ConvertTime) SetTimeZone(tz - _Tz, true); else _Tz = tz; return true; #else auto tt = GetUnix(); auto local = localtime(&tt); if (!local) return false; auto tz = local->tm_gmtoff / 60; if (ConvertTime) SetTimeZone(tz - _Tz, true); else _Tz = (int16)tz; return true; #endif return false; } int LDateTime::SystemTimeZone(bool ForceUpdate) { if (ForceUpdate || CurTz == NO_ZONE) { CurTz = 0; CurTzOff = 0; #ifdef MAC #ifdef LGI_COCOA NSTimeZone *timeZone = [NSTimeZone localTimeZone]; if (timeZone) { NSDate *Now = [NSDate date]; CurTz = (int) [timeZone secondsFromGMTForDate:Now] / 60; CurTzOff = [timeZone daylightSavingTimeOffsetForDate:Now] / 60; CurTz -= CurTzOff; } #elif defined LGI_CARBON CFTimeZoneRef tz = CFTimeZoneCopySystem(); CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now); if (dst) { CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now); CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60; } else { CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60; } CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60; CFRelease(tz); #endif #elif defined(WIN32) timeb tbTime; ftime(&tbTime); CurTz = -tbTime.timezone; TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) CurTzOff = -Tzi.DaylightBias; #elif defined(LINUX) || defined(HAIKU) int six_months = (365 * 24 * 60 * 60) / 2; time_t now = 0, then = 0; time (&now); then = now - six_months; tm now_tz, then_tz; tm *t = localtime_r(&now, &now_tz); if (t) { localtime_r(&then, &then_tz); CurTz = now_tz.tm_gmtoff / 60; if (now_tz.tm_isdst) { CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60; CurTz = then_tz.tm_gmtoff / 60; } else // This is not DST so there is no offset right? CurTzOff = 0; // (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60; } else return NO_ZONE; #else #error "Impl me." #endif } return CurTz + CurTzOff; } int LDateTime::SystemTimeZoneOffset() { if (CurTz == NO_ZONE) SystemTimeZone(); return CurTzOff; } #if defined WIN32 LDateTime ConvertSysTime(SYSTEMTIME &st, int year) { LDateTime n; if (st.wYear) { n.Year(st.wYear); n.Month(st.wMonth); n.Day(st.wDay); } else { n.Year(year); n.Month(st.wMonth); // Find the 'nth' matching weekday, starting from the first day in the month n.Day(1); LDateTime c = n; for (int i=0; iCompare(b); } #elif USE_ZDUMP static bool ParseValue(char *s, LString &var, LString &val) { if (!s) return false; char *e = strchr(s, '='); if (!e) return false; *e++ = 0; var = s; val = e; *e = '='; return var != 0 && val != 0; } #endif /* Testing code... LDateTime Start, End; LArray Info; Start.Set("1/1/2010"); End.Set("31/12/2014"); LDateTime::GetDaylightSavingsInfo(Info, Start, &End); LStringPipe p; for (int i=0; i,int> { MonthHash() { for (int i=0; i &Info, LDateTime &Start, LDateTime *End) { bool Status = false; #if defined(WIN32) TIME_ZONE_INFORMATION Tzi; auto r = GetTimeZoneInformation(&Tzi); if (r > TIME_ZONE_ID_UNKNOWN) { Info.Length(0); // Find the dates for the previous year from Start. This allows // us to cover the start of the current year. LDateTime s = ConvertSysTime(Tzi.StandardDate, Start.Year() - 1); LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year() - 1); // Create initial Info entry, as the last change in the previous year auto *i = &Info.New(); if (s < d) { // Year is: Daylight->Standard->Daylight LDateTime tmp = d; i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } else { // Year is: Standard->Daylight->Standard LDateTime tmp = s; i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } for (auto y=Start.Year(); y<=(End?End->Year():Start.Year()); y++) { if (s < d) { // Cur year, first event: end of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.StandardDate, y); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; // Cur year, second event: start of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.DaylightDate, y); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } else { // Cur year, first event: start of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.DaylightDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; // Cur year, second event: end of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.StandardDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } } Status = true; } #elif defined(MAC) LDateTime From = Start; From.AddMonths(-6); LDateTime To = End ? *End : Start; To.AddMonths(6); auto ToUnix = To.GetUnix(); auto tz = [NSTimeZone systemTimeZone]; auto startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(From.Ts().Get() / Second64Bit) - Offset1800]; while (startDate) { auto next = [tz nextDaylightSavingTimeTransitionAfterDate:startDate]; auto &i = Info.New(); - auto nextTs = [next timeIntervalSince1970]; - i.Utc = (nextTs + Offset1800) * Second64Bit; + auto nextTs = (time_t)[next timeIntervalSince1970]; + i.Utc = nextTs; i.Offset = (int)([tz secondsFromGMTForDate:[next dateByAddingTimeInterval:60]]/60); #if DEBUG_DST_INFO { LDateTime dt; - dt.Set(i.UtcTimeStamp); + dt.Set(i.Utc); LgiTrace("%s:%i - Ts=%s Off=%i\n", _FL, dt.Get().Get(), i.Offset); } #endif if (nextTs >= ToUnix) break; [startDate release]; startDate = next; } if (startDate) [startDate release]; #elif USE_ZDUMP if (!Zdump.Length()) { static bool First = true; auto linkLoc = "/etc/localtime"; #if defined(LINUX) auto zoneLoc = "/usr/share/zoneinfo"; #elif defined(HAIKU) auto zoneLoc = "/boot/system/data/zoneinfo"; #else #error "Impl me" #endif if (!LFileExists(linkLoc)) { if (First) { LgiTrace("%s:%i - LDateTime::GetDaylightSavingsInfo error: '%s' doesn't exist.\n" " It should link to something in the '%s' tree.\n", _FL, linkLoc, zoneLoc); #ifdef HAIKU LgiTrace(" To fix that: pkgman install timezone_data and then create the '%s' link.\n", linkLoc); #endif } return First = false; } auto f = popen(LString::Fmt("zdump -v %s", linkLoc), "r"); if (f) { char s[1024]; size_t r; LStringPipe p(1024); while ((r = fread(s, 1, sizeof(s), f)) > 0) p.Write(s, (int)r); fclose(f); Zdump = p.NewLStr().Split("\n"); } else { if (First) { LgiTrace("%s:%i - LDateTime::GetDaylightSavingsInfo error: zdump didn't run.\n", _FL); #ifdef HAIKU LgiTrace("To fix that: pkgman install timezone_data\n"); #endif } return First = false; } } MonthHash Lut; LDateTime Prev; int PrevOff = 0; for (auto Line: Zdump) { auto l = Line.SplitDelimit(" \t"); if (l.Length() >= 16 && l[0].Equals("/etc/localtime")) { // /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LDateTime Utc; Utc.Year(l[5].Int()); #if DEBUG_DST_INFO if (Utc.Year() < 2020) continue; // printf("DST: %s\n", Line.Get()); #endif auto Tm = l[4].SplitDelimit(":"); if (Tm.Length() != 3) { #if DEBUG_DST_INFO printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4].Get(), Line.Get()); #endif continue; } Utc.Hours(Tm[0].Int()); Utc.Minutes(Tm[1].Int()); Utc.Seconds(Tm[2].Int()); if (Utc.Minutes() < 0) { #if DEBUG_DST_INFO printf("%s:%i - Mins is zero: %s\n", _FL, l[4].Get()); #endif continue; } int m = Lut.Find(l[2]); if (!m) { #if DEBUG_DST_INFO printf("%s:%i - Unknown month '%s'\n", _FL, l[2].Get()); #endif continue; } Utc.Day(l[3].Int()); Utc.Month(m); LString Var, Val; if (!ParseValue(l[14], Var, Val) || Var != "isdst") { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } if (!ParseValue(l[15], Var, Val) || Var != "gmtoff") { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } int Off = atoi(Val) / 60; if (Utc.Ts().Valid() == 0) continue; if (Prev.Year() && Prev < Start && Start < Utc) { // Emit initial entry for 'start' auto &inf = Info.New(); Prev.Get(inf.Utc); inf.Offset = PrevOff; #if DEBUG_DST_INFO printf("Info: Start=%s %i\n", Prev.Get().Get(), inf.Offset); #endif } if (Utc > Start) { // Emit furthur entries for DST events between start and end. auto &inf = Info.New(); Utc.Get(inf.Utc); inf.Offset = Off; #if DEBUG_DST_INFO printf("Info: Next=%s %i\n", Utc.Get().Get(), inf.Offset); #endif if (End && Utc > *End) { // printf("Utc after end: %s > %s\n", Utc.Get().Get(), End->Get().Get()); break; } } Prev = Utc; PrevOff = Off; } } Status = Info.Length() > 1; #elif defined(HAIKU) LTimeZoneInfo tzinfo; if (!tzinfo.Read()) { #if DEBUG_DST_INFO LgiTrace("%s:%i - info read failed.\n", _FL); #endif return false; } Status = tzinfo.GetDaylightSavingsInfo(Info, Start, End); #if DEBUG_DST_INFO if (!Status) printf("%s:%i - GetDaylightSavingsInfo failed.\n", _FL); #endif #else LAssert(!"Not implemented."); #endif return Status; } bool LDateTime::DstToLocal(LArray &Dst, LDateTime &dt) { if (dt.GetTimeZone()) { LAssert(!"Should be a UTC date."); return true; } #if DEBUG_DST_INFO LgiTrace("DstToLocal: %s\n", dt.Get().Get()); #endif LAssert(Dst.Length() > 1); // Needs to have at least 2 entries...? for (size_t i=0; i= start && dt < end; if (InRange) { dt.SetTimeZone(a.Offset, true); #if DEBUG_DST_INFO LgiTrace("\tRng[%i]: %s -> %s, SetTimeZone(%g), dt=%s\n", (int)i, start.Get().Get(), end.Get().Get(), (double)a.Offset/60.0, dt.Get().Get()); #endif return true; } } auto Last = Dst.Last(); LDateTime d; d.Set(Last.Utc); if (dt >= d && dt.Year() == d.Year()) { // If it's after the last DST change but in the same year... it's ok... // Just use the last offset. dt.SetTimeZone(Last.Offset, true); return true; } #if DEBUG_DST_INFO for (auto d: Dst) LgiTrace("Dst: %s = %i\n", d.GetLocal().Get().Get(), d.Offset); #endif LgiTrace("%s:%i - No valid DST range for: %s\n", _FL, dt.Get().Get()); - LAssert(!"No valid DST range for this date."); + static bool first = true; + if (first) + { + first = false; + LAssert(!"No valid DST range for this date."); + } return false; } int LDateTime::DayOfWeek() const { int Index = 0; int Day = IsLeapYear() ? 29 : 28; switch (_Year / 100) { case 19: { Index = 3; break; } case 20: { Index = 2; break; } } // get year right int y = _Year % 100; int r = y % 12; Index = (Index + (y / 12) + r + (r / 4)) % 7; // get month right if (_Month % 2 == 0) { // even month if (_Month > 2) Day = _Month; } else { // odd month switch (_Month) { case 1: { Day = 31; if (IsLeapYear()) { Index = Index > 0 ? Index - 1 : Index + 6; } break; } case 11: case 3: { Day = 7; break; } case 5: { Day = 9; break; } case 7: { Day = 11; break; } case 9: { Day = 5; break; } } } // get day right int Diff = Index - (Day - _Day); while (Diff < 0) Diff += 7; return Diff % 7; } LDateTime LDateTime::Now() { LDateTime dt; dt.SetNow(); return dt; } LDateTime &LDateTime::SetNow() { #ifdef WIN32 SYSTEMTIME stNow; auto sysTz = SystemTimeZone(); if (_Tz == sysTz) GetLocalTime(&stNow); else GetSystemTime(&stNow); #if 1 *this = stNow; if (_Tz && _Tz != sysTz) { // Adjust to this objects timezone... auto tz = _Tz; _Tz = 0; SetTimeZone(tz, true); } #else // This is actually less efficient. FILETIME ftNow; SystemTimeToFileTime(&stNow, &ftNow); uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime; OsTime(i64); #endif #else time_t now; time(&now); struct tm *time = localtime(&now); if (time) *this = time; #ifndef LGI_STATIC else { LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now); } #endif #endif return *this; } #define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) #define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" ) LString LDateTime::GetDate() const { char s[32]; int Ch = GetDate(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetDate(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; default: case GDTF_DAY_MONTH_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; case GDTF_YEAR_MONTH_DAY: Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); break; } } return Ch; } LString LDateTime::GetTime() const { char s[32]; int Ch = GetTime(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetTime(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_TIME_MASK) { case GDTF_12HOUR: default: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours)); break; } case GDTF_24HOUR: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds); break; } } } return Ch; } LTimeStamp LDateTime::Ts() const { LTimeStamp ts; Get(ts); return ts; } time_t LDateTime::GetUnix() { /* This isn't unit time? LTimeStamp s; Get(s); #if defined(WINDOWS) return s.Get() / LDateTime::Second64Bit / 116445168000000000LL; #else return s.Get() / LDateTime::Second64Bit - Offset1800; #endif */ struct tm t = *this; time_t tt = timegm(&t); if (_Tz) tt -= _Tz * 60; return tt; } bool LDateTime::SetUnix(time_t tt) { /* This isn't unit time? #if defined(WINDOWS) return Set(s * LDateTime::Second64Bit + 116445168000000000LL); #else return Set((s + Offset1800) * LDateTime::Second64Bit); #endif */ struct tm *t; if (_Tz) tt += _Tz * 60; #if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 t = gmtime(&tt); if (t) #else struct tm tmp; if (_gmtime64_s(t = &tmp, &tt) == 0) #endif { return Set(t, false); } return false; } bool LDateTime::Set(const LTimeStamp &s) { #if defined WIN32 return OsTime(s.Get()); #else SetUnix(s.Unix()); _Thousands = s.Get() % Second64Bit; return true; #endif } bool LDateTime::Set(struct tm *t, bool inferTimezone) { if (!t) return false; _Year = t->tm_year + 1900; _Month = t->tm_mon + 1; _Day = t->tm_mday; _Hours = t->tm_hour; _Minutes = t->tm_min; _Seconds = t->tm_sec; _Thousands = 0; if (inferTimezone) { auto diff = timegm(t) - mktime(t); _Tz = (int16)(diff / 60); } return true; } uint64_t LDateTime::OsTime() const { #ifdef WINDOWS FILETIME Utc; SYSTEMTIME System = *this; if (SystemTimeToFileTime(&System, &Utc)) { uint64_t s = ((uint64_t)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime; if (_Tz) // Adjust for timezone s -= (int64)_Tz * 60 * Second64Bit; return s; } else { DWORD Err = GetLastError(); LAssert(!"SystemTimeToFileTime failed."); } #else if (_Year < MIN_YEAR) return 0; struct tm t; ZeroObj(t); t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; time_t sec = timegm(&t); if (sec == -1) return 0; if (_Tz) { // Adjust the output to UTC from the current timezone. sec -= _Tz * 60; } return sec; #endif return 0; } bool LDateTime::OsTime(uint64_t ts) { #ifdef WINDOWS if (_Tz) // Adjust for timezone ts += (int64)_Tz * 60 * Second64Bit; FILETIME Utc; Utc.dwHighDateTime = ts >> 32; Utc.dwLowDateTime = ts & 0xffffffff; SYSTEMTIME System; if (!FileTimeToSystemTime(&Utc, &System)) { DWORD Err = GetLastError(); LAssert(!"FileTimeToSystemTime failed."); return false; } *this = System; return true; #else return SetUnix((time_t)ts); #endif } bool LDateTime::Get(LTimeStamp &s) const { #ifdef WINDOWS if (!IsValid()) { LAssert(!"Needs a valid date."); return false; } s.Ref() = OsTime(); if (!s.Valid()) return false; return true; #else if (_Year < MIN_YEAR) return false; auto sec = OsTime(); s.Ref() = (uint64)(sec + Offset1800) * Second64Bit + _Thousands; return true; #endif } LString LDateTime::Get() const { char buf[32]; int Ch = GetDate(buf, sizeof(buf)); buf[Ch++] = ' '; Ch += GetTime(buf+Ch, sizeof(buf)-Ch); return LString(buf, Ch); } void LDateTime::Get(char *Str, size_t SLen) const { if (Str) { GetDate(Str, SLen); size_t len = strlen(Str); if (len < SLen - 1) { Str[len++] = ' '; GetTime(Str+len, SLen-len); } } } bool LDateTime::Set(const char *Str) { if (!Str) return false; if (Strlen(Str) > 100) return false; char Local[256]; strcpy_s(Local, sizeof(Local), Str); char *Sep = strchr(Local, ' '); if (Sep) { *Sep++ = 0; if (!SetTime(Sep)) return false; } if (!SetDate(Local)) return false; return true; } void LDateTime::Month(char *m) { int i = IsMonth(m); if (i >= 0) _Month = i + 1; } int DateComponent(const char *s) { int64 i = Atoi(s); return i ? (int)i : LDateTime::IsMonth(s); } bool LDateTime::SetDate(const char *Str) { bool Status = false; if (Str) { auto T = LString(Str).SplitDelimit("/-.,_\\"); if (T.Length() == 3) { int i[3] = { DateComponent(T[0]), DateComponent(T[1]), DateComponent(T[2]) }; int fmt = _Format & GDTF_DATE_MASK; // Do some guessing / overrides. // Don't let _Format define the format completely. if (i[0] > 1000) { fmt = GDTF_YEAR_MONTH_DAY; } else if (i[2] > 1000) { if (i[0] > 12) fmt = GDTF_DAY_MONTH_YEAR; else if (i[1] > 12) fmt = GDTF_MONTH_DAY_YEAR; } switch (fmt) { case GDTF_MONTH_DAY_YEAR: { _Month = i[0]; _Day = i[1]; _Year = i[2]; break; } case GDTF_DAY_MONTH_YEAR: { _Day = i[0]; _Month = i[1]; _Year = i[2]; break; } case GDTF_YEAR_MONTH_DAY: { _Year = i[0]; _Month = i[1]; _Day = i[2]; break; } default: { _Year = i[2]; if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR) { // Assume m/d/yyyy _Day = i[1]; _Month = i[0]; } else { // Who knows??? // Assume d/m/yyyy _Day = i[0]; _Month = i[1]; } break; } } if (_Year < 100) { LAssert(_Day < 1000 && _Month < 1000); if (_Year >= 80) _Year += 1900; else _Year += 2000; } Status = true; } else { // Fall back to fuzzy matching auto T = LString(Str).SplitDelimit(" ,"); MonthHash Lut; int FMonth = 0; int FDay = 0; int FYear = 0; for (unsigned i=0; i 0) { if (i >= 1000) { FYear = i; } else if (i < 32) { FDay = i; } } } else { int i = Lut.Find(p); if (i) FMonth = i; } } if (FMonth && FDay) { Day(FDay); Month(FMonth); } if (FYear) { Year(FYear); } else { LDateTime Now; Now.SetNow(); Year(Now.Year()); } } } return Status; } bool LDateTime::SetTime(const char *Str) { if (!Str) return false; auto T = LString(Str).SplitDelimit(":."); if (T.Length() < 2 || T.Length() > 4) return false; #define SetClamp(out, in, minVal, maxVal) \ out = (int)Atoi(in.Get(), 10, 0); \ if (out > maxVal) out = maxVal; \ else if (out < minVal) out = minVal SetClamp(_Hours, T[0], 0, 23); SetClamp(_Minutes, T[1], 0, 59); SetClamp(_Seconds, T[2], 0, 59); _Thousands = 0; const char *s = T.Last(); if (s) { if (strchr(s, 'p') || strchr(s, 'P')) { if (_Hours != 12) _Hours += 12; } else if (strchr(s, 'a') || strchr(s, 'A')) { if (_Hours == 12) _Hours -= 12; } } if (T.Length() > 3) { LString t = "0."; t += s; _Thousands = (int) (t.Float() * 1000); } return true; } int LDateTime::IsWeekDay(const char *s) { for (unsigned n=0; n= 4) { Year((int)t[0].Int()); Month((int)t[1].Int()); Day((int)t[2].Int()); } else if (t[2].Length() >= 4) { Day((int)t[0].Int()); Month((int)t[1].Int()); Year((int)t[2].Int()); } else { LAssert(!"Unknown date format?"); return false; } } } else if (a[i].Length() == 4) Year((int)a[i].Int()); else if (!Day()) Day((int)a[i].Int()); } else if (IsAlpha(*c)) { int WkDay = IsWeekDay(c); if (WkDay >= 0) continue; int Mnth = IsMonth(c); if (Mnth >= 0) Month(Mnth + 1); } else if (*c == '-' || *c == '+') { c++; if (strlen(c) == 4) { // Timezone.. int64 Tz = a[i].Int(); int Hrs = (int) (Tz / 100); int Min = (int) (Tz % 100); SetTimeZone(Hrs * 60 + Min, false); } } } return IsValid(); } int LDateTime::Sizeof() { return sizeof(int) * 7; } bool LDateTime::Serialize(LFile &f, bool Write) { int32 i; if (Write) { #define wf(fld) i = fld; f << i; wf(_Day); wf(_Month); wf(_Year); wf(_Thousands); wf(_Seconds); wf(_Minutes); wf(_Hours); } else { #define rf(fld) f >> i; fld = i; rf(_Day); rf(_Month); rf(_Year); rf(_Thousands); rf(_Seconds); rf(_Minutes); rf(_Hours); } return true; } /* bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write) { #ifndef LGI_STATIC if (Props && Name) { struct _Date { uint8_t Day; uint8_t Month; int16_t Year; uint8_t Hour; uint8_t Minute; uint16_t ThouSec; }; LAssert(sizeof(_Date) == 8); if (Write) { _Date d; d.Day = _Day; d.Month = _Month; d.Year = _Year; d.Hour = _Hours; d.Minute = _Minutes; d.ThouSec = (_Seconds * 1000) + _Thousands; return Props->Set(Name, &d, sizeof(d)); } else // Read { void *Ptr; int Len; if (Props->Get(Name, Ptr, Len) && sizeof(_Date) == Len) { _Date *d = (_Date*) Ptr; _Day = d->Day; _Month = d->Month; _Year = d->Year; _Hours = d->Hour; _Minutes = d->Minute; _Seconds = d->ThouSec / 1000; _Thousands = d->ThouSec % 1000; return true; } } } #endif return false; } */ int LDateTime::Compare(const LDateTime *Date) const { // this - *Date auto ThisTs = IsValid() ? Ts() : LTimeStamp(); auto DateTs = Date->IsValid() ? Date->Ts() : LTimeStamp(); if (ThisTs.Get() & 0x800000000000000) { Get(ThisTs); } // If these ever fire, the cast to int64_t will overflow LAssert((ThisTs.Get() & 0x800000000000000) == 0); LAssert((DateTs.Get() & 0x800000000000000) == 0); auto Diff = ThisTs - DateTs; if (Diff < 0) return -1; return Diff > 0 ? 1 : 0; } #define DATETIME_OP(op) \ bool LDateTime::operator op(const LDateTime &dt) const \ { \ auto a = Ts(); \ auto b = dt.Ts(); \ return a op b; \ } DATETIME_OP(<) DATETIME_OP(<=) DATETIME_OP(>) DATETIME_OP(>=) bool LDateTime::operator ==(const LDateTime &dt) const { return _Year == dt._Year && _Month == dt._Month && _Day == dt._Day && _Hours == dt._Hours && _Minutes == dt._Minutes && _Seconds == dt._Seconds && _Thousands == dt._Thousands; } bool LDateTime::operator !=(const LDateTime &dt) const { return _Year != dt._Year || _Month != dt._Month || _Day != dt._Day || _Hours != dt._Hours || _Minutes != dt._Minutes || _Seconds != dt._Seconds || _Thousands != dt._Thousands; } int LDateTime::DiffMonths(const LDateTime &dt) { int a = (Year() * 12) + Month(); int b = (dt.Year() * 12) + dt.Month(); return b - a; } LDateTime LDateTime::operator -(const LDateTime &dt) { LTimeStamp a, b; Get(a); dt.Get(b); /// Resolution of a second when using 64 bit timestamps int64 Sec = Second64Bit; int64 Min = 60 * Sec; int64 Hr = 60 * Min; int64 Day = 24 * Hr; int64 d = (int64)a.Get() - (int64)b.Get(); LDateTime r; r._Day = (int16) (d / Day); d -= r._Day * Day; r._Hours = (int16) (d / Hr); d -= r._Hours * Hr; r._Minutes = (int16) (d / Min); d -= r._Minutes * Min; r._Seconds = (int16) (d / Sec); #ifdef WIN32 d -= r._Seconds * Sec; r._Thousands = (int16) (d / 10000); #else r._Thousands = 0; #endif return r; } LDateTime LDateTime::operator +(const LDateTime &dt) { LDateTime s = *this; s.AddMonths(dt.Month()); s.AddDays(dt.Day()); s.AddHours(dt.Hours()); s.AddMinutes(dt.Minutes()); // s.AddSeconds(dt.Seconds()); return s; } LDateTime &LDateTime::operator =(const LDateTime &t) { _Day = t._Day; _Year = t._Year; _Thousands = t._Thousands; _Month = t._Month; _Seconds = t._Seconds; _Minutes = t._Minutes; _Hours = t._Hours; _Tz = t._Tz; _Format = t._Format; return *this; } LDateTime &LDateTime::operator =(struct tm *time) { if (time) { _Seconds = time->tm_sec; _Minutes = time->tm_min; _Hours = time->tm_hour; _Day = time->tm_mday; _Month = time->tm_mon + 1; _Year = time->tm_year + 1900; } else Empty(); return *this; } bool LDateTime::IsSameDay(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month() && Year() == d.Year(); } bool LDateTime::IsSameMonth(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month(); } bool LDateTime::IsSameYear(LDateTime &d) const { return Year() == d.Year(); } LDateTime LDateTime::StartOfDay() const { LDateTime dt = *this; dt.Hours(0); dt.Minutes(0); dt.Seconds(0); dt.Thousands(0); return dt; } LDateTime LDateTime::EndOfDay() const { LDateTime dt = *this; dt.Hours(23); dt.Minutes(59); dt.Seconds(59); dt.Thousands(999); return dt; } bool LDateTime::IsLeapYear(int Year) const { if (Year < 0) Year = _Year; if (Year % 4 != 0) return false; if (Year % 400 == 0) return true; if (Year % 100 == 0) return false; return true; } int LDateTime::DaysInMonth() const { if (_Month == 2 && IsLeapYear()) { return 29; } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0; } void LDateTime::AddSeconds(int64 Seconds) { LTimeStamp i; if (Get(i)) { i.Ref() += Seconds * Second64Bit; Set(i); } } void LDateTime::AddMinutes(int64 Minutes) { LTimeStamp i; if (Get(i)) { int64 delta = Minutes * 60 * Second64Bit; i.Ref() += delta; Set(i); } } void LDateTime::AddHours(int64 Hours) { LTimeStamp i; if (Get(i)) { i.Ref() += Hours * HourLength * Second64Bit; Set(i); } } bool LDateTime::AddDays(int64 Days) { if (!Days) return true; LTimeStamp Ts; if (!Get(Ts)) return false; Ts.Ref() += Days * LDateTime::DayLength * Second64Bit; bool b = Set(Ts); return b; } void LDateTime::AddMonths(int64 Months) { int64 m = _Month + Months; do { if (m < 1) { _Year--; m += 12; } else if (m > 12) { _Year++; m -= 12; } else { break; } } while (1); _Month = (int16) m; if (_Day > DaysInMonth()) _Day = DaysInMonth(); } LString LDateTime::DescribePeriod(double seconds) { int mins = (int) (seconds / 60); seconds -= mins * 60; int hrs = mins / 60; mins -= hrs * 60; int days = hrs / 24; hrs -= days * 24; int years = days / 365; // leap year, sheap year lol days -= years * 365; LString s; if (years > 0) s.Printf("%iyrs %id %ih", years, days, hrs); else if (days > 0) s.Printf("%id %ih %im", days, hrs, mins); else if (hrs > 0) s.Printf("%ih %im %is", hrs, mins, (int)seconds); else if (mins > 0) s.Printf("%im %is", mins, (int)seconds); else s.Printf("%is", (int)seconds); return s; } LString LDateTime::DescribePeriod(LDateTime to) { auto ThisTs = Ts(); auto ToTs = to.Ts(); auto diff = ThisTs < ToTs ? ToTs - ThisTs : ThisTs - ToTs; auto seconds = (double)diff / LDateTime::Second64Bit; return DescribePeriod(seconds); } int LDateTime::MonthFromName(const char *Name) { if (Name) { for (int m=0; m<12; m++) { if (strnicmp(Name, MonthsShort[m], strlen(MonthsShort[m])) == 0) { return m + 1; break; } } } return -1; } bool LDateTime::Decode(const char *In) { // Test data: // // Tue, 6 Dec 2005 1:25:32 -0800 Empty(); if (!In) { LAssert(0); return false; } bool Status = false; // Tokenize delimited by whitespace LString::Array T = LString(In).SplitDelimit(", \t\r\n"); if (T.Length() < 2) { if (T[0].IsNumeric()) { // Some sort of timestamp? uint64_t Ts = Atoi(T[0].Get()); if (Ts > 0) { return SetUnix(Ts); } else return false; } else { // What now? return false; } } else { bool GotDate = false; for (unsigned i=0; i 31) { // Y/M/D? Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else if (Date[2].Int() > 31) { // D/M/Y? Day((int)Date[0].Int()); Year((int)Date[2].Int()); } else { // Ambiguous year... bool YrFirst = true; if (Date[0].Length() == 1) YrFirst = false; // else we really can't tell.. just go with year first if (YrFirst) { Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else { Day((int)Date[0].Int()); Year((int)Date[2].Int()); } LDateTime Now; Now.SetNow(); if (Year() + 2000 <= Now.Year()) Year(2000 + Year()); else Year(1900 + Year()); } if (Date[1].IsNumeric()) Month((int)Date[1].Int()); else { int m = MonthFromName(Date[1]); if (m > 0) Month(m); } GotDate = true; Status = true; } else if (s.Find(":") >= 0) { // whole time // Do some validation bool Valid = true; for (char *c = s; *c && Valid; c++) { if (!(IsDigit(*c) || *c == ':')) Valid = false; } if (Valid) { LString::Array Time = s.Split(":"); if (Time.Length() == 2 || Time.Length() == 3) { // Hour int i = (int) Time[0].Int(); if (i >= 0) Hours(i); if (s.Lower().Find("p") >= 0) { if (Hours() < 12) Hours(Hours() + 12); } // Minute i = (int) Time[1].Int(); if (i >= 0) Minutes(i); if (Time.Length() == 3) { // Second i = (int) Time[2].Int(); if (i >= 0) Seconds(i); } Status = true; } } } else if (IsAlpha(s(0))) { // text int m = MonthFromName(s); if (m > 0) Month(m); } else if (strchr("+-", *s)) { // timezone DoTimeZone: LDateTime Now; double OurTmz = (double)Now.SystemTimeZone() / 60; if (s && strchr("-+", *s) && strlen(s) == 5) { #if 1 int i = atoi(s); int hr = i / 100; int min = i % 100; SetTimeZone(hr * 60 + min, false); #else // adjust for timezone char Buf[32]; memcpy(Buf, s, 3); Buf[3] = 0; double TheirTmz = atof(Buf); memcpy(Buf+1, s + 3, 2); TheirTmz += (atof(Buf) / 60); if (Tz) { *Tz = TheirTmz; } double AdjustHours = OurTmz - TheirTmz; AddMinutes((int) (AdjustHours * 60)); #endif } else { // assume GMT AddMinutes((int) (OurTmz * 60)); } } else if (s.IsNumeric()) { int Count = 0; for (char *c = s; *c; c++) { if (!IsDigit(*c)) break; Count++; } if (Count <= 2) { if (Day()) { // We already have a day... so this might be // a 2 digit year... LDateTime Now; Now.SetNow(); int Yr = atoi(s); if (2000 + Yr <= Now.Year()) Year(2000 + Yr); else Year(1900 + Yr); } else { // A day number (hopefully)? Day((int)s.Int()); } } else if (Count == 4) { if (!Year()) { // A year! Year((int)s.Int()); Status = true; } else { goto DoTimeZone; } // My one and only Y2K fix // d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr); } } } } return Status; } bool LDateTime::GetVariant(const char *Name, LVariant &Dst, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: // Type: Int32 Dst = Year(); break; case DateMonth: // Type: Int32 Dst = Month(); break; case DateDay: // Type: Int32 Dst = Day(); break; case DateHour: // Type: Int32 Dst = Hours(); break; case DateMinute: // Type: Int32 Dst = Minutes(); break; case DateSecond: // Type: Int32 Dst = Seconds(); break; case DateDate: // Type: String { char s[32]; GetDate(s, sizeof(s)); Dst = s; break; } case DateTime: // Type: String { char s[32]; GetTime(s, sizeof(s)); Dst = s; break; } case TypeString: // Type: String case DateDateAndTime: // Type: String { char s[32]; Get(s, sizeof(s)); Dst = s; break; } case TypeInt: // Type: Int64 case DateTimestamp: // Type: Int64 { LTimeStamp i; if (Get(i)) Dst = (int64)i.Get(); break; } case DateSecond64Bit: { Dst = Second64Bit; break; } default: { return false; } } return true; } bool LDateTime::SetVariant(const char *Name, LVariant &Value, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: Year(Value.CastInt32()); break; case DateMonth: Month(Value.CastInt32()); break; case DateDay: Day(Value.CastInt32()); break; case DateHour: Hours(Value.CastInt32()); break; case DateMinute: Minutes(Value.CastInt32()); break; case DateSecond: Seconds(Value.CastInt32()); break; case DateDate: SetDate(Value.Str()); break; case DateTime: SetTime(Value.Str()); break; case DateDateAndTime: Set(Value.Str()); break; case DateTimestamp: Set((uint64)Value.CastInt64()); break; default: return false; } return true; } bool LDateTime::CallMethod(const char *Name, LVariant *ReturnValue, LArray &Args) { switch (LStringToDomProp(Name)) { case DateSetNow: SetNow(); if (ReturnValue) *ReturnValue = true; break; case DateSetStr: if (Args.Length() < 1) return false; bool Status; if (Args[0]->Type == GV_INT64) Status = Set((uint64) Args[0]->Value.Int64); else Status = Set(Args[0]->Str()); if (ReturnValue) *ReturnValue = Status; break; case DateGetStr: { char s[256] = ""; Get(s, sizeof(s)); if (ReturnValue) *ReturnValue = s; break; } default: return false; } return true; } #ifdef _DEBUG #define DATE_ASSERT(i) \ if (!(i)) \ { \ LAssert(!"LDateTime unit test failed."); \ return false; \ } static bool CompareDateStr(LString a, LString b) { auto delim = "/: ap"; auto aParts = a.SplitDelimit(delim); auto bParts = a.SplitDelimit(delim); if (aParts.Length() != 6 || bParts.Length() != 6) return false; for (int i=0; i= 8); // Check 64bit get/set LDateTime t("1/1/2017 0:0:0"), t2; LTimeStamp i; DATE_ASSERT(t.Get(i)); // LgiTrace("Get='%s'\n", t.Get().Get()); auto i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); t2.SetFormat(GDTF_DAY_MONTH_YEAR); t2.Set(i2); LString s = t2.Get(); // LgiTrace("Set='%s'\n", s.Get()); DATE_ASSERT(CompareDateStr(s, "2/1/2017 12:00:00a")); t.SetNow(); // LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); t2 = t; t2.ToUtc(); // LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); t2.ToLocal(); // LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); DATE_ASSERT(t == t2); // Check get/set Unix time t.ToUtc(); t.SetUnix(tt = 1704067200); s = t.Get(); DATE_ASSERT(CompareDateStr(s, Jan1)); auto new_tt = t.GetUnix(); DATE_ASSERT(new_tt == tt); // Now do it with a timezone set... t.SetTimeZone(t.SystemTimeZone(), false); t.SetUnix(tt); DATE_ASSERT(CompareDateStr(s, Jan1)); new_tt = t.GetUnix(); DATE_ASSERT(new_tt == tt); // Check various Add### functions // Check infer timezone t.Empty(); t.Set(Jan1); DATE_ASSERT(t.InferTimeZone(false)); DATE_ASSERT(t.GetTimeZone() == 660); t.SetTimeZone(660, false); // Now try something right on the edge of the DST change... just before... t.SetUnix(1712412000); // 7/4/2024 1:00:00 +1100 t.SetTimeZone(0, true); DATE_ASSERT(t.InferTimeZone(false)); DATE_ASSERT(t.GetTimeZone() == 660); // Just after... t.SetUnix(1712426400); // 7/4/2024 4:00:00 +1000 t.SetTimeZone(0, true); DATE_ASSERT(t.InferTimeZone(false)); DATE_ASSERT(t.GetTimeZone() == 600); return true; } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// LTimeStamp <imeStamp::operator =(const time_t unixTime) { #if defined(WINDOWS) ts = (unixTime + SEC_TO_UNIX_EPOCH) * WINDOWS_TICK; #else ts = (unixTime + LDateTime::Offset1800) * LDateTime::Second64Bit; #endif return *this; } LTimeStamp <imeStamp::operator =(const LDateTime &dt) { dt.Get(*this); return *this; } time_t LTimeStamp::Unix() const { #if defined(WINDOWS) return (ts / WINDOWS_TICK) - SEC_TO_UNIX_EPOCH; #else return (ts / LDateTime::Second64Bit) - LDateTime::Offset1800; #endif } diff --git a/test/UnitTests/src/ContainerTests.cpp b/test/UnitTests/src/ContainerTests.cpp --- a/test/UnitTests/src/ContainerTests.cpp +++ b/test/UnitTests/src/ContainerTests.cpp @@ -1,265 +1,264 @@ #include "lgi/common/Lgi.h" #include "UnitTests.h" #include "lgi/common/UnrolledList.h" #include "lgi/common/HashTable.h" class PrivLContainers { public: bool ListDeleteOne(int pos, int sz) { LArray a; List l; for (int i=0; i a; List l; for (int i=0; i a; a.Add(1); a.Add(4); a.Add(5); a.Add(8); int n = 0; for (auto &i : a) { if (i != ref[n++]) return FAIL(_FL, "iterator error"); } } { List a; a.Add(new int(1)); a.Add(new int(4)); a.Add(new int(5)); a.Add(new int(8)); int n = 0; for (auto i : a) { if (*i != ref[n++]) return FAIL(_FL, "iterator error"); } if (n != 4) return FAIL(_FL, "count error"); n = 0; auto i3 = a.begin(); for (int *v = *i3; i3; v = *++i3) { if (*a[n] != **i3) return FAIL(_FL, "iterator error"); n++; } if (n != 4) return FAIL(_FL, "count error"); if (!d->ListInsert(45, 80)) return FAIL(_FL, "list insert"); if (!d->ListInsert(63, 80)) return FAIL(_FL, "list insert"); if (!d->ListInsert(64, 80)) return FAIL(_FL, "list insert"); if (!d->ListDeleteOne(45, 80)) return FAIL(_FL, "list delete one"); if (!d->ListDeleteOne(63, 80)) return FAIL(_FL, "list delete one"); if (!d->ListDeleteOne(64, 80)) return FAIL(_FL, "list delete one"); } #if 0 { GHashTbl h; for (int i=1; i<=100; i++) { h.Add(i, i << 1); } int count = 0; for (auto i : h) { if (i.key << 1 != i.value) return FAIL(_FL, "hash iterate"); count++; } if (count != h.Length()) return FAIL(_FL, "hash iterate"); count = 0; for (auto i : h) { if (i.key == 50) h.Delete(i.key); else count++; } if (count != h.Length()) return FAIL(_FL, "hash delete during iterate"); } #endif { LUnrolledList IntLst; IntLst.Add(123); LUnrolledList StrLst; StrLst.Add("Hello"); for (auto i : StrLst) { if (_stricmp(i, "Hello") != 0) return FAIL(_FL, "str lst iteration"); } StrLst.Add("Aacc"); StrLst.Add("Tyertw"); StrLst.Add("Bbbsd"); StrLst.Sort ( - [](LString &a, LString &b, int Dir) + [](auto a, auto b) { - return _stricmp(a, b) * Dir; - }, - 1 + return _stricmp(a.Get(), b.Get()); + } ); StrLst.Delete("Tyertw"); // Compact test LUnrolledList Comp; for (int i=0; i<20; i++) Comp.Add(i+1); for (int i=0; i<8; i++) Comp.DeleteAt(8); Comp.Compact(); } { LHashTbl,int> IntMap; IntMap.Add(45, 67); for (auto i : IntMap) { printf("Int: %i -> %i\n", i.key, i.value); } IntMap.Delete(45); LHashTbl,int> CaseSenMap; CaseSenMap.Add("Abc", 34); CaseSenMap.Add("abc", 23); if (CaseSenMap.Length() != 2) return FAIL(_FL, "Wrong len."); for (auto i : CaseSenMap) { printf("Case: %s -> %i\n", i.key, i.value); } LHashTbl,int> CaseInsenMap; CaseInsenMap.Add("Abc", 34); CaseInsenMap.Add("abc", 23); if (CaseInsenMap.Length() != 1) return FAIL(_FL, "Wrong len."); for (auto i : CaseInsenMap) { printf("Insensitive: %s -> %i\n", i.key, i.value); } LHashTbl,int> ConstWideMap; ConstWideMap.Add(L"Asd", 456); for (auto i : ConstWideMap) { printf("ConstWide: %S -> %i\n", i.key, i.value); } LHashTbl,int> PoolMap; PoolMap.Add("Asd", 123); PoolMap.Add("Def", 124); for (auto i : PoolMap) { printf("PoolMap: %s -> %i\n", i.key, i.value); } // Check iterator invalidation LHashTbl,int> ItMap(4); ItMap.Add(23, 34); ItMap.Add(24, 56); int n = 0; for (auto it = ItMap.begin(); it != ItMap.end(); it++) { ItMap.Add(35, 567); n++; } if (n != 1) return FAIL(_FL, "Iterator didn't get invalidated"); // Check swapping works LHashTbl,int> SwapTest; SwapTest.Swap(ItMap); if (SwapTest.GetSize() != 8) return FAIL(_FL, "Swap failed."); } return true; }