From c45135d5b2d33e52e639bebd2589ce189452ffd9 Mon Sep 17 00:00:00 2001 From: Shawn Schantz Date: Fri, 9 May 2025 18:29:54 +0700 Subject: [PATCH 1/2] fix(mac): handle [PackageInfo] section in kmp.inf file also prevent crash when no package name found Fixes: #13718 --- .../KMConfiguration/KMConfigurationWindowController.m | 11 +++++++++-- mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m | 8 ++++++++ .../Keyman4MacIM/en.lproj/Localizable.strings | 3 +++ mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMXFile.m | 3 ++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m b/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m index 2b06db4cad..f07d06a557 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m @@ -130,8 +130,15 @@ NSArray *pArray = (NSArray *)obj; NSString *packageFolder = [self packageFolderFromPath:[pArray objectAtIndex:0]]; NSString *packageName = [self.AppDelegate packageNameFromPackageInfo:packageFolder]; - os_log_debug([KMLogs uiLog], "tableContents, packageFolder: %{public}@, packageName: %{public}@", packageFolder, packageName); - [_tableContents addObject:[NSDictionary dictionaryWithObjectsAndKeys:packageName, @"HeaderTitle", nil]]; + + if (packageName) { + os_log_debug([KMLogs testLog], "tableContents, packageFolder: %{public}@, packageName: %{public}@", packageFolder, packageName); + [_tableContents addObject:[NSDictionary dictionaryWithObjectsAndKeys:packageName, @"HeaderTitle", nil]]; + } else { + os_log_error([KMLogs testLog], "tableContents, no packageName for packageFolder: %{public}@", packageFolder); + NSString *noPackageName = NSLocalizedString(@"unknown-package-name", nil); + [_tableContents addObject:[NSDictionary dictionaryWithObjectsAndKeys:noPackageName, @"HeaderTitle", nil]]; + } for (NSString *path in pArray) { os_log_debug([KMLogs uiLog], "tableContents, path = '%{public}@'", path); NSDictionary *info = [KMXFile keyboardInfoFromKmxFile:path]; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m index 12c0b3c564..dfb4496a37 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m @@ -17,11 +17,14 @@ static NSString *const kPackageJsonFile = @"kmp.json"; static NSString *const kPackageInfFile = @"kmp.inf"; +// kmp.info section names static NSString *const kPackage = @"[Package]"; static NSString *const kButtons = @"[Buttons]"; static NSString *const kStartMenu = @"[StartMenu]"; static NSString *const kStartMenuEntries = @"[StartMenuEntries]"; static NSString *const kInfo = @"[Info]"; +// for older kmp.inf files, [PackageInfo] may be used instead of [Info] +static NSString *const kPackageInfo = @"[PackageInfo]"; static NSString *const kFiles = @"[Files]"; static NSString *const kAuthor = @"Author"; @@ -215,6 +218,11 @@ typedef enum { contentType = ctInfo; continue; } + // for older kmp.inf files, [PackageInfo] may be used instead of [Info] + else if ([[line lowercaseString] hasPrefix:[kPackageInfo lowercaseString]]) { + contentType = ctInfo; + continue; + } else if ([[line lowercaseString] hasPrefix:[kFiles lowercaseString]]) { contentType = ctFiles; continue; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings index 112f7e2b8f..367c301cea 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings +++ b/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings @@ -32,6 +32,9 @@ /* Message displayed in Configuration window when keyboard metadata cannot be loaded */ "message-error-unknown-metadata" = "unknown"; +/* Configuration window section heading when keyboard has no package name */ +"unknown-package-name" = "Unnamed Keyboard Package"; + /* Button text to acknowledge that .kmp file could not be read */ "button-keyboard-file-unreadable" = "OK"; diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMXFile.m b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMXFile.m index 2ce2fbfa89..0d60dcef04 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMXFile.m +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMXFile.m @@ -161,7 +161,8 @@ NSString *const kKMVisualKeyboardKey = @"KMVisualKeyboardKey"; + (NSDictionary *)keyboardInfoFromKmxFile:(NSString *)path { NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path]; - + os_log_debug([KMELogs configLog], "keyboardInfoFromKmxFile, path: %{public}@", path); + if (file == nil) { os_log_error([KMELogs configLog], "Failed to open file"); return nil; From 299f37e48b88c378b71ce6d6e425f5ac2f260748 Mon Sep 17 00:00:00 2001 From: Shawn Schantz Date: Mon, 12 May 2025 16:21:30 +0700 Subject: [PATCH 2/2] fix(mac): clarify file format differences for kmp.inf 5.0 and 6.0 --- mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m index dfb4496a37..1c6c3acfff 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m @@ -22,9 +22,10 @@ static NSString *const kPackage = @"[Package]"; static NSString *const kButtons = @"[Buttons]"; static NSString *const kStartMenu = @"[StartMenu]"; static NSString *const kStartMenuEntries = @"[StartMenuEntries]"; -static NSString *const kInfo = @"[Info]"; -// for older kmp.inf files, [PackageInfo] may be used instead of [Info] +// kmp.inf files version 5.0 includes a [PackageInfo] section static NSString *const kPackageInfo = @"[PackageInfo]"; +// kmp.inf files version 6.0 and later replace [PackageInfo] with [Info] +static NSString *const kInfo = @"[Info]"; static NSString *const kFiles = @"[Files]"; static NSString *const kAuthor = @"Author"; @@ -214,11 +215,12 @@ typedef enum { contentType = ctStartMenuEntries; continue; } + // as of version 6.0, kmp.inf files contain a section [Info] else if ([[line lowercaseString] hasPrefix:[kInfo lowercaseString]]) { contentType = ctInfo; continue; } - // for older kmp.inf files, [PackageInfo] may be used instead of [Info] + // version 5.0 kmp.inf files contain a [PackageInfo] section instead else if ([[line lowercaseString] hasPrefix:[kPackageInfo lowercaseString]]) { contentType = ctInfo; continue;