diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..098cb70e262e9bf8edba58693a289e58c771063c --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,16 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + // TODO: we need to move to the following, but this gets us started with infrastructure without breaking our build + // extends: [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended"], + extends: ["plugin:@typescript-eslint/eslint-recommended"], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, +}; diff --git a/.github/multi-labeler.yml b/.github/multi-labeler.yml index d2dc8bffbcc31cfafce6bba781729b59ea2997ca..93ce22629a6656bdf28980500d8ed4cc92329b80 100644 --- a/.github/multi-labeler.yml +++ b/.github/multi-labeler.yml @@ -98,4 +98,8 @@ labels: - label: 'epic-gestures' matcher: - branch: '.*epic-gestures.*' # anywhere in the branch name, e.g. feat/epic-gestures/web/... or feat/web/foo-epic-ldml + branch: '.*epic-gestures.*' # anywhere in the branch name, e.g. feat/epic-gestures/web/... or feat/web/foo-epic-gestures + + - label: 'epic-kmcompx' + matcher: + branch: '.*epic-kmcompx.*' diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml index 80161dfeda0b94cf6f2874b752f855544af792e1..8c745bef3071cbfadd576e89610860a4fb7c0712 100644 --- a/.github/workflows/deb-packaging.yml +++ b/.github/workflows/deb-packaging.yml @@ -102,7 +102,7 @@ jobs: strategy: fail-fast: true matrix: - dist: [focal, jammy, kinetic] + dist: [focal, jammy, kinetic, lunar] arch: [amd64] runs-on: ubuntu-latest @@ -159,7 +159,7 @@ jobs: upload-to-llso: name: Upload packages to llso - needs: deb_signing + needs: [sourcepackage, deb_signing] runs-on: self-hosted environment: "deploy (linux)" if: github.event.client_payload.isTestBuild == 'false' diff --git a/.gitignore b/.gitignore index cb0de77c31d50942fff291e345656f81ca8ff571..0abec10c3b1651343c64920b615f5a5c236de9a6 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /common/windows/delphi/**/*.bpl /common/windows/delphi/**/*.map /common/windows/delphi/**/*.tds +/common/windows/delphi/**/*.identcache # Temp files generated during release build /windows/src/buildtools/inst/copydebug.mak @@ -190,3 +191,10 @@ lcov.info /keyman*.buildinfo /keyman*.changes /keyman*.tar.?z + +#Sabine: +# /common/test/keyboards/invalid/source/*.kmx +# /developer/src/test/auto/kmcomp/*.kmn +# /developer/src/test/auto/kmcomp/*.kvk +# /developer/src/test/auto/kmcomp/*.kvk* +# /developer/src/test/auto/kmcomp/*.txt diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000000000000000000000000000000000000..1c46a510eb4508ab1bbc640d7fab211fbe5931f1 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,5 @@ +external-sources=true + +# If you get SC1091 for sourced files, add the following setting to +# your .vscode/settings.json file: +# "shellcheck.useWorkspaceRootAsCwd": false diff --git a/HISTORY.md b/HISTORY.md index 45e682389773abc29881af870168def4939d2216..c05c241eff96740ca9e41e287d1a851045aaf025 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,259 @@ # Keyman Version History +## 17.0.113 alpha 2023-05-26 + +* docs(android): Update documentation for building Android on Linux (#8860) +* fix(android): Fix typo in build-utils.sh (#8859) +* refactor(developer): compiler interface part 1 for wasm (#8850) +* chore(developer): remove SetError macro (#8856) + +## 17.0.112 alpha 2023-05-25 + +* refactor(developer): move file write out of WriteCompiledKeyboard and CompileKeyboard merge (#8796) + +## 17.0.111 alpha 2023-05-24 + +* fix(android/app): Pathing for publish script (#8845) +* chore(developer): add test for missing .js file to kmc-package (#8846) +* feat(linux): Add dbus system service (#8509) +* chore(linux): Add missing dependency to GHA (#8849) + +## 17.0.110 alpha 2023-05-22 + +* fix(common): ensure child builds don't rebuild dependencies unnecessarily (#8834) + +## 17.0.109 alpha 2023-05-17 + +* fix(windows): add wrap-symbols to Text Editor Makefile (#8819) +* chore(linux): Make postinst script comply with Debian policy (#8810) +* chore(windows): remove legacy core and flag ️ (#8593) + +## 17.0.108 alpha 2023-05-16 + +* feat(windows): add text editor to the support makefile (#8750) +* feat(developer): verify keyboard versions in kmc-package (#8769) +* feat(developer): verify bcp47 tags are valid and minimal in kmc-package (#8778) +* feat(developer): verify at least one language in package (#8783) +* chore(developer): verify file types of content files in package (#8792) +* chore(developer): verify that package has at least a model or keyboard (#8793) +* chore(ios): Changes required for XCode 14.3 (#8746) + +## 17.0.107 alpha 2023-05-15 + +* chore(linux): Fix installation build step on TC (#8784) +* refactor(android/engine): Consolidate updateSelection (#8739) + +## 17.0.106 alpha 2023-05-12 + +* chore(developer): move package formats to common/web/types (#8729) +* feat(developer): add package validation (#8740) +* feat(developer): add validation of package filenames (#8751) +* feat(developer): validate content file names in packages (#8755) +* feat(developer): validate package name in compiler (#8757) +* chore(developer): rename Compiler and related classes (#8726) +* feat(developer): uset api from wasm! (#8716) + +## 17.0.105 alpha 2023-05-11 + +* chore(common): Update crowdin strings for Amharic (#8748) +* chore(android/app): Cleanup build-play-store-notes script (#8749) +* chore(linux): Add build.sh for keyman-config (#8733) + +## 17.0.104 alpha 2023-05-10 + +* chore(developer): rename kmc-keyboard to kmc-ldml (#8725) +* chore(linux): bring kmp.json for unit test up to spec (#8732) +* feat(common): add `builder_run_action` shorthand function (#8742) + +## 17.0.103 alpha 2023-05-09 + +* fix(mac): `$CONFIG` not `$CONFIGURATION` in build path (#8724) +* chore(linux): Fix another problem uploading to launchpad (#8727) + +## 17.0.102 alpha 2023-05-08 + +* chore(linux): Build for Lunar on all platforms (#8713) +* fix(linux): Fix linux tests (#8510) +* chore(linux): Fix uploading to launchpad (#8720) + +## 17.0.101 alpha 2023-05-05 + +* chore(common): `set -eu` in build-utils.sh (#8698) +* fix(android/app): Resize OSK when resuming app from background (#8704) + +## 17.0.100 alpha 2023-05-04 + +* (#8690) +* (#8691) + +## 17.0.99 alpha 2023-05-03 + +* chore(linux): Fix build script (#8705) + +## 17.0.98 alpha 2023-05-02 + +* chore(common): run common and resources tests (#8692) + +## 17.0.97 alpha 2023-04-28 + +* docs(windows): Add verify vs build tools (#8676) +* (#8686) + +## 17.0.96 alpha 2023-04-27 + +* chore(android): Update QRGen version for Java 11 (#8673) +* refactor(android/engine): Move toggleSuggestionBanner to KMKeyboard (#8679) +* refactor(android/engine): Use isKeyboardLoaded() on a consistent basis (#8680) +* chore(developer): test message correctness in compiler modules (#8664) +* chore(developer): add eslint devDependency for kmc (#8665) +* chore(developer): kmc-keyboard should not use any node-specific modules (#8666) +* fix(common): builder parse multi-param order (#8668) +* refactor(developer): use callbacks to avoid direct fs access in kmc-package (#8674) +* refactor(android/engine): Move shouldIgnore booleans to KMKeyboard (#8682) +* chore(linux): Build for Ubuntu 23.04 Lunar as well (#8683) + +## 17.0.95 alpha 2023-04-26 + +* refactor(android/engine): Move currentBanner to KMKeyboard (#8671) +* chore(android/engine): Remove jvm args (#8675) +* fix(developer): kmc: fix jis/abnt2 scan codes (#8670) + +## 17.0.94 alpha 2023-04-25 + +* fix(android/engine): Update InApp input connection (#8647) +* chore(common): Move the_99 test fixture keyboard (#8660) +* chore(android): Move namespace to Gradle files (#8667) + +## 17.0.93 alpha 2023-04-24 + +* feat(common): kmx file reader (#8641) +* feat(developer): support FollowKeyboardVersion in kmc-package (#8642) +* chore(common): Update crowdin strings for Simplified Chinese (#8653) +* fix(common): builder - pass deps flags to child scripts (#8654) +* feat(developer): use CompilerEvent for messages in all of kmc (#8651) +* chore(developer): fixup deps for kmc modules (#8652) +* feat(core): ldml repertoire test, initial ICU integration (#8441) + +## 17.0.92 alpha 2023-04-21 + +* fix(developer): kmc-package path reference (#8634) +* chore(developer): only run keyboards-repo tests for a full-test (#8635) +* refactor(developer): move comperr.h to kmn_compiler_errors.h (#8636) +* refactor(developer): kmc-model to use `CompilerEvent` (#8637) + +## 17.0.91 alpha 2023-04-20 + +* fix(developer): kmc-kmn would abort build on warnings (#8617) +* fix(developer): kmc no longer emits double file extension for models (#8618) +* refactor(developer): move build activities (#8622) +* feat(developer): add warning options to kmc (#8623) +* feat(developer): use new message infrastructure in kmc-kmn (#8625) +* refactor(developer): move shared test helpers into common (#8626) +* feat(developer): add CompilerMessages support to kmc-package (#8627) +* feat(developer): warn if .kps file includes a non-binary .kvk file (#8628) +* chore(web): sets kmc-model as dependency of input-processor test action (#8629) +* fix(android/engine): Fix resetContext calls when selection changes (#8611) + +## 17.0.90 alpha 2023-04-19 + +* chore(android/engine): Cleanup old SDK calls (#8612) +* fix(developer): package editor no longer loses RTL flag for LMs (#8606) + +## 17.0.89 alpha 2023-04-18 + +* chore(developer): tweak build of extension list in kmc (#8597) +* docs(ios): Add help page for Settings (#8546) +* fix(developer): add missing dependencies (#8605) + +## 17.0.88 alpha 2023-04-17 + +* docs(android): Update Linux dev environment for Java 11 (#8596) + +## 17.0.87 alpha 2023-04-13 + +* refactor(developer): DRY kmc module references (#8577) +* chore(developer): kmc-kmn should build all baseline fixtures (#8578) +* fix(developer): kmc should not validate as well as compile keyboard (#8579) +* fix(developer): improve 'does not exist' error message (#8580) +* fix(common): consolidate npm publishing (#8587) +* chore(common): add missing deps and files for npm publish (#8588) + +## 17.0.86 alpha 2023-04-12 + +* docs(windows): link to wiki architecture faq page (#8584) + +## 17.0.85 alpha 2023-04-11 + +* chore(developer): AddWarning and SetError cleanup (#8571) +* fix(developer): add DRY_RUN variable to build.sh (#8576) +* fix(ios): fixes xcode-triggered debug, run builds (#8575) + +## 17.0.84 alpha 2023-04-10 + +* epic: kmcompx - kmcmpdll cross-platform compiler (#7330) +* chore(android): Update projects to use Java 11 (#8543) +* docs: clarify dependency meanings in build-utils (#8567) +* fix(mac): fix corrupt installer (#8565) + +## 17.0.83 alpha 2023-04-05 + +* fix(windows): use right instead of left alignment for popup buttons (#8561) + +## 17.0.82 alpha 2023-03-31 + +* feat(developer): support jis and abnt2 (#8513) +* revert: #8549 "chore(web): merge master into feature-esmodule-web-engine (A17S9 end) " (#8553) + +## 17.0.81 alpha 2023-03-30 + +* feat(windows): simple text editor using Edit Control (#8391) + +## 17.0.80 alpha 2023-03-29 + +* chore(linux): Remove dependency on `sudo` (#8535) +* chore(linux): Improve robustness of test cleanup (#8536) +* fix(linux): Remove warning after pressing Close button (#8538) + +## 17.0.79 alpha 2023-03-28 + +* chore(linux): Revert "Run and ignore autopkgtests on s390x" (#8506) +* docs(linux): Update minimum required Ubuntu version (#8526) +* docs(linux): Update common questions (#8532) + +## 17.0.78 alpha 2023-03-27 + +* chore(linux): Remove python3-raven dependency (#8507) +* refactor(android/engine): Make KMKeyboardJSHandler a normal class (#8494) + +## 17.0.77 alpha 2023-03-25 + +* chore(common): use mac /usr/bin/stat rather than homebrew version (#8498) + +## 17.0.76 alpha 2023-03-24 + +* chore(linux): Run and ignore autopkgtests on s390x (#8492) +* refactor(android/engine): Consolidate dispatchKey (#8483) + +## 17.0.75 alpha 2023-03-23 + +* chore(common): define BUILDER_CONFIGURATION env var (#8496) + +## 17.0.74 alpha 2023-03-22 + +* chore(windows): update sentry-native to 0.6.0 (#8464) +* chore(linux): Prevent building on s390x (#8477) +* fix(linux): Display error message for corrupt .kmp file (#8479) + +## 17.0.73 alpha 2023-03-21 + +* refactor(android/engine): Consolidate insertText (#8438) +* chore(common): prevent multiple npm ci runs in child scripts (#8484) + +## 17.0.72 alpha 2023-03-20 + +* chore(common): die early when --ci and --debug passed to test.sh (#8465) +* chore(common): Update crowdin strings for Kibaku (#8447) + ## 17.0.71 alpha 2023-03-18 * fix(core): ldml update fr-azerty for VKEY mapping (#8434) diff --git a/VERSION.md b/VERSION.md index 52335fbf1840ff4b3b3d2955be35fb9a12902913..a3a3dc809e3ff7ff42f204abcc79812de3a7b640 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -17.0.72 \ No newline at end of file +17.0.114 \ No newline at end of file diff --git a/android/KMAPro/build-play-store-notes.inc.sh b/android/KMAPro/build-play-store-notes.inc.sh new file mode 100755 index 0000000000000000000000000000000000000000..04fa8764179ff7e888b8065bfbbd22ae31ff28c0 --- /dev/null +++ b/android/KMAPro/build-play-store-notes.inc.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +# Assumption: parent script that sources this already has START STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +function generateReleaseNotes() { + pushd "$KEYMAN_ROOT/android/KMAPro/" + + # + # Copy release notes for Gradle Play Publisher to upload + # Reference: https://github.com/Triple-T/gradle-play-publisher#uploading-release-notes + # + local PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/$TIER.txt" + if [ $TIER = "stable" ]; then + PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/default.txt" + fi + builder_heading "Generating Play Store release notes to $PLAY_RELEASE_NOTES" + # write to file + echo "" > "$PLAY_RELEASE_NOTES" + + # Copy whatsnew.md to release notes 1 line at a time, + # filtering for lines that start with "*". + # Pad release notes if whatsnew.md doesn't have any line items + # Play Store release notes have a limit of 500 characters + local DEFAULT_RELEASE_NOTE="* Additional bug fixes and improvements" + local FILTERED_LINES=$( grep '^\s*\*.*$' "$KEYMAN_ROOT/android/help/about/whatsnew.md" || [[ $? == 1 ]] ) # Continue if grep has no matches + if [ -z "$FILTERED_LINES" ]; then + FILTERED_LINES="$DEFAULT_RELEASE_NOTE" + builder_warn "Warning: whatsnew.md empty so using default release note: '$FILTERED_LINES'" + fi + + # Change IFS to new line + local old_IFS="${IFS}" + IFS=$'\n' + for line in $FILTERED_LINES + do + local CHARS_IN_RELEASE_NOTES=$( wc -m < "$PLAY_RELEASE_NOTES" ) + local CHARS_IN_CURRENT_LINE=$( wc -m <<< $line ) + if (( CHARS_IN_RELEASE_NOTES + CHARS_IN_CURRENT_LINE + 1 < 450 )); then + # Copy line to Play Store release notes + echo "$line" >> "$PLAY_RELEASE_NOTES" + else + # 450 chars reached + builder_warn "Warning: Play Store release notes approaching 500 character limit" + echo "$DEFAULT_RELEASE_NOTE" >> "$PLAY_RELEASE_NOTES" + break + fi + done + + # Restore IFS + IFS="${old_IFS}" + popd +} diff --git a/android/KMAPro/build-play-store-notes.sh b/android/KMAPro/build-play-store-notes.sh deleted file mode 100755 index 3846ce9c42df2576a5184bed0809747723ddb902..0000000000000000000000000000000000000000 --- a/android/KMAPro/build-play-store-notes.sh +++ /dev/null @@ -1,55 +0,0 @@ -# Set sensible script defaults: -# set -e: Terminate script if a command returns an error -set -e -# set -u: Terminate script if an unset variable is used -set -u - -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" -## END STANDARD BUILD SCRIPT INCLUDE - -QUIET=0 - -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" - -THIS_DIR="$(dirname "$THIS_SCRIPT")" - -# -# Copy release notes for Gradle Play Publisher to upload -# Reference: https://github.com/Triple-T/gradle-play-publisher#uploading-release-notes -# -PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/$TIER.txt" -if [ $TIER = "stable" ]; then - PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/default.txt" -fi -echo "Generating Play Store release notes to $PLAY_RELEASE_NOTES" -echo "" > "$PLAY_RELEASE_NOTES" - -# Copy whatsnew.md to release notes 1 line at a time, -# filtering for lines that start with "*". -# Pad release notes if whatsnew.md doesn't have any line items -# Play Store release notes have a limit of 500 characters -DEFAULT_RELEASE_NOTE="* Additional bug fixes and improvements" -FILTERED_LINES=$( grep '^\s*\*.*$' "$KEYMAN_ROOT/android/help/about/whatsnew.md" || [[ $? == 1 ]] ) # Continue if grep has no matches -if [ -z "$FILTERED_LINES" ]; then - FILTERED_LINES="$DEFAULT_RELEASE_NOTE" - builder_warn "Warning: whatsnew.md empty so using default release note: '$FILTERED_LINES'" -fi - -IFS=$'\n' # Change IFS to new line -for line in $FILTERED_LINES -do - CHARS_IN_RELEASE_NOTES=$( wc -m < $PLAY_RELEASE_NOTES ) - CHARS_IN_CURRENT_LINE=$( wc -m <<< $line ) - if (( CHARS_IN_RELEASE_NOTES + CHARS_IN_CURRENT_LINE + 1 < 450 )); then - # Copy line to Play Store release notes - echo "$line" >> $PLAY_RELEASE_NOTES - else - # 450 chars reached - builder_warn "Warning: Play Store release notes approaching 500 character limit" - echo "$DEFAULT_RELEASE_NOTE" >> $PLAY_RELEASE_NOTES - break - fi -done diff --git a/android/KMAPro/build.gradle b/android/KMAPro/build.gradle index f77d9cc534b59115cddf2144cdd53847806f1a5d..7e49d3dc01819159a066fe7c5b4450a667867599 100644 --- a/android/KMAPro/build.gradle +++ b/android/KMAPro/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.4.0' // sentry-android-gradle-plugin 2.1.5+ requires AGP 7.0 classpath 'io.sentry:sentry-android-gradle-plugin:2.1.2' classpath 'name.remal:gradle-plugins:1.5.0' diff --git a/android/KMAPro/build.sh b/android/KMAPro/build.sh index 46a4676e2faaec20d20e8f8305a766f5256c3d81..192e3211187130db205b09b1ba36d0f63ebaeab5 100755 --- a/android/KMAPro/build.sh +++ b/android/KMAPro/build.sh @@ -80,9 +80,6 @@ fi if builder_start_action configure; then - # Copy Keyman Engine for Android - cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/${CONFIG}/keyman-engine.aar" "$KEYMAN_ROOT/android/KMAPro/kMAPro/libs/keyman-engine.aar" - KEYBOARD_PACKAGE_ID="sil_euro_latin" KEYBOARDS_TARGET="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp" MODEL_PACKAGE_ID="nrc.en.mtnt" @@ -96,6 +93,9 @@ fi if builder_start_action build; then + # Copy Keyman Engine for Android + cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/android/KMAPro/kMAPro/libs/keyman-engine.aar" + # Convert markdown to html for offline help build_help_html android KMAPro/kMAPro/src/main/assets/info diff --git a/android/KMAPro/gradle/wrapper/gradle-wrapper.properties b/android/KMAPro/gradle/wrapper/gradle-wrapper.properties index 41dfb87909a877d96c3af4adccce4c7a301b55a2..ae04661ee733431762e7ccf8ab9b7409ed44960c 100644 --- a/android/KMAPro/gradle/wrapper/gradle-wrapper.properties +++ b/android/KMAPro/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/KMAPro/kMAPro/build.gradle b/android/KMAPro/kMAPro/build.gradle index df38b2ade8ac64bf87a2ad22bb27ddc90ef6ee5d..e12b928a9c7f2a78a584fd6f1095e3bb32284765 100644 --- a/android/KMAPro/kMAPro/build.gradle +++ b/android/KMAPro/kMAPro/build.gradle @@ -2,7 +2,7 @@ plugins { id 'com.android.application' id 'io.sentry.android.gradle' // https://github.com/Triple-T/gradle-play-publisher/issues/947#issuecomment-843634852 - id 'com.github.triplet.play' version '3.7.0-agp4.2' apply false + id 'com.github.triplet.play' version '3.8.1' apply false id 'name.remal.default-plugins' } @@ -11,15 +11,12 @@ apply from: "$rootPath/version.gradle" android { compileSdkVersion 33 + namespace="com.tavultesoft.kmapro" // Don't compress kmp files so they can be copied via AssetManager aaptOptions { noCompress "kmp" } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } defaultConfig { applicationId "com.tavultesoft.kmapro" @@ -164,7 +161,7 @@ dependencies { // Add dependency for generating QR Codes // (Even though it's embedded in KMEA, because we're manually copying keyman-engine.aar, // we "lose" it in the dependency management) - implementation ('com.github.kenglxn.QRGen:android:2.7.0') { + implementation ('com.github.kenglxn.QRGen:android:3.0.1') { transitive = true } } diff --git a/android/KMAPro/kMAPro/src/main/AndroidManifest.xml b/android/KMAPro/kMAPro/src/main/AndroidManifest.xml index 6acb4d9da107592b9bf3c9d0cf0924c243cfd4e0..5392c842277eb0152a2c19cde66d4bbe0f7b30fa 100644 --- a/android/KMAPro/kMAPro/src/main/AndroidManifest.xml +++ b/android/KMAPro/kMAPro/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index e112a6360d42d353b866124cb82e0d3865752726..6b92d4c2eb5ab8641b70876097c37d2727aa6b03 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -109,6 +109,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene private final int minTextSize = 16; private final int maxTextSize = 72; private int textSize = minTextSize; + private int lastOrientation = Configuration.ORIENTATION_UNDEFINED; private static final String defaultKeyboardInstalled = "DefaultKeyboardInstalled"; private static final String defaultDictionaryInstalled = "DefaultDictionaryInstalled"; private static final String userTextKey = "UserText"; @@ -262,6 +263,14 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene super.onResume(); KMManager.onResume(); KMManager.hideSystemKeyboard(); + + // onConfigurationChanged() only triggers when device is rotated while app is in foreground + // This handles when device is rotated while app is in background + Configuration newConfig = this.getResources().getConfiguration(); + if (newConfig != null && newConfig.orientation != lastOrientation) { + lastOrientation = newConfig.orientation; + KMManager.onConfigurationChanged(newConfig); + } resizeTextView(textView.isKeyboardVisible()); KMManager.addKeyboardEventListener(this); @@ -349,6 +358,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene getSupportActionBar().setBackgroundDrawable(getActionBarDrawable(this)); resizeTextView(textView.isKeyboardVisible()); invalidateOptionsMenu(); + lastOrientation = newConfig.orientation; } @SuppressLint("RestrictedApi") diff --git a/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml index 09b4d4134007a7adecf36cef3aa7ce2eedc5c784..5071cc42698b6dbbf3b1b5d2653b5e7c6d4fcac8 100644 --- a/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml @@ -1,5 +1,6 @@ + አጋራ @@ -25,6 +26,7 @@ እዚህ ጋር መጻፍ ይጀምሩ … + የጽሑፍ መጠን፦ %1$d @@ -156,5 +158,5 @@ የፊደል ገበታው አዲሱን የኪይማን ስሪት ይፈልጋል - Unable to launch web browser + ብራውሰሩን ማስጀመር አልተቻለም diff --git a/android/KMAPro/kMAPro/src/main/res/values-zh-rCN/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-zh-rCN/strings.xml index 950f7530015c582654ff2e715c7ac70d6602fb72..ef07ed981347080a67d7ddef78f5dc1ce3ea6570 100644 --- a/android/KMAPro/kMAPro/src/main/res/values-zh-rCN/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values-zh-rCN/strings.xml @@ -1,5 +1,6 @@ + 分享 @@ -25,6 +26,7 @@ 开始在这里输入… + 文本大小: %1$d diff --git a/android/KMEA/app/build.gradle b/android/KMEA/app/build.gradle index 4b4da18754f130bca6dce5112e293baac545375a..423862b79b82be1fc6ba02bee160ff83317ca524 100644 --- a/android/KMEA/app/build.gradle +++ b/android/KMEA/app/build.gradle @@ -8,6 +8,7 @@ apply from: "$rootPath/version.gradle" android { compileSdkVersion 33 + namespace "com.keyman.engine" defaultConfig { minSdkVersion 21 @@ -16,6 +17,9 @@ android { // VERSION_CODE and VERSION_NAME from version.gradle but Gradle removes them for libraries buildConfigField "String", "KEYMAN_ENGINE_VERSION_NAME", "\""+VERSION_NAME+"\"" buildConfigField "String", "VERSION_ENVIRONMENT", "\""+VERSION_ENVIRONMENT+"\"" + + // Sepcify library filename keyman-engine-$CONFIG.aar + setProperty("archivesBaseName", "keyman-engine") } buildTypes { @@ -25,13 +29,6 @@ android { } } - // Sepcify library filename $CONFIG/keyman-engine.aar - libraryVariants.all { variant -> - variant.outputs.all { - outputFileName = "${variant.buildType.name}/keyman-engine.aar" - } - } - // TODO: Remove ResourceType when SDK > 17 lintOptions { disable 'ImpliedQuantity', 'MissingQuantity', 'MissingTranslation', 'ResourceType' @@ -52,16 +49,6 @@ android { } systemProperty 'kmeaTestMode', 'true' workingDir = "../" // Defaults to the `app` subdirectory, which is different from Android Studio's default. - - //Avoid Warning: an illegal reflective access operation has occurred - //Change argument jdk.module.illegalAccess=warn for detailed information which module needs to be opened - //Problem is caused by robolectric: see https://github.com/robolectric/robolectric/issues/4776 - if(JavaVersion.current().isJava9Compatible()){ - jvmArgs ('-Djdk.module.illegalAccess=permit') - jvmArgs('--add-opens', 'java.base/java.lang=ALL-UNNAMED') - jvmArgs('--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED') - } - } } } @@ -81,8 +68,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.8.1' // Generate QR Codes - // 3.0.1 requires Java 11 - implementation ('com.github.kenglxn.QRGen:android:2.7.0') { + implementation ('com.github.kenglxn.QRGen:android:3.0.1') { transitive = true } } diff --git a/android/KMEA/app/src/main/AndroidManifest.xml b/android/KMEA/app/src/main/AndroidManifest.xml index b880992618947d224aae4fee680d3e3eac32f2ce..c4522addc4ada721c3d589a1efa04e13d9014e92 100644 --- a/android/KMEA/app/src/main/AndroidManifest.xml +++ b/android/KMEA/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index 2c137f971f24f68164f39794c00bcca190cb42ab..88dc290322cb9ba588934c855f3ee79afa13e229 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -47,6 +47,9 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; import android.webkit.ConsoleMessage; import android.webkit.WebChromeClient; import android.webkit.WebSettings; @@ -67,15 +70,33 @@ import io.sentry.SentryLevel; final class KMKeyboard extends WebView { private static final String TAG = "KMKeyboard"; private final Context context; - private KeyboardType keyboardType = KeyboardType.KEYBOARD_TYPE_UNDEFINED; private String packageID; private String keyboardID; private String keyboardName; private String keyboardVersion; + private boolean shouldIgnoreTextChange = false; + private boolean shouldIgnoreSelectionChange = false; + + protected KeyboardType keyboardType = KeyboardType.KEYBOARD_TYPE_UNDEFINED; protected ArrayList javascriptAfterLoad = new ArrayList(); private static String currentKeyboard = null; + + /** + * Banner state value: "blank" - no banner available. + */ + protected static final String KM_BANNER_STATE_BLANK = "blank"; + /** + * Banner state value: "suggestion" - dictionary suggestions are shown. + */ + protected static final String KM_BANNER_STATE_SUGGESTION = "suggestion"; + + /** + * Current banner state. + */ + protected static String currentBanner = KM_BANNER_STATE_BLANK; + private static String txtFont = ""; private static String oskFont = null; private static String keyboardRoot = ""; @@ -121,7 +142,7 @@ final class KMKeyboard extends WebView { } public boolean getShouldShowHelpBubble() { - if(this._shouldShowHelpBubble == null) { + if (this._shouldShowHelpBubble == null) { SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); this._shouldShowHelpBubble = prefs.getBoolean(KMManager.KMKey_ShouldShowHelpBubble, true); } @@ -133,6 +154,55 @@ final class KMKeyboard extends WebView { this._shouldShowHelpBubble = flag; } + protected boolean shouldIgnoreTextChange() { + return shouldIgnoreTextChange; + } + + protected void setShouldIgnoreTextChange(boolean ignore) { + this.shouldIgnoreTextChange = ignore; + } + + protected boolean shouldIgnoreSelectionChange() { + return shouldIgnoreSelectionChange; + } + + protected void setShouldIgnoreSelectionChange(boolean ignore) { + this.shouldIgnoreSelectionChange = ignore; + } + + protected boolean updateText(String text) { + boolean result = false; + String kmText = ""; + if (text != null) { + kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n"); + } + + if (KMManager.isKeyboardLoaded(this.keyboardType) && !shouldIgnoreTextChange) { + this.loadJavascript(KMString.format("updateKMText('%s')", kmText)); + result = true; + } + + shouldIgnoreTextChange = false; + return result; + } + + protected boolean updateSelectionRange(int selStart, int selEnd) { + boolean result = false; + InputConnection ic = KMManager.getInputConnection(this.keyboardType); + if (ic != null) { + ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); + if (icText != null) { + updateText(icText.text.toString()); + } + } + this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); + result = true; + + return result; + } + + + @SuppressWarnings("deprecation") @SuppressLint("SetJavaScriptEnabled") public void initKMKeyboard(final Context context) { @@ -150,21 +220,10 @@ final class KMKeyboard extends WebView { getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); getSettings().setSupportZoom(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - getSettings().setUseWideViewPort(true); - getSettings().setLoadWithOverviewMode(true); - setWebContentsDebuggingEnabled(true); - } - if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) - setLayerType(View.LAYER_TYPE_SOFTWARE, null); // Disable hardware acceleration for API < 17, Keyman keyboard is slower without HWA but it causes some display issues. - // (Tested on Samsung Galaxy Nexus running Android 4.1.2) - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { - // These were deprecated in API 18 - getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); - getSettings().setPluginState(WebSettings.PluginState.ON_DEMAND); - } + getSettings().setUseWideViewPort(true); + getSettings().setLoadWithOverviewMode(true); + setWebContentsDebuggingEnabled(true); setWebChromeClient(new WebChromeClient() { public boolean onConsoleMessage(ConsoleMessage cm) { @@ -412,6 +471,24 @@ final class KMKeyboard extends WebView { return currentKeyboard; } + public static void setCurrentBanner(String banner) { + currentBanner = banner; + } + + public static String currentBanner() { return currentBanner; } + + protected void toggleSuggestionBanner(HashMap associatedLexicalModel, boolean keyboardChanged) { + //reset banner state if new language has no lexical model + if (currentBanner != null && currentBanner.equals(KM_BANNER_STATE_SUGGESTION) + && associatedLexicalModel == null) { + setCurrentBanner(KMKeyboard.KM_BANNER_STATE_BLANK); + } + + if(keyboardChanged) { + setLayoutParams(KMManager.getKeyboardLayoutParams()); + } + } + /** * Return the full path to the display text font. Usually used for creating a Typeface font * @return String diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java index 47d7d03231589b03907e2aaae509809fbfe7f3a4..2e939d15ba91716395f23b780b4d8899dc72b2a4 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java @@ -2,16 +2,28 @@ package com.keyman.engine; import android.content.Context; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.os.VibrationEffect; import android.os.Vibrator; import android.util.DisplayMetrics; import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; import android.webkit.JavascriptInterface; -import android.widget.RelativeLayout; import static android.content.Context.VIBRATOR_SERVICE; -public abstract class KMKeyboardJSHandler { +import com.keyman.engine.KMManager.KeyboardType; +import com.keyman.engine.util.CharSequenceUtil; +import com.keyman.engine.util.KMLog; + +public class KMKeyboardJSHandler { private Context context; private KMKeyboard k = null; private static int KM_VIBRATE_DURATION = 100; // milliseconds @@ -64,6 +76,116 @@ public abstract class KMKeyboardJSHandler { } } + /** + * Inserts the selected string s + * @param dn Number of pre-caret code points (UTF+8 characters) to delete + * @param s Text to insert + * @param dr Number of post-caret code points to delete. + */ + @JavascriptInterface + public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) { + Handler mainLoop = new Handler(Looper.getMainLooper()); + mainLoop.post(new Runnable() { + public void run() { + if (k == null) { + KMLog.LogError(TAG, "insertText failed: Keyboard is null"); + return; + } + + if (k.subKeysWindow != null) { + return; + } + + if (!isInappKMTextViewValid(k.keyboardType)) { + return; + } + + InputConnection ic = KMManager.getInputConnection(k.keyboardType); + if (ic == null) { + KMLog.LogError(TAG, "insertText failed: InputConnection is null"); + return; + } + + ic.beginBatchEdit(); + + int deleteLeft = dn; + + // Delete any existing selected text. + ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); + if (icText != null) { // This can be null if the input connection becomes invalid. + int start = icText.startOffset + icText.selectionStart; + int end = icText.startOffset + icText.selectionEnd; + if (end < start) { + // Swap start/end for backward selection + int temp = start; + start = end; + end = temp; + } + if (end > start) { + if (s.length() == 0) { + ic.setSelection(start, start); + ic.deleteSurroundingText(0, end - start); + ic.endBatchEdit(); + return; + } else { + k.setShouldIgnoreSelectionChange(true); + ic.setSelection(start, start); + ic.deleteSurroundingText(0, end - start); + } + + // KeymanWeb tells us how to delete the selection, but we don't + // want to do that twice + deleteLeft = 0; + } + } + + if (s.length() > 0 && s.charAt(0) == '\n') { + keyDownUp(KeyEvent.KEYCODE_ENTER, 0); + ic.endBatchEdit(); + return; + } + + // Perform left-deletions + if (deleteLeft > 0) { + if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { + k.setShouldIgnoreTextChange(true); + k.setShouldIgnoreSelectionChange(true); + } + performLeftDeletions(ic, deleteLeft); + } + + // Perform right-deletions + for (int i = 0; i < dr; i++) { + CharSequence chars = ic.getTextAfterCursor(1, 0); + if (chars != null && chars.length() > 0) { + char c = chars.charAt(0); + k.setShouldIgnoreSelectionChange(true); + if (Character.isHighSurrogate(c)) { + ic.deleteSurroundingText(0, 2); + } else { + ic.deleteSurroundingText(0, 1); + } + } + } + + if (s.length() > 0) { + k.setShouldIgnoreSelectionChange(true); + // Commit the string s. Use newCursorPosition 1 so cursor will end up after the string. + ic.commitText(s, 1); + } + + k.dismissHelpBubble(); + k.setShouldShowHelpBubble(false); + + ic.endBatchEdit(); + ViewGroup parent = (ViewGroup) k.getParent(); + if (parent != null && KMManager.getHapticFeedback() && !executingHardwareKeystroke) { + parent.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + } + } + }); + } + // Store the current keyboard chirality status from KMW in the Keyboard @JavascriptInterface public void setIsChiral(boolean isChiral) { @@ -73,14 +195,155 @@ public abstract class KMKeyboardJSHandler { } @JavascriptInterface - public abstract boolean dispatchKey(final int code, final int eventModifiers); + public boolean dispatchKey(final int code, final int eventModifiers) { + Handler mainLoop = new Handler(Looper.getMainLooper()); + mainLoop.post(new Runnable() { + public void run() { + if (k == null) { + KMLog.LogError(TAG, "dispatchKey failed: Keyboard is null"); + return; + } + + if (k.subKeysWindow != null) { + return; + } + + if (!isInappKMTextViewValid(k.keyboardType)) { + return; + } + + InputConnection ic = KMManager.getInputConnection(k.keyboardType); + if (ic == null) { + KMLog.LogError(TAG, "dispatchKey failed: InputConnection is null"); + return; + } + + k.dismissHelpBubble(); + k.setShouldShowHelpBubble(false); + + // Handle tab or enter since KMW didn't process it + if (KMManager.isDebugMode()) { + Log.d(TAG, "dispatchKey called with code: " + code + ", eventModifiers: " + eventModifiers); + } + if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { + keyDownUp(KeyEvent.KEYCODE_TAB, eventModifiers); + } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { + keyDownUp(KeyEvent.KEYCODE_ENTER, eventModifiers); + } + } + }); + return true; + } + + private void keyDownUp(int keyEventCode, int eventModifiers) { + if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { + KMTextView textView = (KMTextView)KMTextView.activeView; + if (keyEventCode == KeyEvent.KEYCODE_TAB) { + KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); + textView.dispatchKeyEvent(event); + } else { + textView.keyDownUp(keyEventCode); + } + } else if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + InputConnection ic = KMManager.getInputConnection(KeyboardType.KEYBOARD_TYPE_SYSTEM); + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); + } + } + + /* + // Chromium up until version M81 had a bug where deleteSurroundingText deletes an entire + // grapheme cluster instead of one code-point. See Chromium issue #1024738 + // https://bugs.chromium.org/p/chromium/issues/detail?id=1024738 + // + // We'll retrieve up to (dn*2+16) characters before the cursor to collect enough characters + // for surrogate pairs + a long grapheme cluster. + // This buffer will be used to put back characters as-needed + */ + private static void performLeftDeletions(InputConnection ic, int dn) { + int originalBufferLength = dn*2 + 16; // characters + CharSequence charsBackup = getCharacterSequence(ic, originalBufferLength); + + int lastIndex = charsBackup.length()-1; + + // Exit if there's no context to delete + if (lastIndex < 0) { + return; + } + + // Count the number of characters which are surrogate pairs + int numPairs = CharSequenceUtil.countSurrogatePairs(charsBackup, dn); + + // Chop dn+numPairs code points from the end of charsBackup + // subSequence indices are start(inclusive) to end(exclusive) + CharSequence expectedChars = charsBackup.subSequence(0, charsBackup.length() - (dn + numPairs)); + ic.deleteSurroundingText(dn + numPairs, 0); + CharSequence newContext = getCharacterSequence(ic, originalBufferLength - 2*dn); + + CharSequence charsToRestore = CharSequenceUtil.restoreChars(expectedChars, newContext); + if (charsToRestore.length() > 0) { + // Restore expectedChars that Chromium deleted. + // Use newCusorPosition 1 so cursor will be after the inserted string + ic.commitText(charsToRestore, 1); + } + } /** - * Inserts the selected string s - * @param dn Number of pre-caret code points (UTF+8 characters) to delete - * @param s Text to insert - * @param dr Number of post-caret code points to delete. + * Get a character sequence from the InputConnection. + * Sometimes the WebView can split a surrogate pair at either end, + * so chop that and update the cursor + * @param ic - the InputConnection + * @param length - number of characters to get + * @return CharSequence */ - @JavascriptInterface - public abstract void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke); + private static CharSequence getCharacterSequence(InputConnection ic, int length) { + if (ic == null || length <= 0) { + return ""; + } + + CharSequence sequence = ic.getTextBeforeCursor(length, 0); + if (sequence == null || sequence.length() <= 0) { + return ""; + } + + // Move the cursor back if there's a split surrogate pair + if (Character.isHighSurrogate(sequence.charAt(sequence.length()-1))) { + ic.commitText("", -1); + sequence = ic.getTextBeforeCursor(length, 0); + } + + if (sequence == null || sequence.length() <= 0) { + return ""; + } + + if (Character.isLowSurrogate(sequence.charAt(0))) { + // Adjust if the first char is also a split surrogate pair + // subSequence indices are start(inclusive) to end(exclusive) + sequence = sequence.subSequence(1, sequence.length()); + } + + return sequence; + } + + /** + * If the keyboard type is KEYBOARD_TYPE_INAPP, check if the KMTextView is valid. + * For KEYBOARD_TYPE_SYSTEM, returns true. + * @param keyboardType + * @return boolean - false if keyboard type is INAPP and KMTextView is invalid. Otherwise true + */ + private static boolean isInappKMTextViewValid(KeyboardType keyboardType) { + if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { + if (KMTextView.activeView == null) { + if (KMManager.isDebugMode()) { + Log.w(TAG, "activeView is null"); + } + return false; + } + if (KMTextView.activeView.getClass() != KMTextView.class) { + return false; + } + } + + return true; + } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java index 559e06fa6cc3a9508aa7148e818057be940bddab..7ea366f0d12b443cafdf3a9695b90f30036d734a 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java @@ -245,8 +245,8 @@ public final class KMKeyboardWebViewClient extends WebViewClient { if (KMManager.currentLexicalModel != null) { modelPredictionPref = prefs.getBoolean(KMManager.getLanguagePredictionPreferenceKey(KMManager.currentLexicalModel.get(KMManager.KMKey_LanguageID)), true); } - KMManager.currentBanner = (isModelActive && modelPredictionPref) ? - KMManager.KM_BANNER_STATE_SUGGESTION : KMManager.KM_BANNER_STATE_BLANK; + kmKeyboard.setCurrentBanner((isModelActive && modelPredictionPref) ? + KMKeyboard.KM_BANNER_STATE_SUGGESTION : KMKeyboard.KM_BANNER_STATE_BLANK); RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams(); kmKeyboard.setLayoutParams(params); } else if (url.indexOf("suggestPopup") >= 0) { diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java index b8e115f3e89d3cc8c408f383a27633a0266634f0..ebb8727bef53e5c39457f7f5bfeb1fcad89d7e82 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java @@ -158,6 +158,7 @@ public final class KMManager { }; protected static InputMethodService IMService; + private static boolean debugMode = false; private static boolean shouldAllowSetKeyboard = true; private static boolean didCopyAssets = false; @@ -174,10 +175,6 @@ public final class KMManager { private static KMManager.SpacebarText spacebarText = KMManager.SpacebarText.LANGUAGE_KEYBOARD; // must match default given in kmwbase.ts - protected static boolean InAppKeyboardShouldIgnoreTextChange = false; - protected static boolean InAppKeyboardShouldIgnoreSelectionChange = false; - protected static boolean SystemKeyboardShouldIgnoreTextChange = false; - protected static boolean SystemKeyboardShouldIgnoreSelectionChange = false; protected static KMKeyboard InAppKeyboard = null; protected static KMKeyboard SystemKeyboard = null; protected static KMKeyboardWebViewClient InAppKeyboardWebViewClient = null; @@ -189,22 +186,6 @@ public final class KMManager { public final static String predictionPrefSuffix = ".mayPredict"; public final static String correctionPrefSuffix = ".mayCorrect"; - /** - * Banner state value: "blank" - no banner available. - */ - protected static final String KM_BANNER_STATE_BLANK = "blank"; - /** - * Banner state value: "suggestion" - dictionary suggestions are shown. - */ - protected static final String KM_BANNER_STATE_SUGGESTION = "suggestion"; - - //TODO: should be part of kmkeyboard - /** - * Current banner state. - */ - protected static String currentBanner = KM_BANNER_STATE_BLANK; - - // Special override for when the keyboard may have haptic feedback when typing. // haptic feedback disabled for hardware keystrokes private static boolean mayHaveHapticFeedback = false; @@ -421,13 +402,11 @@ public final class KMManager { didCopyAssets = true; } - if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { - initInAppKeyboard(appContext); - } else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - initSystemKeyboard(appContext); - } else { + if (keyboardType == KeyboardType.KEYBOARD_TYPE_UNDEFINED) { String msg = "Cannot initialize: Invalid keyboard type"; KMLog.LogError(TAG, msg); + } else { + initKeyboard(appContext, keyboardType); } JSONUtils.initialize(new File(getPackagesDir())); @@ -459,6 +438,22 @@ public final class KMManager { } public static InputMethodService getInputMethodService() { return IMService; } + /** + * Get the input connection based on the keyboard type. + * @param {KeyboardType} keyboard + * @return InputConnection + */ + protected static InputConnection getInputConnection(KeyboardType keyboard) { + if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) { + return KMTextView.activeView.onCreateInputConnection(new EditorInfo()); + } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && IMService != null) { + return IMService.getCurrentInputConnection(); + } + + KMLog.LogError(TAG, "Unable to determine input connection"); + return null; + } + public static boolean executeHardwareKeystroke(int code, int shift, int lstates, int eventModifiers) { if (SystemKeyboard != null) { return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_SYSTEM, lstates, eventModifiers); @@ -609,36 +604,35 @@ public final class KMManager { return params; } - private static void initInAppKeyboard(Context appContext) { - if (InAppKeyboard == null) { + private static void initKeyboard(Context appContext, KeyboardType keyboardType) { + KMKeyboard keyboard = null; + KMKeyboardWebViewClient webViewClient = null; + + if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard == null) { InAppKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_INAPP); - RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - InAppKeyboard.setLayoutParams(params); - InAppKeyboard.setVerticalScrollBarEnabled(false); - InAppKeyboard.setHorizontalScrollBarEnabled(false); - InAppKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, KeyboardType.KEYBOARD_TYPE_INAPP); - InAppKeyboard.setWebViewClient(InAppKeyboardWebViewClient); - InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext, InAppKeyboard), "jsInterface"); - InAppKeyboard.loadKeyboard(); + InAppKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType); + keyboard = InAppKeyboard; + webViewClient = InAppKeyboardWebViewClient; + } else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard == null) { + SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM); + SystemKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType); + keyboard = SystemKeyboard; + webViewClient = SystemKeyboardWebViewClient; + } - setEngineWebViewVersionStatus(appContext, InAppKeyboard); + if (keyboard == null) { + return; } - } - private static void initSystemKeyboard(Context appContext) { - if (SystemKeyboard == null) { - SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM); - RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - SystemKeyboard.setLayoutParams(params); - SystemKeyboard.setVerticalScrollBarEnabled(false); - SystemKeyboard.setHorizontalScrollBarEnabled(false); - SystemKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM); - SystemKeyboard.setWebViewClient(SystemKeyboardWebViewClient); - SystemKeyboard.addJavascriptInterface(new KMSystemKeyboardJSHandler(appContext, SystemKeyboard), "jsInterface"); - SystemKeyboard.loadKeyboard(); + RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); + keyboard.setLayoutParams(params); + keyboard.setVerticalScrollBarEnabled(false); + keyboard.setHorizontalScrollBarEnabled(false); + keyboard.setWebViewClient(webViewClient); + keyboard.addJavascriptInterface(new KMKeyboardJSHandler(appContext, keyboard), "jsInterface"); + keyboard.loadKeyboard(); - setEngineWebViewVersionStatus(appContext, SystemKeyboard); - } + setEngineWebViewVersionStatus(appContext, keyboard); } public static String getLanguagePredictionPreferenceKey(String langID) { @@ -663,12 +657,12 @@ public final class KMManager { public static boolean isKeyboardLoaded(KeyboardType type) { if (type == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (InAppKeyboardWebViewClient != null) { + if (InAppKeyboard != null && InAppKeyboardWebViewClient != null) { return InAppKeyboardWebViewClient.getKeyboardLoaded(); } return false; } else if (type == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (SystemKeyboardWebViewClient != null) { + if (SystemKeyboard != null && SystemKeyboardWebViewClient != null) { return SystemKeyboardWebViewClient.getKeyboardLoaded(); } return false; @@ -764,7 +758,7 @@ public final class KMManager { * @param permission - The manifest permission to query * @return boolean - true if the manifest permission is granted */ - protected static boolean hasPermission(Context context, String permission) { + public static boolean hasPermission(Context context, String permission) { // API to check permission depends on Android SDK level if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED); @@ -1331,12 +1325,12 @@ public final class KMManager { boolean mayCorrect = prefs.getBoolean(getLanguageCorrectionPreferenceKey(languageID), true); RelativeLayout.LayoutParams params; - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded() && !InAppKeyboardShouldIgnoreTextChange && modelFileExists) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange() && modelFileExists) { params = getKeyboardLayoutParams(); InAppKeyboard.setLayoutParams(params); InAppKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect)); } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && !SystemKeyboardShouldIgnoreTextChange && modelFileExists) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange() && modelFileExists) { params = getKeyboardLayoutParams(); SystemKeyboard.setLayoutParams(params); SystemKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect)); @@ -1501,11 +1495,13 @@ public final class KMManager { boolean result1 = false; boolean result2 = false; - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded()) + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { result1 = InAppKeyboard.setKeyboard(packageID, keyboardID, languageID); + } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { result2 = SystemKeyboard.setKeyboard(packageID, keyboardID, languageID); + } return (result1 || result2); } @@ -1513,20 +1509,26 @@ public final class KMManager { public static boolean setKeyboard(Keyboard keyboardInfo) { boolean result1 = false; boolean result2 = false; + HashMap associatedLexicalModel = null; + String languageID = null; + + if (keyboardInfo != null) { + languageID = keyboardInfo.getLanguageID(); + associatedLexicalModel = getAssociatedLexicalModel(languageID); + } - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded() && keyboardInfo != null) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && keyboardInfo != null) { result1 = InAppKeyboard.setKeyboard(keyboardInfo); + InAppKeyboard.toggleSuggestionBanner(associatedLexicalModel, result1); } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && keyboardInfo != null) + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && keyboardInfo != null) { result2 = SystemKeyboard.setKeyboard(keyboardInfo); - - if (keyboardInfo != null) { - String languageID = keyboardInfo.getLanguageID(); - toggleSuggestionBanner(languageID, result1, result2); - registerAssociatedLexicalModel(languageID); + SystemKeyboard.toggleSuggestionBanner(associatedLexicalModel, result2); } + registerAssociatedLexicalModel(languageID); + return (result1 || result2); } @@ -1539,21 +1541,19 @@ public final class KMManager { * @return the success result */ public static boolean prepareKeyboardSwitch(String packageID, String keyboardID, String languageID, String keyboardName) { - - boolean result1 = false; boolean result2 = false; - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded()) - { + HashMap associatedLexicalModel = getAssociatedLexicalModel(languageID); + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { result1 = InAppKeyboard.prepareKeyboardSwitch(packageID, keyboardID, languageID,keyboardName); + InAppKeyboard.toggleSuggestionBanner(associatedLexicalModel, result1); } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) - { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { result2 = SystemKeyboard.prepareKeyboardSwitch(packageID, keyboardID, languageID,keyboardName); + SystemKeyboard.toggleSuggestionBanner(associatedLexicalModel, result2); } - toggleSuggestionBanner(languageID, result1, result2); registerAssociatedLexicalModel(languageID); return (result1 || result2); @@ -1563,11 +1563,13 @@ public final class KMManager { boolean result1 = false; boolean result2 = false; - if (InAppKeyboard != null && (InAppKeyboardWebViewClient.getKeyboardLoaded() || isTestMode())) + if (InAppKeyboard != null && (InAppKeyboardWebViewClient.getKeyboardLoaded() || isTestMode())) { result1 = InAppKeyboard.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont); + } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { result2 = SystemKeyboard.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont); + } registerAssociatedLexicalModel(languageID); @@ -1606,11 +1608,11 @@ public final class KMManager { } public static void clearKeyboardCache() { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { InAppKeyboard.clearCache(true); InAppKeyboard.loadKeyboard(); } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { SystemKeyboard.clearCache(true); SystemKeyboard.loadKeyboard(); } @@ -1636,7 +1638,7 @@ public final class KMManager { if (IMService != null) { IMService.switchToPreviousInputMethod(); } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + } else { // This method is added in API level 16 and deprecated in API level 28 // Reference: https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#switchToLastInputMethod(android.os.IBinder) InputMethodManager imm = (InputMethodManager) appContext.getSystemService(Context.INPUT_METHOD_SERVICE); @@ -1649,7 +1651,7 @@ public final class KMManager { if (IMService != null) { IMService.switchToNextInputMethod(false); } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + } else { // This method is added in API level 16 and deprecated in API level 28 // Reference: https://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html#switchToNextInputMethod(android.os.IBinder,%20boolean) InputMethodManager imm = (InputMethodManager) appContext.getSystemService(Context.INPUT_METHOD_SERVICE); @@ -1838,7 +1840,9 @@ public final class KMManager { public static int getBannerHeight(Context context) { int bannerHeight = 0; - if (currentBanner.equals(KM_BANNER_STATE_SUGGESTION)) { + if (InAppKeyboard != null && InAppKeyboard.currentBanner().equals(KMKeyboard.KM_BANNER_STATE_SUGGESTION)) { + bannerHeight = (int) context.getResources().getDimension(R.dimen.banner_height); + } else if (SystemKeyboard != null && SystemKeyboard.currentBanner().equals(KMKeyboard.KM_BANNER_STATE_SUGGESTION)) { bannerHeight = (int) context.getResources().getDimension(R.dimen.banner_height); } return bannerHeight; @@ -1858,12 +1862,12 @@ public final class KMManager { } public static void applyKeyboardHeight(Context context, int height) { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { InAppKeyboard.loadJavascript(KMString.format("setOskHeight('%s')", height)); RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); InAppKeyboard.setLayoutParams(params); } - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { SystemKeyboard.loadJavascript(KMString.format("setOskHeight('%s')", height)); RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); SystemKeyboard.setLayoutParams(params); @@ -1934,11 +1938,11 @@ public final class KMManager { public static void setNumericLayer(KeyboardType kbType) { if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded() && !InAppKeyboardShouldIgnoreTextChange) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange()) { InAppKeyboard.loadJavascript("setNumericLayer()"); } } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && !SystemKeyboardShouldIgnoreTextChange) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange()) { SystemKeyboard.loadJavascript("setNumericLayer()"); } } @@ -1946,25 +1950,11 @@ public final class KMManager { public static boolean updateText(KeyboardType kbType, String text) { boolean result = false; - String kmText = ""; - if (text != null) { - kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n"); - } if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded() && !InAppKeyboardShouldIgnoreTextChange) { - InAppKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText)); - result = true; - } - - InAppKeyboardShouldIgnoreTextChange = false; + return InAppKeyboard.updateText(text); } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && !SystemKeyboardShouldIgnoreTextChange) { - SystemKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText)); - result = true; - } - - SystemKeyboardShouldIgnoreTextChange = false; + return SystemKeyboard.updateText(text); } return result; @@ -1973,27 +1963,17 @@ public final class KMManager { public static boolean updateSelectionRange(KeyboardType kbType, int selStart, int selEnd) { boolean result = false; if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded() && !InAppKeyboardShouldIgnoreSelectionChange) { - InAppKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); - result = true; + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) { + result = InAppKeyboard.updateSelectionRange(selStart, selEnd); } - InAppKeyboardShouldIgnoreSelectionChange = false; + InAppKeyboard.setShouldIgnoreSelectionChange(false); } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && !SystemKeyboardShouldIgnoreSelectionChange) { - InputConnection ic = (IMService != null ? IMService.getCurrentInputConnection() : null); - if (ic != null) { - ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); - if (icText != null) { - updateText(kbType, icText.text.toString()); - } - } - - SystemKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); - result = true; + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) { + result = SystemKeyboard.updateSelectionRange(selStart, selEnd); } - SystemKeyboardShouldIgnoreSelectionChange = false; + SystemKeyboard.setShouldIgnoreSelectionChange(false); } return result; @@ -2001,11 +1981,11 @@ public final class KMManager { public static void resetContext(KeyboardType kbType) { if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (InAppKeyboard != null && InAppKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { InAppKeyboard.loadJavascript("resetContext()"); } } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded()) { + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { SystemKeyboard.loadJavascript("resetContext()"); } } @@ -2152,21 +2132,6 @@ public final class KMManager { globeKeyState = state; } - private static void toggleSuggestionBanner(String languageID, boolean inappKeyboardChanged, boolean systemKeyboardChanged) { - //reset banner state if new language has no lexical model - if (currentBanner.equals(KMManager.KM_BANNER_STATE_SUGGESTION) - && getAssociatedLexicalModel(languageID)==null) { - currentBanner = KMManager.KM_BANNER_STATE_BLANK; - } - - if(inappKeyboardChanged) { - InAppKeyboard.setLayoutParams(getKeyboardLayoutParams()); - } - if(systemKeyboardChanged) { - SystemKeyboard.setLayoutParams(getKeyboardLayoutParams()); - } - } - /** * Return if the lock screen is locked (prevents keyboard picker menu from being displayed) * @return boolean @@ -2226,387 +2191,4 @@ public final class KMManager { globeKeyState = GlobeKeyState.GLOBE_KEY_STATE_UP; } } - - private static final class KMInAppKeyboardJSHandler extends KMKeyboardJSHandler { - - KMInAppKeyboardJSHandler(Context context, KMKeyboard k) { - super(context, k); - } - private static final String HANDLER_TAG = "IAK: JS Handler"; - - @JavascriptInterface - public boolean dispatchKey(final int code, final int eventModifiers) { - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (InAppKeyboard == null) { - KMLog.LogError(TAG, "dispatchKey failed: InAppKeyboard is null"); - return; - } - - if (InAppKeyboard.subKeysWindow != null || KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class) { - if ((KMTextView.activeView == null) && isDebugMode()) { - Log.w(HANDLER_TAG, "dispatchKey failed: activeView is null"); - } - return; - } - - // Handle tab or enter since KMW didn't process it - KMTextView textView = (KMTextView) KMTextView.activeView; - if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); - textView.dispatchKeyEvent(event); - } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_ENTER, 0, eventModifiers, 0, 0, 0); - textView.dispatchKeyEvent(event); - } - } - }); - return true; - } - - // This annotation is required in Jelly Bean and later: - @JavascriptInterface - public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) { - if(dr != 0) { - Log.d(TAG, "Right deletions requested but are not presently supported by the in-app keyboard."); - } - - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (InAppKeyboard == null) { - KMLog.LogError(TAG, "insertText failed: InAppKeyboard is null"); - return; - } - - if (InAppKeyboard.subKeysWindow != null || KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class) { - if ((KMTextView.activeView == null) && isDebugMode()) { - Log.w("IAK: JS Handler", "insertText failed: activeView is null"); - } - return; - } - - InAppKeyboard.dismissHelpBubble(); - InAppKeyboard.setShouldShowHelpBubble(false); - - KMTextView textView = (KMTextView) KMTextView.activeView; - textView.beginBatchEdit(); - - int start = textView.getSelectionStart(); - int end = textView.getSelectionEnd(); - // Workaround for Android TextView bug where end < start - // Reference: https://issuetracker.google.com/issues/36911048 - if (end < start) { - Log.d(TAG, "Swapping TextView selection end:" + end + " and start:" + start); - int temp = end; - end = start; - start = temp; - } - - int deleteLeft = dn; - - if(start != end && dn == 1 && s.length() == 0) { - /* Handle backspace with a selection: just delete selection */ - deleteLeft = 0; - } - - if (deleteLeft <= 0) { - if (start == end) { - if (s.length() > 0 && s.charAt(0) == '\n') { - textView.keyDownUp(KeyEvent.KEYCODE_ENTER); - } else if (s.length() > 0) { - // *** TO DO: Try to find a solution to the bug on API < 17, insert overwrites on next line - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - textView.getText().insert(start, s); - } else { - textView.getText().delete(start, end); - } - } else { - if (s.length() > 0 && s.charAt(0) == '\n') { - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - textView.getText().replace(start, end, ""); - textView.keyDownUp(KeyEvent.KEYCODE_ENTER); - } else { - if (s.length() == 0) { - textView.getText().delete(start, end); - } else { - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - textView.getText().replace(start, end, s); - } - } - } - } else { - if(start != end) { - // Delete the selection - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - textView.getText().delete(start, end); - textView.setSelection(start); - end = start; - deleteLeft = 0; - } - for (int i = 0; i < deleteLeft; i++) { - CharSequence chars = textView.getText().subSequence(0, start); - if (chars != null && chars.length() > 0) { - char c = chars.charAt(start - 1); - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - if (Character.isLowSurrogate(c)) { - textView.getText().delete(start - 2, end); - } else { - textView.getText().delete(start - 1, end); - } - - start = textView.getSelectionStart(); - end = textView.getSelectionEnd(); - } - } - - if (s.length() > 0) { - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - textView.getText().insert(start, s); - } - } - - // Collapse the selection - textView.setSelection(start + s.length()); - textView.endBatchEdit(); - if (mayHaveHapticFeedback && !executingHardwareKeystroke) { - textView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } - } - }); - } - } - - private static final class KMSystemKeyboardJSHandler extends KMKeyboardJSHandler { - KMSystemKeyboardJSHandler(Context context, KMKeyboard k) { - super(context, k); - } - private static final String HANDLER_TAG = "SWK: JS Handler"; - - @JavascriptInterface - public boolean dispatchKey(final int code, final int eventModifiers) { - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (SystemKeyboard == null) { - KMLog.LogError(TAG, "dispatchKey failed: SystemKeyboard is null"); - return; - } - - if (SystemKeyboard.subKeysWindow != null) { - return; - } - - InputConnection ic = IMService.getCurrentInputConnection(); - if (ic == null) { - if (isDebugMode()) { - Log.w(HANDLER_TAG, "insertText failed: InputConnection is null"); - } - return; - } - - SystemKeyboard.dismissHelpBubble(); - SystemKeyboard.setShouldShowHelpBubble(false); - - // Handle tab or enter since KMW didn't process it - Log.d(HANDLER_TAG, "dispatchKey called with code: " + code + ", eventModifiers: " + eventModifiers); - if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); - ic.sendKeyEvent(event); - } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_ENTER, 0, eventModifiers, 0, 0, 0); - ic.sendKeyEvent(event); - } - } - }); - return true; - } - - // This annotation is required in Jelly Bean and later: - @JavascriptInterface - public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) { - // TODO: Unify in-app and system insertText - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (SystemKeyboard == null) { - KMLog.LogError(TAG, "insertText failed: SystemKeyboard is null"); - return; - } - - if (SystemKeyboard.subKeysWindow != null) { - return; - } - - InputConnection ic = IMService.getCurrentInputConnection(); - if (ic == null) { - if (isDebugMode()) { - Log.w(HANDLER_TAG, "insertText failed: InputConnection is null"); - } - return; - } - - ic.beginBatchEdit(); - - int deleteLeft = dn; - - // Delete any existing selected text. - ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); - if (icText != null) { // This can be null if the input connection becomes invalid. - int start = icText.startOffset + icText.selectionStart; - int end = icText.startOffset + icText.selectionEnd; - if (end < start) { - // Swap start/end for backward selection - int temp = start; - start = end; - end = temp; - } - if (end > start) { - if (s.length() == 0) { - ic.setSelection(start, start); - ic.deleteSurroundingText(0, end - start); - ic.endBatchEdit(); - return; - } else { - SystemKeyboardShouldIgnoreSelectionChange = true; - ic.setSelection(start, start); - ic.deleteSurroundingText(0, end - start); - } - - // KeymanWeb tells us how to delete the selection, but we don't - // want to do that twice - deleteLeft = 0; - } - } - - if (s.length() > 0 && s.charAt(0) == '\n') { - keyDownUp(KeyEvent.KEYCODE_ENTER); - ic.endBatchEdit(); - return; - } - - // Perform left-deletions - if (deleteLeft > 0) { - performLeftDeletions(ic, deleteLeft); - } - - // Perform right-deletions - for (int i = 0; i < dr; i++) { - CharSequence chars = ic.getTextAfterCursor(1, 0); - if (chars != null && chars.length() > 0) { - char c = chars.charAt(0); - SystemKeyboardShouldIgnoreSelectionChange = true; - if (Character.isHighSurrogate(c)) { - ic.deleteSurroundingText(0, 2); - } else { - ic.deleteSurroundingText(0, 1); - } - } - } - - if (s.length() > 0) { - SystemKeyboardShouldIgnoreSelectionChange = true; - - // Commit the string s. Use newCursorPosition 1 so cursor will end up after the string. - ic.commitText(s, 1); - } - - SystemKeyboard.dismissHelpBubble(); - SystemKeyboard.setShouldShowHelpBubble(false); - - ic.endBatchEdit(); - ViewGroup parent = (ViewGroup) SystemKeyboard.getParent(); - if (parent != null && mayHaveHapticFeedback && !executingHardwareKeystroke) { - parent.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } - } - }); - } - - private void keyDownUp(int keyEventCode) { - IMService.getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); - IMService.getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); - } - - /* - // Chromium up until version M81 had a bug where deleteSurroundingText deletes an entire - // grapheme cluster instead of one code-point. See Chromium issue #1024738 - // https://bugs.chromium.org/p/chromium/issues/detail?id=1024738 - // - // We'll retrieve up to (dn*2+16) characters before the cursor to collect enough characters - // for surrogate pairs + a long grapheme cluster. - // This buffer will be used to put back characters as-needed - */ - private static void performLeftDeletions(InputConnection ic, int dn) { - int originalBufferLength = dn*2 + 16; // characters - CharSequence charsBackup = getCharacterSequence(ic, originalBufferLength); - - int lastIndex = charsBackup.length()-1; - - // Exit if there's no context to delete - if (lastIndex < 0) { - return; - } - - // Count the number of characters which are surrogate pairs - int numPairs = CharSequenceUtil.countSurrogatePairs(charsBackup, dn); - - // Chop dn+numPairs code points from the end of charsBackup - // subSequence indices are start(inclusive) to end(exclusive) - CharSequence expectedChars = charsBackup.subSequence(0, charsBackup.length() - (dn + numPairs)); - ic.deleteSurroundingText(dn + numPairs, 0); - CharSequence newContext = getCharacterSequence(ic, originalBufferLength - 2*dn); - - CharSequence charsToRestore = CharSequenceUtil.restoreChars(expectedChars, newContext); - if (charsToRestore.length() > 0) { - // Restore expectedChars that Chromium deleted. - // Use newCusorPosition 1 so cursor will be after the inserted string - ic.commitText(charsToRestore, 1); - } - } - - /** - * Get a character sequence from the InputConnection. - * Sometimes the WebView can split a surrogate pair at either end, - * so chop that and update the cursor - * @param ic - the InputConnection - * @param length - number of characters to get - * @return CharSequence - */ - private static CharSequence getCharacterSequence(InputConnection ic, int length) { - if (ic == null || length <= 0) { - return ""; - } - - CharSequence sequence = ic.getTextBeforeCursor(length, 0); - if (sequence == null || sequence.length() <= 0) { - return ""; - } - - // Move the cursor back if there's a split surrogate pair - if (Character.isHighSurrogate(sequence.charAt(sequence.length()-1))) { - ic.commitText("", -1); - sequence = ic.getTextBeforeCursor(length, 0); - } - - if (sequence == null || sequence.length() <= 0) { - return ""; - } - - if (Character.isLowSurrogate(sequence.charAt(0))) { - // Adjust if the first char is also a split surrogate pair - // subSequence indices are start(inclusive) to end(exclusive) - sequence = sequence.subSequence(1, sequence.length()); - } - - return sequence; - } - } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java b/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java index 0e1db6847fb9b586325738dd37efd760bc5b700d..1111d92978b25ce92e4253352d6c82b45554d103 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java @@ -436,7 +436,11 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl .setAutoCancel(true) .setContentIntent(startUpdateIntent); - notificationManager.notify(notification_id,builder.build()); + try { + notificationManager.notify(notification_id, builder.build()); + } catch (SecurityException e) { + KMLog.LogException(TAG, "Security permission POST_NOTIFICATIONS denied", e); + } } private boolean isContextAvailable() { diff --git a/android/KMEA/app/src/main/res/values-am-rET/strings.xml b/android/KMEA/app/src/main/res/values-am-rET/strings.xml index dc9e7711add173453a6ffa4f061b698d50420e63..1bb95d51576187d5626c1791145dedbdc8b0b9ef 100644 --- a/android/KMEA/app/src/main/res/values-am-rET/strings.xml +++ b/android/KMEA/app/src/main/res/values-am-rET/strings.xml @@ -168,5 +168,5 @@ የፊደል ገበታውን ለመቀየር እዚህ ጋር ንካ - Unable to launch web browser + ብራውሰሩን ማስጀመር አልተቻለም diff --git a/android/KMEA/app/src/test/resources/com/tavultesoft/kmea/robolectric.properties b/android/KMEA/app/src/test/resources/com/tavultesoft/kmea/robolectric.properties index 1c3d9c523c48b87c383ca4417adbcf1a28494fa8..480ab323eae4117794ffd24d9816472a8a318997 100644 --- a/android/KMEA/app/src/test/resources/com/tavultesoft/kmea/robolectric.properties +++ b/android/KMEA/app/src/test/resources/com/tavultesoft/kmea/robolectric.properties @@ -1,3 +1,2 @@ -#Android SDK 29 requires java 9. -#To execute the tests on during the automatic build process, we need to force robolectric to use an older sdk version for test -sdk=28 \ No newline at end of file +#Android SDK to use for tests. +sdk=33 \ No newline at end of file diff --git a/android/KMEA/build.gradle b/android/KMEA/build.gradle index 55a31cb9af6d4dacfab68362ebb7ed65074c9eb1..8e6b39bd3e3cb4051878835e18e2031df016a423 100644 --- a/android/KMEA/build.gradle +++ b/android/KMEA/build.gradle @@ -5,9 +5,7 @@ buildscript { mavenCentral() } dependencies { - // Gradle plugin 7.3.0+ requires JDK 11 - // https://developer.android.com/studio/releases/gradle-plugin#compatibility-7-3-0 - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.4.0' // io.sentry:sentry-android-gradle-plugin not available for library project classpath 'io.sentry:sentry-android:6.9.2' classpath 'name.remal:gradle-plugins:1.5.0' diff --git a/android/KMEA/build.sh b/android/KMEA/build.sh index 4ddc7a206bf6a27654cae4c7d73ead73055b376c..c479d9a13375a226ef115a6820c5c3efecb14855 100755 --- a/android/KMEA/build.sh +++ b/android/KMEA/build.sh @@ -50,7 +50,7 @@ fi builder_describe_outputs \ configure:engine /android/KMEA/app/src/main/assets/keymanandroid.js \ - build:engine /android/KMEA/app/build/outputs/aar/${CONFIG}/keyman-engine.aar + build:engine /android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar #### Build @@ -72,6 +72,14 @@ fi if builder_start_action configure:engine; then + builder_finish_action success configure:engine +fi + +# Destinations that will need the keymanweb artifacts + + +if builder_start_action build:engine; then + # Copy KeymanWeb artifacts echo "Copying Keyman Web artifacts" cp "$KEYMAN_WEB_ROOT/build/app/embed/$CONFIG/osk/ajax-loader.gif" "$ENGINE_ASSETS/ajax-loader.gif" @@ -86,14 +94,6 @@ if builder_start_action configure:engine; then echo "Copying es6-shim polyfill" cp "$KEYMAN_ROOT/node_modules/es6-shim/es6-shim.min.js" "$ENGINE_ASSETS/es6-shim.min.js" - builder_finish_action success configure:engine -fi - -# Destinations that will need the keymanweb artifacts - - -if builder_start_action build:engine; then - echo "BUILD_FLAGS $BUILD_FLAGS" # Build without test ./gradlew $DAEMON_FLAG clean $BUILD_FLAGS diff --git a/android/KMEA/gradle/wrapper/gradle-wrapper.properties b/android/KMEA/gradle/wrapper/gradle-wrapper.properties index 41dfb87909a877d96c3af4adccce4c7a301b55a2..ae04661ee733431762e7ccf8ab9b7409ed44960c 100644 --- a/android/KMEA/gradle/wrapper/gradle-wrapper.properties +++ b/android/KMEA/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/README.md b/android/README.md index ffdbb4a015c1f9f540f5710b5cb524ac29ad4484..9b138ccfceae6e50d134894959c4ddc57a17a925 100644 --- a/android/README.md +++ b/android/README.md @@ -1,33 +1,48 @@ # Keyman for Android & Keyman Engine for Android ## Prerequisites -See [build configuration](../docs/build/index.md) for details on how to configure your build environment. + +See [build configuration](../docs/build/index.md) for details on how to configure +your build environment. ## Keyman Minimum Android Requirements + Keyman for Android has a minSdkVersion of 21 for [Android 5.0 Lollipop](https://developer.android.com/about/versions/lollipop) ## Keyman for Android Development -Keyman for Android (formerly named KMAPro) can be built from a command line (preferred) or Android Studio. -Building Keyman Web is a precursor for compiling KMEA, so verify your system has all the [Minimum Web Compilation Requirements](../web/README.md#minimum-web-compilation-requirements) +Keyman for Android (formerly named KMAPro) can be built from a command line +(preferred) or Android Studio. + +Building Keyman Web is a precursor for compiling KMEA, so verify your system has +all the [Minimum Web Compilation Requirements](../web/README.md#minimum-web-compilation-requirements) + +When attempting to build via Android Studio the first time, you'll also need to +accept the SDK licenses. Depending on your OS, you may need to run a command +line similar to: -When attempting to build via Android Studio the first time, you'll also need to accept the SDK licenses. Depending on your OS, you may need to run a command line similar to: ```bash yes | ~/Library/Android/sdk/tools/bin/sdkmanager --licenses ``` ### Crash Reporting -Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a server https://sentry.keyman.com. The analytics for Debug are associated with an App Bundle ID `com.tavultesoft.kmapro.debug`. - + +Keyman for Android uses [Sentry](https://sentry.io) for crash reporting. The +analytics for Debug are associated with an App Bundle ID +`com.tavultesoft.kmapro.debug`. + ### Compiling From Command Line + 1. Launch a command prompt and cd to the directory **keyman/android** 2. Run the top level build script `./build.sh configure build --debug` which will: * Compile KMEA (and its KMW dependency) * Download default keyboard and dictionary resources as needed * Compile KMAPro -3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-${version}.apk** where `${version}` is the current version number. +3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-\${version}.apk** + where `${version}` is the current version number. ### Compiling From Android Studio + 1. Ensure that [Keyman Engine for Android](#how-to-build-keyman-engine-for-android) is built. 2. Launch Android Studio and import the Gradle project **keyman/android/KMAPro/build.gradle** 3. From the project view, *configure* anything that Gradle reports. @@ -37,24 +52,31 @@ Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a ser 3. Change Module to kMAPro 4. Name your run configuration kMAPro (or as desired) 5. Run your new configuration: Select Run --> Run 'kMAPro' -6. Select a physical device or create a new virtual device to match your target API version - For Ubuntu 18.04, you will need to add your user to the `kvm` group for permission accessing the emulator. -``` +6. Select a physical device or create a new virtual device to match your target + API version. + + For Ubuntu 18.04, you will need to add your user to the `kvm` group + for permission accessing the emulator. + + ```bash sudo apt install qemu-kvm sudo adduser kvm -``` + ``` ### Running From Command Line + 1. Launch a command prompt 2. Ensure that a physical device is connected or an emulator is running. 1. To list physical devices: `$ANDROID_HOME/platform-tools/adb.exe devices` 2. To list created emulator devices: `$ANDROID_HOME/tools/emulator.exe -list-avds` - 3. [Create a new virtual device](https://developer.android.com/studio/run/managing-avds.html) if there are no - existing devices. + 3. [Create a new virtual device](https://developer.android.com/studio/run/managing-avds.html) + if there are no existing devices. 4. Start an emulator: `$ANDROID_HOME/tools/emulator.exe @nameofemulator` 3. Load the APK: `$ANDROID_HOME/platform-tools/adb.exe install -r path/to/apk.apk` - * If multiple devices are connected you may need `$ANDROID_HOME/platform-tools/adb.exe install -r -s SERIAL path/to/apk.apk`. Replace `SERIAL` with - the device serial number listed in step 2. + + * If multiple devices are connected you may need + `$ANDROID_HOME/platform-tools/adb.exe install -r -s SERIAL path/to/apk.apk`. + Replace `SERIAL` with the device serial number listed in step 2. ### Compiling the app's offline help Keyman for Android help is maintained in the Markdown files in android/help/. @@ -119,10 +141,6 @@ android { aaptOptions { noCompress "kmp" } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } ``` 5. After the `android {}` object, include the following: ````gradle @@ -142,7 +160,7 @@ dependencies { implementation 'androidx.preference:preference:1.2.0' // Include this if you want to have QR Codes displayed on Keyboard Info - implementation ('com.github.kenglxn.QRGen:android:2.7.0') { + implementation ('com.github.kenglxn.QRGen:android:3.0.1') { transitive = true } } diff --git a/android/Samples/KMSample1/app/build.gradle b/android/Samples/KMSample1/app/build.gradle index 23dbaf892b0ccf13697e9745cf0b5a55727056c7..7be5ef6b1b5974ec9be48b3b62752e431bee9f34 100644 --- a/android/Samples/KMSample1/app/build.gradle +++ b/android/Samples/KMSample1/app/build.gradle @@ -4,15 +4,12 @@ plugins { android { compileSdkVersion 33 + namespace="com.keyman.kmsample1" // Don't compress kmp files so they can be copied via AssetManager aaptOptions { noCompress "kmp" } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } defaultConfig { applicationId "com.keyman.kmsample1" diff --git a/android/Samples/KMSample1/app/src/main/AndroidManifest.xml b/android/Samples/KMSample1/app/src/main/AndroidManifest.xml index 664edec8b576426728efdb435faa09735895be12..3385b035cc7cb99bfc3ffc6e6200d591059e9456 100644 --- a/android/Samples/KMSample1/app/src/main/AndroidManifest.xml +++ b/android/Samples/KMSample1/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + - + - + #include @@ -21,30 +22,31 @@ #define KMX_64BIT #endif -typedef uint32_t KMX_DWORD; -typedef int32_t KMX_BOOL; -typedef uint8_t KMX_BYTE; -typedef uint16_t KMX_WORD; +typedef uint32_t KMX_DWORD; +typedef int32_t KMX_BOOL; +typedef uint8_t KMX_BYTE; +typedef uint16_t KMX_WORD; #if defined(__cplusplus) typedef char16_t km_kbp_cp; typedef char32_t km_kbp_usv; #else -typedef uint16_t km_kbp_cp; // code point -typedef uint32_t km_kbp_usv; // Unicode Scalar Value +typedef uint16_t km_kbp_cp; // code point +typedef uint32_t km_kbp_usv; // Unicode Scalar Value #endif -typedef km_kbp_cp KMX_WCHAR; // wc, 16-bit UNICODE character -typedef KMX_WCHAR *PKMX_WCHAR; +typedef km_kbp_cp KMX_WCHAR; // wc, 16-bit UNICODE character +typedef KMX_WCHAR* PKMX_WCHAR; -typedef char KMX_CHAR; -typedef KMX_CHAR *PKMX_CHAR; +typedef char KMX_CHAR; +typedef char* PKMX_STR; +typedef KMX_CHAR* PKMX_CHAR; -typedef uint32_t KMX_UINT; +typedef uint32_t KMX_UINT; -typedef KMX_BYTE *PKMX_BYTE; -typedef KMX_WORD *PKMX_WORD; -typedef KMX_DWORD *PKMX_DWORD; +typedef KMX_BYTE* PKMX_BYTE; +typedef KMX_WORD* PKMX_WORD; +typedef KMX_DWORD* PKMX_DWORD; #ifndef FALSE #define FALSE 0 diff --git a/common/include/kmx_file.h b/common/include/kmx_file.h index 4519e0ccef3117ad3b0c52e6845f6c7e1f8190aa..998c97800728e600e8a0be0e2a931e9544438118 100644 --- a/common/include/kmx_file.h +++ b/common/include/kmx_file.h @@ -64,6 +64,8 @@ namespace kmx { // Different begin types #define BEGIN_ANSI 0 #define BEGIN_UNICODE 1 +#define BEGIN_NEWCONTEXT 2 +#define BEGIN_POSTKEYSTROKE 3 #define TSS_NONE 0 #define TSS_BITMAP 1 @@ -302,6 +304,7 @@ struct COMP_STORE { struct COMP_KEY { KMX_WORD Key; + KMX_WORD _reserved; KMX_DWORD Line; KMX_DWORD ShiftFlags; KMX_DWORD dpOutput; diff --git a/core/tests/unit/test_assert.h b/common/include/test_assert.h similarity index 100% rename from core/tests/unit/test_assert.h rename to common/include/test_assert.h diff --git a/core/tests/unit/test_color.h b/common/include/test_color.h similarity index 100% rename from core/tests/unit/test_color.h rename to common/include/test_color.h diff --git a/common/linux/build.sh b/common/linux/build.sh index 26c2cccb1812874db178eae2b455d0cd56ea9ed9..852a626bed79d62c59f15aba1dd454c4dd26aa48 100755 --- a/common/linux/build.sh +++ b/common/linux/build.sh @@ -1,17 +1,15 @@ #!/usr/bin/env bash -# -# Compiles and tests the common Linux modules -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE +if [[ $BUILDER_OS != linux ]]; then + builder_echo grey "Platform is not linux; skipping common/linux" + exit 0 +fi + # # TODO: when we have linux-specific tests, add them here # as child modules diff --git a/common/mac/build.sh b/common/mac/build.sh index 84e3b3761d66a82624dab4b757c4638881bf27e7..b1c93d333d704574d4d1a589cd26c90c37818879 100755 --- a/common/mac/build.sh +++ b/common/mac/build.sh @@ -1,17 +1,15 @@ #!/usr/bin/env bash -# -# Compiles and tests the common mac modules -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE +if [[ $BUILDER_OS != mac ]]; then + builder_echo grey "Platform is not macOS; skipping common/mac" + exit 0 +fi + # # TODO: when we have mac-specific tests, add them here # as child modules diff --git a/common/models/types/.build-builder b/common/models/types/.build-builder new file mode 100644 index 0000000000000000000000000000000000000000..6db097fddbbc34e18b27f6a6903f1daff40275e4 --- /dev/null +++ b/common/models/types/.build-builder @@ -0,0 +1 @@ +The presence of this file tells CI to use the new builder_ style parameters for build.sh. \ No newline at end of file diff --git a/common/models/types/build.sh b/common/models/types/build.sh index ff116a58b38620ae16a57f78e23a6acb0e8ca0ab..aa495b8ede970b09212c95e8d3e8ee6a9449e981 100755 --- a/common/models/types/build.sh +++ b/common/models/types/build.sh @@ -10,101 +10,64 @@ set -eu # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" ## END STANDARD BUILD SCRIPT INCLUDE -EX_USAGE=64 - - -display_usage ( ) { - echo "Usage: $0 [-test] [-publish-to-npm]" - echo " $0 -help" - echo - echo " -help displays this screen and exits" - echo " -test runs tests" - echo " -publish-to-npm publishes the current version to the npm package index" - echo " -dry-run do test, etc, but don't actually publish" -} - -################################ Main script ################################ - -run_tests=0 -install_dependencies=1 -should_publish=0 -npm_dist_tag= -should_dry_run=0 - -# Process command-line arguments -while [[ $# -gt 0 ]] ; do - key="$1" - case $key in - -help|-h) - display_usage - exit - ;; - -dry-run) - should_dry_run=1 - ;; - -test) - run_tests=1 - install_dependencies=0 - ;; - -version) - echo "Warning: -version is now ignored" - ;; - -tier) - echo "Warning: -tier is now ignored" - ;; - -publish-to-npm) - should_publish=1 - ;; - *) - echo "$0: invalid option: $key" - display_usage - exit $EX_USAGE - esac - shift # past the processed argument -done - -# Dry run settings -if (( should_dry_run )); then - DRY_RUN=--dry-run -else - DRY_RUN= +cd "$THIS_SCRIPT_PATH" + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + + +builder_describe "Build Keyman model types package" \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "pack build a local .tgz pack for testing" \ + "publish publish to npm" \ + "--dry-run,-n don't actually publish, just dry run" + +builder_describe_outputs \ + configure /node_modules \ + build /common/models/types/build/common/models/types/tsconfig.tsbuildinfo + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action clean; then + rm -rf ./build/ + builder_finish_action success clean fi -# Check that we're doing any task at all! -if (( !run_tests )) && (( !should_publish )); then - echo "$0: Must do at least one of: -test, -publish-npm" >&2 - display_usage - exit $EX_USAGE +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action configure; then + verify_npm_setup + builder_finish_action success configure fi -# Check if Node.JS/npm is installed. -type npm >/dev/null ||\ - builder_die "Build environment setup error detected! Please ensure Node.js is installed!" +#------------------------------------------------------------------------------------------------------------------- -if (( install_dependencies )) ; then - npm install || builder_die "Could not download dependencies." +if builder_start_action build; then + tsc --build + builder_finish_action success build fi -if (( run_tests )); then - npm test || builder_die "Tests failed" +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action test; then + npm test + builder_finish_action success test fi -if (( should_publish )); then - if [[ $TIER == stable ]]; then - npm_dist_tag=latest - else - npm_dist_tag=$TIER - fi - - set_npm_version - - # Note: In either case, npm publish MUST be given --access public to publish - # a package in the @keymanapp scope on the public npm package index. - # - # See `npm help publish` for more details. - echo "Publishing $DRY_RUN npm package with tag $npm_dist_tag" - npm publish $DRY_RUN --access public --tag $npm_dist_tag || builder_die "Could not publish $npm_dist_tag release." +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action publish; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm + builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/common/predictive-text/unit_tests/test.sh b/common/predictive-text/unit_tests/test.sh index a0554b6325a714d31ab908cb6b9d8bd7c0481dba..f1b49635f62081e6b61958dad39cfebed4ed809e 100755 --- a/common/predictive-text/unit_tests/test.sh +++ b/common/predictive-text/unit_tests/test.sh @@ -58,7 +58,7 @@ if builder_start_action test:libraries; then pushd "$KEYMAN_ROOT/common/models/templates" echo - echo "### Running $builder_term common/models/templates) tests" + echo "### Running $(builder_term common/models/templates) tests" if builder_has_option --ci; then npm run test -- -reporter mocha-teamcity-reporter else @@ -68,7 +68,7 @@ if builder_start_action test:libraries; then pushd "$KEYMAN_ROOT/common/models/types" echo - echo "### Running $builder_term common/models/types) tests" + echo "### Running $(builder_term common/models/types) tests" # Is not mocha-based; it's TSC-based instead, as we're just ensuring that the .d.ts # file is a proper TS declaration file. npm run test @@ -100,7 +100,7 @@ if [[ $VERSION_ENVIRONMENT == test ]] && builder_has_action test :browser; then if builder_pull_get_details; then if ! ([[ $builder_pull_title =~ \(web\) ]] || builder_pull_has_label test-browserstack); then - echo "Auto-skipping $builder_term test:browser) for unrelated CI test build" + echo "Auto-skipping $(builder_term test:browser) for unrelated CI test build" exit 0 fi fi @@ -145,4 +145,4 @@ if builder_start_action test:browser; then npm run karma -- start $KARMA_INFO_LEVEL $KARMA_FLAGS $BROWSERS unit_tests/in_browser/$KARMA_CONFIG builder_finish_action success test:browser -fi \ No newline at end of file +fi diff --git a/common/schemas/kps/README.md b/common/schemas/kps/README.md index 4322a28bf67e1b6c146a9b1881b0508487e0cec1..e4aacf97d8f2bc518d747693f015a52cb2a3b8b3 100644 --- a/common/schemas/kps/README.md +++ b/common/schemas/kps/README.md @@ -4,3 +4,6 @@ Master version: https://github.com/keymanapp/api.keyman.com/blob/master/schemas/ ## 2021-07-19 7.0 * Initial version 7.0 + +## 2023-04-21 7.0.1 +* Removes LexicalModel.Version, as it was never read or written diff --git a/common/schemas/kps/kps.xsd b/common/schemas/kps/kps.xsd index adbdc36178e79e76653eee3538d93f9c2b85276a..e6ee424e28d8a9b139143fb947ba57a27a30a2f3 100644 --- a/common/schemas/kps/kps.xsd +++ b/common/schemas/kps/kps.xsd @@ -126,7 +126,6 @@ - diff --git a/common/test/keyboards/.gitignore b/common/test/keyboards/.gitignore index eab8a63166efa340f700a028136e731787b84319..5dda6d16d8af96edd60087dabcc4a8d1140de415 100644 --- a/common/test/keyboards/.gitignore +++ b/common/test/keyboards/.gitignore @@ -1,3 +1,4 @@ -*/build/ +baseline/kmp.json +**/build/ */*_source.zip index.html diff --git a/common/test/keyboards/baseline/README.md b/common/test/keyboards/baseline/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cede107085160bc4cfbf00f9bba32a969c517d8c --- /dev/null +++ b/common/test/keyboards/baseline/README.md @@ -0,0 +1,24 @@ +# Keyboard baseline fixtures + +This folder contains a set of keyboards that are used by multiple projects to +verify behaviour, both for compilation, and for runtime tests. + +The following projects are known to use these keyboards: +* core -- .kmn files only, compiled with kmc during tests +* developer/kmcmplib -- .kmn files, compiled for tests, and .kmx as reference +* linux -- .kmn files for test steps only, .kmx files + +The .kmx files were built with kmcomp, not kmc, in order to ensure that +developer/kmcmplib gets a valid baseline reference. + +Once kmc is stable, it is possible that we will be able to use the kmc npm +module to build .kmx for all projects that need them. + +## Build parameters + +Keyboards were built with debug information and no compiler version embedded, +with kmcomp 16.0.138: + +```bat +for %d in (*.kmn) do kmcomp -no-compiler-version -d %d +``` diff --git a/common/test/keyboards/baseline/baseline.kpj b/common/test/keyboards/baseline/baseline.kpj new file mode 100644 index 0000000000000000000000000000000000000000..b98e7519fda1980e86dbcf6748de7b25dd576ce8 --- /dev/null +++ b/common/test/keyboards/baseline/baseline.kpj @@ -0,0 +1,327 @@ + + + + $PROJECTPATH\build + False + False + True + keyboard + + + + id_k_000___null_keyboard + k_000___null_keyboard.kmn + k_000___null_keyboard.kmn + 1.0 + .kmn + + + id_k_001___basic_input_unicodei + k_001___basic_input_unicodei.kmn + k_001___basic_input_unicodei.kmn + 1.0 + .kmn + + + id_k_002___basic_input_unicode + k_002___basic_input_unicode.kmn + k_002___basic_input_unicode.kmn + 1.0 + .kmn + + + id_k_003___nul + k_003___nul.kmn + k_003___nul.kmn + 1.0 + .kmn + + + id_k_004___basic_input__shift_2_ + k_004___basic_input__shift_2_.kmn + k_004___basic_input__shift_2_.kmn + 1.0 + .kmn + + + id_k_005___nul_with_initial_context + k_005___nul_with_initial_context.kmn + k_005___nul_with_initial_context.kmn + 1.0 + .kmn + + + id_k_006___vkey_input__shift_ctrl_ + k_006___vkey_input__shift_ctrl_.kmn + k_006___vkey_input__shift_ctrl_.kmn + 1.0 + .kmn + + + id_k_007___vkey_input__ctrl_alt_ + k_007___vkey_input__ctrl_alt_.kmn + k_007___vkey_input__ctrl_alt_.kmn + 1.0 + .kmn + + + id_k_008___vkey_input__ctrl_alt_2_ + k_008___vkey_input__ctrl_alt_2_.kmn + k_008___vkey_input__ctrl_alt_2_.kmn + 1.0 + .kmn + + + id_k_012___ralt + k_012___ralt.kmn + k_012___ralt.kmn + 1.0 + .kmn + + + id_k_013___deadkeys + k_013___deadkeys.kmn + k_013___deadkeys.kmn + 1.0 + .kmn + + + id_k_014___groups_and_virtual_keys + k_014___groups_and_virtual_keys.kmn + k_014___groups_and_virtual_keys.kmn + 1.0 + .kmn + + + id_k_015___ralt_2 + k_015___ralt_2.kmn + k_015___ralt_2.kmn + 1.0 + .kmn + + + id_k_017___space_mnemonic_kbd + k_017___space_mnemonic_kbd.kmn + k_017___space_mnemonic_kbd.kmn + 1.0 + .kmn + + + id_k_018___nul_testing + k_018___nul_testing.kmn + k_018___nul_testing.kmn + 1.0 + .kmn + + + id_k_019___multiple_deadkeys + k_019___multiple_deadkeys.kmn + k_019___multiple_deadkeys.kmn + 1.0 + .kmn + + + id_k_020___deadkeys_and_backspace + k_020___deadkeys_and_backspace.kmn + k_020___deadkeys_and_backspace.kmn + 1.0 + .kmn + + + id_k_021___options + k_021___options.kmn + k_021___options.kmn + 1.0 + .kmn + + + id_k_022___options_with_preset + k_022___options_with_preset.kmn + k_022___options_with_preset.kmn + 1.0 + .kmn + + + id_k_023___options_with_save + k_023___options_with_save.kmn + k_023___options_with_save.kmn + 1.0 + .kmn + + + id_k_024___options_with_save_and_preset + k_024___options_with_save_and_preset.kmn + k_024___options_with_save_and_preset.kmn + 1.0 + .kmn + + + id_k_025___options_with_reset + k_025___options_with_reset.kmn + k_025___options_with_reset.kmn + 1.0 + .kmn + + + id_k_026___system_stores + k_026___system_stores.kmn + k_026___system_stores.kmn + 1.0 + .kmn + + + id_k_027___system_stores_2 + k_027___system_stores_2.kmn + k_027___system_stores_2.kmn + 1.0 + .kmn + + + id_k_028___smp + k_028___smp.kmn + k_028___smp.kmn + 1.0 + .kmn + + + id_k_029___beep + k_029___beep.kmn + k_029___beep.kmn + 1.0 + .kmn + + + id_k_030___multiple_groups + k_030___multiple_groups.kmn + k_030___multiple_groups.kmn + 1.0 + .kmn + + + id_k_031___caps_lock + k_031___caps_lock.kmn + k_031___caps_lock.kmn + 1.0 + .kmn + + + id_k_032___caps_control + k_032___caps_control.kmn + k_032___caps_control.kmn + 1.0 + .kmn + + + id_k_033___caps_always_off + k_033___caps_always_off.kmn + k_033___caps_always_off.kmn + 1.0 + .kmn + + + id_k_034___options_double_set_reset + k_034___options_double_set_reset.kmn + k_034___options_double_set_reset.kmn + 1.0 + .kmn + + + id_k_035___options_double_set_staged + k_035___options_double_set_staged.kmn + k_035___options_double_set_staged.kmn + 1.0 + .kmn + + + id_k_036___options___double_reset_staged + k_036___options___double_reset_staged.kmn + k_036___options___double_reset_staged.kmn + 1.0 + .kmn + + + id_k_037___options___double_reset + k_037___options___double_reset.kmn + k_037___options___double_reset.kmn + 1.0 + .kmn + + + id_k_038___punctkeys + k_038___punctkeys.kmn + k_038___punctkeys.kmn + 1.0 + .kmn + + + id_k_039___generic_ctrlalt + k_039___generic_ctrlalt.kmn + k_039___generic_ctrlalt.kmn + 1.0 + .kmn + + + id_k_040___long_context + k_040___long_context.kmn + k_040___long_context.kmn + 1.0 + .kmn + + + id_k_041___long_context_and_deadkeys + k_041___long_context_and_deadkeys.kmn + k_041___long_context_and_deadkeys.kmn + 1.0 + .kmn + + + id_k_042___long_context_and_split_deadkeys + k_042___long_context_and_split_deadkeys.kmn + k_042___long_context_and_split_deadkeys.kmn + 1.0 + .kmn + + + id_k_043___output_and_keystroke + k_043___output_and_keystroke.kmn + k_043___output_and_keystroke.kmn + 1.0 + .kmn + + + id_k_044___if_and_context + k_044___if_and_context.kmn + k_044___if_and_context.kmn + 1.0 + .kmn + + + id_k_045___deadkey_and_context + k_045___deadkey_and_context.kmn + k_045___deadkey_and_context.kmn + 1.0 + .kmn + + + id_k_046___deadkey_and_contextex + k_046___deadkey_and_contextex.kmn + k_046___deadkey_and_contextex.kmn + 1.0 + .kmn + + + id_k_047___caps_always_off_initially_on + k_047___caps_always_off_initially_on.kmn + k_047___caps_always_off_initially_on.kmn + 1.0 + .kmn + + + id_k_048___modifier_keys_keep_context + k_048___modifier_keys_keep_context.kmn + k_048___modifier_keys_keep_context.kmn + 1.0 + .kmn + + + diff --git a/core/tests/unit/kmx/000 - null keyboard.kmn b/common/test/keyboards/baseline/k_000___null_keyboard.kmn similarity index 83% rename from core/tests/unit/kmx/000 - null keyboard.kmn rename to common/test/keyboards/baseline/k_000___null_keyboard.kmn index 75ad394bbb5944aa553ba998d2bda81d9a7617ea..b1304c20c8c89d7423199e27582b212fccdeb51d 100644 --- a/core/tests/unit/kmx/000 - null keyboard.kmn +++ b/common/test/keyboards/baseline/k_000___null_keyboard.kmn @@ -1,3 +1,4 @@ +store(&NAME) '000 - null keyboard' c Description: Tests null keyboard c keys: [K_A][RALT K_B][SHIFT K_C] c expected: aC @@ -8,4 +9,3 @@ store(&version) '6.0' begin Unicode > use(Main) group(Main) using keys - diff --git a/common/test/keyboards/baseline/k_000___null_keyboard.kmx b/common/test/keyboards/baseline/k_000___null_keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2a49864d0044876a4e4de92c762e54c6791ce423 Binary files /dev/null and b/common/test/keyboards/baseline/k_000___null_keyboard.kmx differ diff --git a/core/tests/unit/kmx/001 - basic input UnicodeI.kmn b/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn similarity index 88% rename from core/tests/unit/kmx/001 - basic input UnicodeI.kmn rename to common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn index 79eb4dee700a7fca7a6f6bdaca228e388c240ee6..446c0e8f1d98e724009ec61447aac2614aceabff 100644 --- a/core/tests/unit/kmx/001 - basic input UnicodeI.kmn +++ b/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn @@ -1,3 +1,4 @@ +store(&NAME) '001 - basic input UnicodeI' c Description: Tests basic input with simple context rules, ANSI characters used only c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F] c expected: abc def diff --git a/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx b/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx new file mode 100644 index 0000000000000000000000000000000000000000..82f60cd001d0ba9bf984253ea81e87a41d1a3df4 Binary files /dev/null and b/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx differ diff --git a/core/tests/unit/kmx/002 - basic input Unicode.kmn b/common/test/keyboards/baseline/k_002___basic_input_unicode.kmn similarity index 87% rename from core/tests/unit/kmx/002 - basic input Unicode.kmn rename to common/test/keyboards/baseline/k_002___basic_input_unicode.kmn index bcbcd718d8af640f5bd7c2971f09b31d09153e50..04c6df9f4a647292d322cbb60540cf5684c71aa5 100644 --- a/core/tests/unit/kmx/002 - basic input Unicode.kmn +++ b/common/test/keyboards/baseline/k_002___basic_input_unicode.kmn @@ -1,7 +1,8 @@ +store(&NAME) '002 - basic input Unicode' c Description: Tests basic input with simple context rules (Unicode) c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F] c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06 -c context: +c context: store(&version) '6.0' diff --git a/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx b/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5f1497a29e0dd26ad493c5cf04d65c69f09cd7e9 Binary files /dev/null and b/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx differ diff --git a/core/tests/unit/kmx/003 - nul.kmn b/common/test/keyboards/baseline/k_003___nul.kmn similarity index 89% rename from core/tests/unit/kmx/003 - nul.kmn rename to common/test/keyboards/baseline/k_003___nul.kmn index 935d34402fbe3331845ff302795c063f031c3be0..32f6e84839ab4dbd4734aec729850010bc15505e 100644 --- a/core/tests/unit/kmx/003 - nul.kmn +++ b/common/test/keyboards/baseline/k_003___nul.kmn @@ -1,3 +1,4 @@ +store(&NAME) '003 - nul' c Description: Tests nul context c keys: [K_A][K_A][K_A] c expected: cd diff --git a/common/test/keyboards/baseline/k_003___nul.kmx b/common/test/keyboards/baseline/k_003___nul.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9a5e835e04af55f0c3f11b84877e9e2d3df8e107 Binary files /dev/null and b/common/test/keyboards/baseline/k_003___nul.kmx differ diff --git a/core/tests/unit/kmx/004 - basic input (shift 2).kmn b/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn similarity index 90% rename from core/tests/unit/kmx/004 - basic input (shift 2).kmn rename to common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn index be91d698c5fcef3f455b2699ee14b6c431e4d290..d87821442735f25e869300d66ba34e5b10a88929 100644 --- a/core/tests/unit/kmx/004 - basic input (shift 2).kmn +++ b/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn @@ -1,3 +1,4 @@ +store(&NAME) '004 - basic input (shift 2)' c Description: Tests basic vkey input with shift only (Unicode) c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F] c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06 diff --git a/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx b/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c465d596d5fc7f7f50881ad8b4377414a05458cf Binary files /dev/null and b/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx differ diff --git a/core/tests/unit/kmx/005 - nul with initial context.kmn b/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn similarity index 83% rename from core/tests/unit/kmx/005 - nul with initial context.kmn rename to common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn index d838c28c6dce18abab9c7d2aba2748944972ef3f..f59016cbe184d0dba78893794b8ca3ce663cfcf6 100644 --- a/core/tests/unit/kmx/005 - nul with initial context.kmn +++ b/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn @@ -1,3 +1,4 @@ +store(&NAME) '005 - nul with initial context' c Description: Tests nul context with an initial context supplied c keys: [K_A][K_A] c expected: xdd diff --git a/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx b/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx new file mode 100644 index 0000000000000000000000000000000000000000..53f9889b32f3e3fa150a9bbfb94fe118d5f532c2 Binary files /dev/null and b/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx differ diff --git a/core/tests/unit/kmx/006 - vkey input (shift ctrl).kmn b/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn similarity index 90% rename from core/tests/unit/kmx/006 - vkey input (shift ctrl).kmn rename to common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn index 452e071ecb1211c6523ff4203dbd3bb3b0f7e1f4..9a966aa651db7c226640e720bb77b8b2693c7879 100644 --- a/core/tests/unit/kmx/006 - vkey input (shift ctrl).kmn +++ b/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn @@ -1,3 +1,4 @@ +store(&NAME) '006 - vkey input (shift ctrl)' c Description: Tests basic vkey input with shift and control (Unicode) c keys: [LCTRL SHIFT K_A][SHIFT K_B][RCTRL SHIFT K_C][K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F] c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06 diff --git a/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx b/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6651a91b1962a3a7737a59686c1eea304ba0abf1 Binary files /dev/null and b/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx differ diff --git a/core/tests/unit/kmx/007 - vkey input (ctrl alt).kmn b/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn similarity index 90% rename from core/tests/unit/kmx/007 - vkey input (ctrl alt).kmn rename to common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn index bae07ea09152d61e00dbecc0273315ed53981947..3c2eb42bfe0092852ee150b12a9f86c34b859c14 100644 --- a/core/tests/unit/kmx/007 - vkey input (ctrl alt).kmn +++ b/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn @@ -1,3 +1,4 @@ +store(&NAME) '007 - vkey input (ctrl alt)' c Description: Tests basic vkey input with control and alt (Unicode) c keys: [LCTRL LALT K_A][K_B][RCTRL RALT K_C][K_SPACE][K_D][K_E][LCTRL LALT K_F] c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06 diff --git a/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx b/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3000af8a8025e65bbe785fd9b97119b138509c13 Binary files /dev/null and b/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx differ diff --git a/core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmn b/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn similarity index 86% rename from core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmn rename to common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn index 43b81eab010053fbc3d60981ee2047a1d7d82cb0..ac267fa15b59080de405dbf80bd137645473ace5 100644 --- a/core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmn +++ b/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn @@ -1,3 +1,4 @@ +store(&NAME) '008 - vkey input (ctrl alt 2)' c Description: Tests basic vkey input with control and alt (Unicode) c keys: [LCTRL LALT K_A][LCTRL LALT K_B][LCTRL LALT K_C] c expected: \u0E01\u0E03 diff --git a/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx b/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0f7e29779eda3a9ac306e7fb2e3a750482163bbe Binary files /dev/null and b/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx differ diff --git a/core/tests/unit/kmx/012 - ralt.kmn b/common/test/keyboards/baseline/k_012___ralt.kmn similarity index 87% rename from core/tests/unit/kmx/012 - ralt.kmn rename to common/test/keyboards/baseline/k_012___ralt.kmn index a4ed290ec84fd0d104d3c4f3bb7a6ffaa5722648..8e041ad99e004d959ad0a02607476b635eb0c200 100644 --- a/core/tests/unit/kmx/012 - ralt.kmn +++ b/common/test/keyboards/baseline/k_012___ralt.kmn @@ -1,3 +1,4 @@ +store(&NAME) '012 - ralt' c Description: Tests Right Alt processing: requires Ctrl+Alt simulation off. c env.simulate_altgr: 0 c keys: [RALT K_A][RALT K_O][LCTRL LALT K_A][LCTRL LALT K_O] @@ -13,4 +14,4 @@ group(main) using keys + [RAlt K_A] > U+05D6 + [RAlt K_O] > U+05E8 + [Ctrl Alt K_A] > "b" -+ [Ctrl Alt K_O] > "c" ++ [Ctrl Alt K_O] > "c" diff --git a/common/test/keyboards/baseline/k_012___ralt.kmx b/common/test/keyboards/baseline/k_012___ralt.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3ac57c165b517b5a530db05d20d0d257e07f8d9a Binary files /dev/null and b/common/test/keyboards/baseline/k_012___ralt.kmx differ diff --git a/core/tests/unit/kmx/013 - deadkeys.kmn b/common/test/keyboards/baseline/k_013___deadkeys.kmn similarity index 88% rename from core/tests/unit/kmx/013 - deadkeys.kmn rename to common/test/keyboards/baseline/k_013___deadkeys.kmn index e462491840375dd98be7c0d0f920f3a1af26a802..d0f4876455eeb00968bc562f575dccefb238444b 100644 --- a/core/tests/unit/kmx/013 - deadkeys.kmn +++ b/common/test/keyboards/baseline/k_013___deadkeys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '013 - deadkeys' c Description: Tests a simple deadkey rule in Unicode c keys: [SHIFT K_6][K_A] c expected: \u00E2 diff --git a/common/test/keyboards/baseline/k_013___deadkeys.kmx b/common/test/keyboards/baseline/k_013___deadkeys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7f93d7d9ae9f2ed3743e3ca997647b328c37481f Binary files /dev/null and b/common/test/keyboards/baseline/k_013___deadkeys.kmx differ diff --git a/core/tests/unit/kmx/014 - groups and virtual keys.kmn b/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn similarity index 93% rename from core/tests/unit/kmx/014 - groups and virtual keys.kmn rename to common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn index 20db871043ecb90c85481bef9da7b97d163104cf..81e26763b6defd39f60cec01b39d1db9c0042d34 100644 --- a/core/tests/unit/kmx/014 - groups and virtual keys.kmn +++ b/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '014 - groups and virtual keys' c Description: Tests multiple groups and virtual keys with mnemonic layout c keys: [K_A][SHIFT K_2][K_A][LCTRL K_2][K_A] c expected: \u03B1\u1FB0\u1FB1 diff --git a/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx b/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..03bb1508a55ebf46c59d21379908465c37eba734 Binary files /dev/null and b/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx differ diff --git a/core/tests/unit/kmx/015 - ralt 2.kmn b/common/test/keyboards/baseline/k_015___ralt_2.kmn similarity index 88% rename from core/tests/unit/kmx/015 - ralt 2.kmn rename to common/test/keyboards/baseline/k_015___ralt_2.kmn index ee87dd6f6b9758ce6c4188b37840025c8de99093..631bea8d8d8e19b6cdad7289f84da320405bbd51 100644 --- a/core/tests/unit/kmx/015 - ralt 2.kmn +++ b/common/test/keyboards/baseline/k_015___ralt_2.kmn @@ -1,3 +1,4 @@ +store(&NAME) '015 - ralt 2' c Description: Tests Right Alt processing with non-US kbds. c keys: [K_A][K_B][K_C][RALT K_A] c expected: abcd @@ -9,4 +10,3 @@ begin Unicode > use(main) group(main) using keys 'c' + [RAlt K_A] > "cd" - diff --git a/common/test/keyboards/baseline/k_015___ralt_2.kmx b/common/test/keyboards/baseline/k_015___ralt_2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0a5e1bbf434b09618983f22e84b6f9fdc7c89d0f Binary files /dev/null and b/common/test/keyboards/baseline/k_015___ralt_2.kmx differ diff --git a/core/tests/unit/kmx/017 - space mnemonic kbd.kmn b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn similarity index 91% rename from core/tests/unit/kmx/017 - space mnemonic kbd.kmn rename to common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn index 33b21536ed2aa1f4d69ed28491d2aa2acecf111b..a66fac420a9c6c27ca5c64ec2538343e97e75597 100644 --- a/core/tests/unit/kmx/017 - space mnemonic kbd.kmn +++ b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn @@ -1,3 +1,4 @@ +store(&NAME) '017 - space mnemonic kbd' c Description: Tests Space handling in mnemonic keyboards (failed with Win 98) c keys: [K_A][K_B][K_SPACE][K_C][K_SPACE][K_D][K_SPACE][K_D][K_E] c expected: XYZ diff --git a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0983bd248870a3724caea783d60d58875fc76173 Binary files /dev/null and b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx differ diff --git a/core/tests/unit/kmx/018 - nul testing.kmn b/common/test/keyboards/baseline/k_018___nul_testing.kmn similarity index 88% rename from core/tests/unit/kmx/018 - nul testing.kmn rename to common/test/keyboards/baseline/k_018___nul_testing.kmn index 3c0cb24f2ae94816701f2bd4c293125418e76472..bc6e41206d521ec792c453ed797a8b8893ee75ce 100644 --- a/core/tests/unit/kmx/018 - nul testing.kmn +++ b/common/test/keyboards/baseline/k_018___nul_testing.kmn @@ -1,3 +1,4 @@ +store(&NAME) '018 - nul testing' c Description: Tests the processing of nul in LHS of rules c keys: [K_A][K_A] c expected: OKa @@ -12,4 +13,3 @@ group(Main) using keys nul + 'a' > 'OK' c EOF - diff --git a/common/test/keyboards/baseline/k_018___nul_testing.kmx b/common/test/keyboards/baseline/k_018___nul_testing.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bf15f3bc139270f242b4fbae8bbbd9b450bd32e6 Binary files /dev/null and b/common/test/keyboards/baseline/k_018___nul_testing.kmx differ diff --git a/core/tests/unit/kmx/019 - multiple deadkeys.kmn b/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn similarity index 94% rename from core/tests/unit/kmx/019 - multiple deadkeys.kmn rename to common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn index c6b19700a035acaa03c2471dd5a83b6bc95bab5f..e70b30f08c5236a146d85e2bed3f92e52f949754 100644 --- a/core/tests/unit/kmx/019 - multiple deadkeys.kmn +++ b/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '019 - multiple deadkeys' c Description: Tests deadkey scenarios c 1. One deadkey in context dk(1) + '1' c 2. One char and one deadkey in context 'a' dk(2) + '2' @@ -55,4 +56,4 @@ c [K_9] [K_X] [K_X] -> test that + '9' > '{' dk(9a) dk(9b) '}' dk(9c) dk(9d) dk(9b) '}' dk(9c) dk(9d) + 'x' > dk(9e) '}' dk(9f) dk(9g) -'{' dk(9a) dk(9e) '}' dk(9f) dk(9g) + 'x' > '9=OK' \ No newline at end of file +'{' dk(9a) dk(9e) '}' dk(9f) dk(9g) + 'x' > '9=OK' diff --git a/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx b/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fe22a2819a535a56b6c8a43dd1239726e9575383 Binary files /dev/null and b/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx differ diff --git a/core/tests/unit/kmx/020 - deadkeys and backspace.kmn b/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn similarity index 95% rename from core/tests/unit/kmx/020 - deadkeys and backspace.kmn rename to common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn index 0a3f97f4616694cbbefcb34eb2ecc7aad2f91218..cf83220f9c322ef546afc1a17e47694777903b8b 100644 --- a/core/tests/unit/kmx/020 - deadkeys and backspace.kmn +++ b/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn @@ -1,3 +1,4 @@ +store(&NAME) '020 - deadkeys and backspace' c Description: Tests deadkey backspacing c 1. One deadkey in context dk(1) + BKSP = nul c 2. One char and one deadkey in context 'a' dk(2) + BKSP = nul diff --git a/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx b/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx new file mode 100644 index 0000000000000000000000000000000000000000..149c2a4f97bb2bd48506ea090f64ee8594015a9e Binary files /dev/null and b/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx differ diff --git a/core/tests/unit/kmx/021 - options.kmn b/common/test/keyboards/baseline/k_021___options.kmn similarity index 91% rename from core/tests/unit/kmx/021 - options.kmn rename to common/test/keyboards/baseline/k_021___options.kmn index a6dbb7c1dd0e5d9a6bf1c9edc8feaf098ed3629f..3851896b84e49ca02cbc77c9e6b6c1f3e395bdb4 100644 --- a/core/tests/unit/kmx/021 - options.kmn +++ b/common/test/keyboards/baseline/k_021___options.kmn @@ -1,3 +1,4 @@ +store(&NAME) '021 - options' c Description: Tests basic option rules c keys: [K_A][K_1][K_A][K_0][K_A] c expected: no foo.foo.no foo. diff --git a/common/test/keyboards/baseline/k_021___options.kmx b/common/test/keyboards/baseline/k_021___options.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f35da34878adc50c677ebe1bc952d99bc916fc91 Binary files /dev/null and b/common/test/keyboards/baseline/k_021___options.kmx differ diff --git a/core/tests/unit/kmx/022 - options with preset.kmn b/common/test/keyboards/baseline/k_022___options_with_preset.kmn similarity index 91% rename from core/tests/unit/kmx/022 - options with preset.kmn rename to common/test/keyboards/baseline/k_022___options_with_preset.kmn index 878acc6fc0eda27f7d3d28933caff53c6c9c95ab..dbaad6e63c811f97763d6b4751174b65642430f8 100644 --- a/core/tests/unit/kmx/022 - options with preset.kmn +++ b/common/test/keyboards/baseline/k_022___options_with_preset.kmn @@ -1,3 +1,4 @@ +store(&NAME) '022 - options with preset' c Description: Tests basic option rules with a preset supplied c keys: [K_A][K_1][K_A][K_0][K_A] c expected: foo.foo.no foo. diff --git a/common/test/keyboards/baseline/k_022___options_with_preset.kmx b/common/test/keyboards/baseline/k_022___options_with_preset.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e681df83394f04ad7be451a4d82051d74924c8d4 Binary files /dev/null and b/common/test/keyboards/baseline/k_022___options_with_preset.kmx differ diff --git a/core/tests/unit/kmx/023 - options with save.kmn b/common/test/keyboards/baseline/k_023___options_with_save.kmn similarity index 86% rename from core/tests/unit/kmx/023 - options with save.kmn rename to common/test/keyboards/baseline/k_023___options_with_save.kmn index aa6c1389712075a259e0d389407485b5e87ebfe4..913bea076f345daf3cea3f9822953ed2978cf9e3 100644 --- a/core/tests/unit/kmx/023 - options with save.kmn +++ b/common/test/keyboards/baseline/k_023___options_with_save.kmn @@ -1,3 +1,4 @@ +store(&NAME) '023 - options with save' c Description: Tests basic option rules with save c keys: [K_A][K_1][K_A][K_0][K_A][K_2] c expected: no foo.foo.no foo. @@ -17,4 +18,4 @@ if(foo = '1') + 'a' > 'foo.' if(foo = '0') + 'a' > 'no foo.' + '1' > set(foo = '1') + '0' > set(foo = '0') -+ '2' > save(foo) \ No newline at end of file ++ '2' > save(foo) diff --git a/common/test/keyboards/baseline/k_023___options_with_save.kmx b/common/test/keyboards/baseline/k_023___options_with_save.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3668b7a066ef37d8ecd0334c9e3fdbd2eca1f944 Binary files /dev/null and b/common/test/keyboards/baseline/k_023___options_with_save.kmx differ diff --git a/core/tests/unit/kmx/024 - options with save and preset.kmn b/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn similarity index 82% rename from core/tests/unit/kmx/024 - options with save and preset.kmn rename to common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn index e8ecd57944961be192d17607f72912cfcb53df86..0c3004c3e32a52d477b38fdced5812fa8f2abc86 100644 --- a/core/tests/unit/kmx/024 - options with save and preset.kmn +++ b/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn @@ -1,3 +1,4 @@ +store(&NAME) '024 - options with save and preset' c Description: Tests basic option rules with save c keys: [K_A][K_1][K_A][K_0][K_A][K_2] c expected: foo.foo.no foo. @@ -18,4 +19,4 @@ if(foo = '1') + 'a' > 'foo.' if(foo = '0') + 'a' > 'no foo.' + '1' > set(foo = '1') + '0' > set(foo = '0') -if(foo = '0') + '2' > save(foo) +if(foo = '0') + '2' > save(foo) diff --git a/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx b/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1fecdfac2131f0ebb90d36134ae99c5f0d5e1342 Binary files /dev/null and b/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx differ diff --git a/core/tests/unit/kmx/025 - options with reset.kmn b/common/test/keyboards/baseline/k_025___options_with_reset.kmn similarity index 90% rename from core/tests/unit/kmx/025 - options with reset.kmn rename to common/test/keyboards/baseline/k_025___options_with_reset.kmn index 2fea1b0ac4b66f506e6f552e03cbabb6eb13323a..13a0d69283759787c8593009b6c419c6e354c5d8 100644 --- a/core/tests/unit/kmx/025 - options with reset.kmn +++ b/common/test/keyboards/baseline/k_025___options_with_reset.kmn @@ -1,3 +1,4 @@ +store(&NAME) '025 - options with reset' c Description: Tests basic option rules with reset c keys: [K_A][K_1][K_A][K_0][K_A][K_3] c expected: foo.foo.no foo. diff --git a/common/test/keyboards/baseline/k_025___options_with_reset.kmx b/common/test/keyboards/baseline/k_025___options_with_reset.kmx new file mode 100644 index 0000000000000000000000000000000000000000..028f81cb5feb143b688f8ea96ef43bcce6d86368 Binary files /dev/null and b/common/test/keyboards/baseline/k_025___options_with_reset.kmx differ diff --git a/core/tests/unit/kmx/026 - system stores.kmn b/common/test/keyboards/baseline/k_026___system_stores.kmn similarity index 95% rename from core/tests/unit/kmx/026 - system stores.kmn rename to common/test/keyboards/baseline/k_026___system_stores.kmn index a9f537e9320b3e67b4917a5e882a948b9cb03ce8..0538c51364bfb902d83590bbca6a070553c00f9e 100644 --- a/core/tests/unit/kmx/026 - system stores.kmn +++ b/common/test/keyboards/baseline/k_026___system_stores.kmn @@ -1,3 +1,4 @@ +store(&NAME) '026 - system stores' c Description: Tests platform and baselayout system store rules c keys: [K_1][K_2][K_3][K_4][K_A][K_B] c expected: windows.desktop.hardware.native.en-US.kbdus.dll. diff --git a/common/test/keyboards/baseline/k_026___system_stores.kmx b/common/test/keyboards/baseline/k_026___system_stores.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bb918cf1429b4e57b68ea4d8bd08e226d558426d Binary files /dev/null and b/common/test/keyboards/baseline/k_026___system_stores.kmx differ diff --git a/core/tests/unit/kmx/027 - system stores 2.kmn b/common/test/keyboards/baseline/k_027___system_stores_2.kmn similarity index 95% rename from core/tests/unit/kmx/027 - system stores 2.kmn rename to common/test/keyboards/baseline/k_027___system_stores_2.kmn index 8d96e999c4132921330f974da997732d4c84e9c9..95457c84639c049ee89335138799e6964b32c02a 100644 --- a/core/tests/unit/kmx/027 - system stores 2.kmn +++ b/common/test/keyboards/baseline/k_027___system_stores_2.kmn @@ -1,3 +1,4 @@ +store(&NAME) '027 - system stores 2' c Description: Tests platform and baselayout system store rules c keys: [K_1][K_2][K_3][K_4][K_A][K_B] c expected: linux.desktop.hardware.native.fr-FR.kbdfr.dll. diff --git a/common/test/keyboards/baseline/k_027___system_stores_2.kmx b/common/test/keyboards/baseline/k_027___system_stores_2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dac90ab985bd96e1b54a14967406fb4236e80d09 Binary files /dev/null and b/common/test/keyboards/baseline/k_027___system_stores_2.kmx differ diff --git a/core/tests/unit/kmx/028 - smp.kmn b/common/test/keyboards/baseline/k_028___smp.kmn similarity index 94% rename from core/tests/unit/kmx/028 - smp.kmn rename to common/test/keyboards/baseline/k_028___smp.kmn index 5bfbbe4bd267a5c556226fa7380bd484f7fdbbcb..7c1eaaba960bc3ec59220b5784ebfafe633bb084 100644 --- a/core/tests/unit/kmx/028 - smp.kmn +++ b/common/test/keyboards/baseline/k_028___smp.kmn @@ -1,3 +1,4 @@ +store(&NAME) '028 - smp' c Description: Tests SMP characters; see #4361 [1-4], #5561 [5] c keys: [K_1][K_2][K_3][K_4][K_5] c expected: \U0001F601yx diff --git a/common/test/keyboards/baseline/k_028___smp.kmx b/common/test/keyboards/baseline/k_028___smp.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f343f969e9bde1431c12375cf7a6c7c68dad0b26 Binary files /dev/null and b/common/test/keyboards/baseline/k_028___smp.kmx differ diff --git a/core/tests/unit/kmx/029 - beep.kmn b/common/test/keyboards/baseline/k_029___beep.kmn similarity index 85% rename from core/tests/unit/kmx/029 - beep.kmn rename to common/test/keyboards/baseline/k_029___beep.kmn index 76e51e620608bc1b4a336586c5518d30f27e5ded..65e650ee44bd82b2b9cf82172ed49943af0149fb 100644 --- a/core/tests/unit/kmx/029 - beep.kmn +++ b/common/test/keyboards/baseline/k_029___beep.kmn @@ -1,3 +1,4 @@ +store(&NAME) '029 - beep' c Description: Tests beep c keys: [K_1] c expected: \b diff --git a/common/test/keyboards/baseline/k_029___beep.kmx b/common/test/keyboards/baseline/k_029___beep.kmx new file mode 100644 index 0000000000000000000000000000000000000000..abf8b899836172c2802a5c5f3972aa85ee03ab52 Binary files /dev/null and b/common/test/keyboards/baseline/k_029___beep.kmx differ diff --git a/core/tests/unit/kmx/030 - multiple groups.kmn b/common/test/keyboards/baseline/k_030___multiple_groups.kmn similarity index 87% rename from core/tests/unit/kmx/030 - multiple groups.kmn rename to common/test/keyboards/baseline/k_030___multiple_groups.kmn index e29b7bd293efdd34bebb1bc805f6f67900760af9..b9148827bc8ebc3c01d3a7a58d7372886814a38c 100644 --- a/core/tests/unit/kmx/030 - multiple groups.kmn +++ b/common/test/keyboards/baseline/k_030___multiple_groups.kmn @@ -1,7 +1,8 @@ +store(&NAME) "030 - multiple groups" c Description: Tests multiple groups. Purpose here is to interleave output and backspaces from kmx processor to consumer c keys: [K_1][K_2] c expected: abc -c context: +c context: store(&version) '10.0' diff --git a/common/test/keyboards/baseline/k_030___multiple_groups.kmx b/common/test/keyboards/baseline/k_030___multiple_groups.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e27ee4dd08c08fc1f45267c54dbfb6dd1bfe5109 Binary files /dev/null and b/common/test/keyboards/baseline/k_030___multiple_groups.kmx differ diff --git a/core/tests/unit/kmx/031 - caps lock.kmn b/common/test/keyboards/baseline/k_031___caps_lock.kmn similarity index 93% rename from core/tests/unit/kmx/031 - caps lock.kmn rename to common/test/keyboards/baseline/k_031___caps_lock.kmn index 8a3f3468b1127e75be99a73c7e9efea22f6d089c..d14b8ad56ba95366082a4b60e51d90b080845e0b 100644 --- a/core/tests/unit/kmx/031 - caps lock.kmn +++ b/common/test/keyboards/baseline/k_031___caps_lock.kmn @@ -1,3 +1,4 @@ +store(&NAME) '031 - caps lock' c Description: Tests Caps Lock c keys: [K_1][SHIFT K_2][SHIFT K_3][K_A][SHIFT K_B] c expected: pass.pass.pass.pass.pass. diff --git a/common/test/keyboards/baseline/k_031___caps_lock.kmx b/common/test/keyboards/baseline/k_031___caps_lock.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5fe3e4fbd74b8e928a0acf7e052369c0de919bae Binary files /dev/null and b/common/test/keyboards/baseline/k_031___caps_lock.kmx differ diff --git a/core/tests/unit/kmx/032 - caps control.kmn b/common/test/keyboards/baseline/k_032___caps_control.kmn similarity index 96% rename from core/tests/unit/kmx/032 - caps control.kmn rename to common/test/keyboards/baseline/k_032___caps_control.kmn index 203026da36181afca8155d38381d9ddd91e4968a..884fb41a6d5b53f86295a628e7131d265745955e 100644 --- a/core/tests/unit/kmx/032 - caps control.kmn +++ b/common/test/keyboards/baseline/k_032___caps_control.kmn @@ -1,3 +1,4 @@ +store(&NAME) '032 - caps control' c Description: Tests Caps Lock env set c keys: [K_1][K_CAPS][K_2][SHIFT K_3][K_4][K_CAPS][K_5][K_CAPS][K_6][K_SHIFT][K_7] c expected: pass.pass.pass.pass.pass.pass.pass. diff --git a/common/test/keyboards/baseline/k_032___caps_control.kmx b/common/test/keyboards/baseline/k_032___caps_control.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1914d1954bb7186f7edb96a9a28e64df0ce182d6 Binary files /dev/null and b/common/test/keyboards/baseline/k_032___caps_control.kmx differ diff --git a/core/tests/unit/kmx/033 - caps always off.kmn b/common/test/keyboards/baseline/k_033___caps_always_off.kmn similarity index 91% rename from core/tests/unit/kmx/033 - caps always off.kmn rename to common/test/keyboards/baseline/k_033___caps_always_off.kmn index 056fcd895104635eb5b6c8e8d422dbf0fc684327..912f2c5ac0d6f3ed1d2dc5d76b0a78bef2630ba4 100644 --- a/core/tests/unit/kmx/033 - caps always off.kmn +++ b/common/test/keyboards/baseline/k_033___caps_always_off.kmn @@ -1,3 +1,4 @@ +store(&NAME) '033 - caps always off' c Description: Tests Caps Lock always off c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3] c expected: pass.pass.pass. diff --git a/common/test/keyboards/baseline/k_033___caps_always_off.kmx b/common/test/keyboards/baseline/k_033___caps_always_off.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cb9a71a0460da245d17985bf76235ebdf6c93080 Binary files /dev/null and b/common/test/keyboards/baseline/k_033___caps_always_off.kmx differ diff --git a/core/tests/unit/kmx/034 - options double set reset.kmn b/common/test/keyboards/baseline/k_034___options_double_set_reset.kmn similarity index 90% rename from core/tests/unit/kmx/034 - options double set reset.kmn rename to common/test/keyboards/baseline/k_034___options_double_set_reset.kmn index 2e8e754b95a3d85fa91f688f505beca70df73836..3fe9225ecadb1d3f3f3c6320f194c4731bce003d 100644 --- a/core/tests/unit/kmx/034 - options double set reset.kmn +++ b/common/test/keyboards/baseline/k_034___options_double_set_reset.kmn @@ -1,3 +1,4 @@ +store(&NAME) '034 - options double set reset' c Description: Tests basic option rules with save reset+set+reset c keys: [K_A][K_A] c expected: foo.foo. diff --git a/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx b/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx new file mode 100644 index 0000000000000000000000000000000000000000..df8eb8062a535df0881c3e960f8b5232fdf6bc94 Binary files /dev/null and b/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx differ diff --git a/core/tests/unit/kmx/035 - options double set staged.kmn b/common/test/keyboards/baseline/k_035___options_double_set_staged.kmn similarity index 90% rename from core/tests/unit/kmx/035 - options double set staged.kmn rename to common/test/keyboards/baseline/k_035___options_double_set_staged.kmn index e92e9bacac766a44fc902911ad276bf5e3ad1de8..75d1b81b89b7c09d42b29b0a488260b45e498c10 100644 --- a/core/tests/unit/kmx/035 - options double set staged.kmn +++ b/common/test/keyboards/baseline/k_035___options_double_set_staged.kmn @@ -1,3 +1,4 @@ +store(&NAME) '035 - options double set staged' c Description: Tests basic option rules with save reset+set+reset c keys: [K_A][K_B][K_C][K_B][K_A] c expected: foo.foo. diff --git a/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx b/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e112a0740f57c46c61d5d72a322bfb0922edaf06 Binary files /dev/null and b/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx differ diff --git a/core/tests/unit/kmx/036 - options - double reset staged.kmn b/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn similarity index 88% rename from core/tests/unit/kmx/036 - options - double reset staged.kmn rename to common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn index 5a9144f3f7d444bd48c727f3c06c33966995ae54..53157b487d3004ebda8d63eac18dda74b76d0c5c 100644 --- a/core/tests/unit/kmx/036 - options - double reset staged.kmn +++ b/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn @@ -1,3 +1,4 @@ +store(&NAME) '036 - options - double reset staged' c Description: Tests basic option rules with save reset+reset c keys: [K_A][K_B][K_B][K_A] c expected: foo.foo. diff --git a/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx b/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ccb05e04c1faa64a540ea3bf3307c988ddfd2e50 Binary files /dev/null and b/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx differ diff --git a/core/tests/unit/kmx/037 - options - double reset.kmn b/common/test/keyboards/baseline/k_037___options___double_reset.kmn similarity index 89% rename from core/tests/unit/kmx/037 - options - double reset.kmn rename to common/test/keyboards/baseline/k_037___options___double_reset.kmn index 5d2eb1d15ed14b7789122e933fd60e9ad1bd302a..05e5fe7b024d3b118e922f812ee8f37b379c39c1 100644 --- a/core/tests/unit/kmx/037 - options - double reset.kmn +++ b/common/test/keyboards/baseline/k_037___options___double_reset.kmn @@ -1,3 +1,4 @@ +store(&NAME) '037 - options - double reset' c Description: Tests basic option rules with save reset+reset c keys: [K_A][K_A] c expected: foo.foo. diff --git a/common/test/keyboards/baseline/k_037___options___double_reset.kmx b/common/test/keyboards/baseline/k_037___options___double_reset.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eed688349c9217c488bedcd573382d937efc8fe4 Binary files /dev/null and b/common/test/keyboards/baseline/k_037___options___double_reset.kmx differ diff --git a/core/tests/unit/kmx/038 - punctkeys.kmn b/common/test/keyboards/baseline/k_038___punctkeys.kmn similarity index 95% rename from core/tests/unit/kmx/038 - punctkeys.kmn rename to common/test/keyboards/baseline/k_038___punctkeys.kmn index fc893993b4f85cb02286346a1f6ad83add81cd55..33f9c052973890a3c6d3abe0d65b8c2eb18281b3 100644 --- a/core/tests/unit/kmx/038 - punctkeys.kmn +++ b/common/test/keyboards/baseline/k_038___punctkeys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '038 - punctkeys' c Description: Tests punctuation keys (Unicode) c keys: [K_A][K_COLON][K_A][K_EQUAL][K_A][K_COMMA][K_A][K_HYPHEN][K_A][K_PERIOD][K_A][K_SLASH][K_A][K_BKQUOTE][K_A][K_LBRKT][K_A][K_BKSLASH][K_A][K_RBRKT][K_A][K_QUOTE][K_A][K_oE2] c expected: efghijklmnop diff --git a/common/test/keyboards/baseline/k_038___punctkeys.kmx b/common/test/keyboards/baseline/k_038___punctkeys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8166409f5f32d1c631f1cdaaeabb6f0a4d19a7d7 Binary files /dev/null and b/common/test/keyboards/baseline/k_038___punctkeys.kmx differ diff --git a/core/tests/unit/kmx/039 - generic ctrlalt.kmn b/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn similarity index 88% rename from core/tests/unit/kmx/039 - generic ctrlalt.kmn rename to common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn index d5404138bf25a1ebc91031b638c3dc0c27adf848..1bbb1de66112c2deb47df970a9c707955fdf6210 100644 --- a/core/tests/unit/kmx/039 - generic ctrlalt.kmn +++ b/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn @@ -1,3 +1,4 @@ +store(&NAME) '039 - generic ctrlalt' c Description: Tests generic alt and control (Unicode) c keys: [K_A][K_A][LCTRL K_B][K_A][LALT K_C][K_A][RCTRL K_B][K_A][RALT K_C] c expected: abcbc diff --git a/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx b/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1bf180a31c8c53250e335c5cd034092f41c99438 Binary files /dev/null and b/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx differ diff --git a/core/tests/unit/kmx/040 - long context.kmn b/common/test/keyboards/baseline/k_040___long_context.kmn similarity index 91% rename from core/tests/unit/kmx/040 - long context.kmn rename to common/test/keyboards/baseline/k_040___long_context.kmn index 994f38bede0d9ff4bdfd923d826675a16247948f..f570d2571082ef6577c6d5e34b71e80ce218102d 100644 --- a/core/tests/unit/kmx/040 - long context.kmn +++ b/common/test/keyboards/baseline/k_040___long_context.kmn @@ -1,3 +1,4 @@ +store(&NAME) '040 - long context' c Description: Tests context longer than 64 chars c keys: [K_SPACE][K_F] c expected: 012345678901234567890123456789012345678901234567890123456789012\u0020 diff --git a/common/test/keyboards/baseline/k_040___long_context.kmx b/common/test/keyboards/baseline/k_040___long_context.kmx new file mode 100644 index 0000000000000000000000000000000000000000..55a88a77abb2acf83872b6a12dc061dce250738f Binary files /dev/null and b/common/test/keyboards/baseline/k_040___long_context.kmx differ diff --git a/core/tests/unit/kmx/041 - long context and deadkeys.kmn b/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn similarity index 96% rename from core/tests/unit/kmx/041 - long context and deadkeys.kmn rename to common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn index 9675485c96d7424821714fbce7d2678d7dae030d..455fa1c41cbc62d197edcee9042ffd08592077e3 100644 --- a/core/tests/unit/kmx/041 - long context and deadkeys.kmn +++ b/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '041 - long context and deadkeys' c Description: Tests that we don't split a dk in context:set. Note that we start with a context c with length MAXCONTEXT-1 (abc...fgh is 60 characters/bytes long + 3 bytes for dk(1)). c keys: [K_Z][K_1][K_2][K_3][K_4] diff --git a/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx b/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..de2b1d5020c9941e3a57d1734085ca1dd0abe11d Binary files /dev/null and b/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx differ diff --git a/core/tests/unit/kmx/042 - long context and split deadkeys.kmn b/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn similarity index 93% rename from core/tests/unit/kmx/042 - long context and split deadkeys.kmn rename to common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn index 457dea34f675d8f34820357f2d3aa090b14ced7a..ce31d18e28228f0ae7542b5a54d9e836c448d072 100644 --- a/core/tests/unit/kmx/042 - long context and split deadkeys.kmn +++ b/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn @@ -1,3 +1,4 @@ +store(&NAME) '042 - long context and split deadkeys' c Description: Tests that we don't split a dk in context. Note that we start with a context with length MAXCONTEXT. c keys: [K_Z][K_SPACE][K_1][K_2] c context: @@ -20,4 +21,3 @@ dk(1) 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk' + ' ' > c Convert to a visible result: 'defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk ' + '1' > 'def' dk(1) 'abcdef' + '2' > 'PASS' - diff --git a/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx b/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fbe552d53b0f731852fd07a3aa5831d25cda51f8 Binary files /dev/null and b/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx differ diff --git a/core/tests/unit/kmx/043 - output and keystroke.kmn b/common/test/keyboards/baseline/k_043___output_and_keystroke.kmn similarity index 90% rename from core/tests/unit/kmx/043 - output and keystroke.kmn rename to common/test/keyboards/baseline/k_043___output_and_keystroke.kmn index 6a5d0c6085efec54b141b3780adc34119ca0c8d3..4c6409c1c7125c8c5ea2ce40851302b0b2815700 100644 --- a/core/tests/unit/kmx/043 - output and keystroke.kmn +++ b/common/test/keyboards/baseline/k_043___output_and_keystroke.kmn @@ -1,3 +1,4 @@ +store(&NAME) '043 - output and keystroke' c Description: Tests the 'emit default key' function by having an empty 'using keys' final group c keys: [K_1][K_2][K_3] c expected: abd3 diff --git a/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx b/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a25aae26a641719fa2715c7c81d3bb9b575b42ca Binary files /dev/null and b/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx differ diff --git a/core/tests/unit/kmx/044 - if and context.kmn b/common/test/keyboards/baseline/k_044___if_and_context.kmn similarity index 91% rename from core/tests/unit/kmx/044 - if and context.kmn rename to common/test/keyboards/baseline/k_044___if_and_context.kmn index 98f4bf98534b8fa00a41aa4acb14c619aee90d0c..352490325ccdfd0db93a101b8929026b3abbe9ea 100644 --- a/core/tests/unit/kmx/044 - if and context.kmn +++ b/common/test/keyboards/baseline/k_044___if_and_context.kmn @@ -1,3 +1,4 @@ +store(&NAME) '044 - if and context' c Description: Tests that context(x) takes if() statements into account. See #4275. c keys: [K_A][K_B] c expected: ex diff --git a/common/test/keyboards/baseline/k_044___if_and_context.kmx b/common/test/keyboards/baseline/k_044___if_and_context.kmx new file mode 100644 index 0000000000000000000000000000000000000000..60c0c6300a6b6337cf3f02886ffdcaffb26c0183 Binary files /dev/null and b/common/test/keyboards/baseline/k_044___if_and_context.kmx differ diff --git a/core/tests/unit/kmx/045 - deadkey and context.kmn b/common/test/keyboards/baseline/k_045___deadkey_and_context.kmn similarity index 90% rename from core/tests/unit/kmx/045 - deadkey and context.kmn rename to common/test/keyboards/baseline/k_045___deadkey_and_context.kmn index 09d276782804f0911bdeda177530d9d1c68276e6..c79953a05b8cfa7a0041a780429b0b4dde2493e5 100644 --- a/core/tests/unit/kmx/045 - deadkey and context.kmn +++ b/common/test/keyboards/baseline/k_045___deadkey_and_context.kmn @@ -1,3 +1,4 @@ +store(&NAME) '045 - deadkey and context' c Description: Tests that context emits deadkeys correctly. See #4275. c keys: [K_Y][K_Z][SHIFT K_SLASH] c expected: correct diff --git a/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx b/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4b1e3bfb2aa31a9c5885a151a395d08e36c9a9fc Binary files /dev/null and b/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx differ diff --git a/core/tests/unit/kmx/046 - deadkey and contextex.kmn b/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn similarity index 89% rename from core/tests/unit/kmx/046 - deadkey and contextex.kmn rename to common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn index 390e01df2d43b9c5bb155d413950eb639eb46c90..48ad796c3e5002905f5dae54d4c0312bd5fa5c31 100644 --- a/core/tests/unit/kmx/046 - deadkey and contextex.kmn +++ b/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn @@ -1,3 +1,4 @@ +store(&NAME) '046 - deadkey and contextex' c Description: Tests that context() emits deadkeys correctly. See #4275. c keys: [K_Y][SHIFT K_M][SHIFT K_SLASH] c expected: correct diff --git a/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx b/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx new file mode 100644 index 0000000000000000000000000000000000000000..618eefbf27fa97b51828277312f179cc1a9aecfd Binary files /dev/null and b/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx differ diff --git a/core/tests/unit/kmx/047 - caps always off initially on.kmn b/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn similarity index 90% rename from core/tests/unit/kmx/047 - caps always off initially on.kmn rename to common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn index 014a15d583edf384933ed665fd855bf9fca02dfa..4c0b69136583e1ff7a736bd2a9997a9caaae6ac9 100644 --- a/core/tests/unit/kmx/047 - caps always off initially on.kmn +++ b/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn @@ -1,3 +1,4 @@ +store(&NAME) '047 - caps always off initially on' c Description: Tests Caps Lock always off, entry to the test has Caps Lock c on to test that it is then switched off on first keystroke event. c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3] diff --git a/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx b/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx new file mode 100644 index 0000000000000000000000000000000000000000..301fcddc0469177249bea6a3887abb6daae0cf5d Binary files /dev/null and b/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx differ diff --git a/core/tests/unit/kmx/048 - modifier keys keep context.kmn b/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn similarity index 85% rename from core/tests/unit/kmx/048 - modifier keys keep context.kmn rename to common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn index 629b9b24221b93a1f8647f454f76195a9ab09b5d..bb0cfc4df5d2eca0b4e7734cde0337365cedc8ce 100644 --- a/core/tests/unit/kmx/048 - modifier keys keep context.kmn +++ b/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn @@ -1,3 +1,4 @@ +store(&NAME) '048 - modifier keys keep context' c Description: Tests that modifier keys don't reset context (#5591) c keys: [K_A][K_SHIFT][K_B][K_A][K_ALT][K_B][K_A][K_CAPS][K_B] c expected: pass.pass.pass. diff --git a/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx b/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx new file mode 100644 index 0000000000000000000000000000000000000000..98ca5a6dbe39d8e012c3f744a706235d18dbc689 Binary files /dev/null and b/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx differ diff --git a/common/test/keyboards/build.sh b/common/test/keyboards/build.sh index 0a8d1782a5701f962876f207b716097a9e94bb42..43562ffb13afdaf9688fd30ebf5eb7b07f16f0bb 100755 --- a/common/test/keyboards/build.sh +++ b/common/test/keyboards/build.sh @@ -29,7 +29,9 @@ display_usage() { for d in "$THIS_DIR/"*/; do d="$(basename "$d")" - echo " $d" + if [ "$d" != "invalid" ]; then + echo " $d" + fi done echo @@ -122,7 +124,9 @@ fi if [ ${#TARGETS[@]} == 0 ]; then for d in "$THIS_DIR/"*/; do d="$(basename "$d")" - TARGETS+=("$d") + if [ "$d" != "invalid" ] && [ "$d" != "issue" ]; then + TARGETS+=("$d") + fi done fi diff --git a/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx new file mode 100644 index 0000000000000000000000000000000000000000..563a139c19f7c6168fb49117503263b82ac5b616 Binary files /dev/null and b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx differ diff --git a/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk new file mode 100644 index 0000000000000000000000000000000000000000..79190ea1a00669de6aa6580f99793b43f565020a Binary files /dev/null and b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk differ diff --git a/common/test/keyboards/invalid/README.md b/common/test/keyboards/invalid/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a1a1b5e3ab91e1230ed96f7c8e745f81967ad3cf --- /dev/null +++ b/common/test/keyboards/invalid/README.md @@ -0,0 +1,163 @@ +# **Tests for kmcompxtest()** +
+ +This folder contains several kmn-files that are used for testing resulting error codes. + +Most of these tests are copied from balochi_phonetic and contain alterations of the data to produce (at least) 1 error. +
+
+ +## Naming Convention of kmn-files for using with kmcompxtest() + + CERR_404D_balochi_phonetic.kmn + + +The naming convention of the files is as follows: + +* All Files **MUST** start with CERR_ followed by 4 characters and an underscore. + +* After the second underscore all combination of char can be used. + +* The 4 characters following CERR_ correspond to the last 4 digits of the error code which is expected to be produced by this file. + + ( e.g. CERR_404D_balochi_phonetic.kmn should produce error 0x0000404D). + +
+
+ +While running, kmcompxtest extracts those 4 characters from the Filename (e.g. 404D for CERR_404D_balochi_phonetic) and compares those to the actual error found. + +* If these 4 characters correspond to the actual error given, the test will be marked as OK. (Code 0) +
+* If these 4 characters signify an error but **no error** does occur, the test will be marked as FAILED (Code 1) +
+* If these 4 characters signify an error but this **specific error** does not occur, the test will be marked as FAILED (Code 4) +
+* If these 4 characters signify an error number that is **not correct**, the test will be marked as FAILED (Code 5) +
+* If more than 1 error is produced, only the error coded in the Filename will be detected + +
+
+ +### Tests are available for the following CERR_: + + +| | Error Number | CERR_ +| :-: | :-----------:| :------------------------------------- +| | 00000800 | CERR_LEXICAL_MODEL_MIN +| | 000008FF | CERR_LEXICAL_MODEL_MAX +| | 00000800 | CERR_LEXICAL_MODEL_MIN +| | 000008FF | CERR_LEXICAL_MODEL_MAX +| | 00000000 | CERR_None +| | 00000001 | CERR_EndOfFile +| | 00008002 | CERR_BadCallParams +| | 00008004 | CERR_CannotAllocateMemory +| | 00008005 | CERR_InfileNotExist +| | 00008006 | CERR_CannotCreateOutfile +| | 00008007 | CERR_UnableToWriteFully +| | 00008008 | CERR_CannotReadInfile +| | 00008009 | CERR_SomewhereIGotItWrong +|**X**| 0000400A | CERR_InvalidToken +| | 0000400B | CERR_InvalidBegin +| | 0000400C | CERR_InvalidName +|**X**| 0000400D | CERR_InvalidVersion +|**X**| 0000400E | CERR_InvalidLanguageLine +| | 0000400F | CERR_LayoutButNoLanguage +| | 00004010 | CERR_InvalidLayoutLine +| | 00004011 | CERR_NoVersionLine +| | 00004012 | CERR_InvalidGroupLine +|**X**| 00004013 | CERR_InvalidStoreLine +| | 00004014 | CERR_InvalidCodeInKeyPartOfRule +|**X**| 00004015 | CERR_InvalidDeadkey +|**X**| 00004016 | CERR_InvalidValue +|**X**| 00004017 | CERR_ZeroLengthString +| | 00004018 | CERR_TooManyIndexToKeyRefs +|**X**| 00004019 | CERR_UnterminatedString +| | 0000401A | CERR_StringInVirtualKeySection +| | 0000401B | CERR_AnyInVirtualKeySection +| | 0000401C | CERR_InvalidAny +|**X**| 0000401D | CERR_StoreDoesNotExist +| | 0000401E | CERR_BeepInVirtualKeySection +| | 0000401F | CERR_IndexInVirtualKeySection +| | 00004020 | CERR_InvalidIndex +| | 00004021 | CERR_OutsInVirtualKeySection +| | 00004022 | CERR_InvalidOuts +| | 00004024 | CERR_ContextInVirtualKeySection +| | 00004025 | CERR_InvalidUse +|**X**| 00004026 | CERR_GroupDoesNotExist +| | 00004027 | CERR_VirtualKeyNotAllowedHere +| | 00004028 | CERR_InvalidSwitch +|**X**| 00004029 | CERR_NoTokensFound +| | 0000402A | CERR_InvalidLineContinuation +|**X**| 0000402B | CERR_LineTooLong +| | 0000402C | CERR_InvalidCopyright +|**X**| 0000402D | CERR_CodeInvalidInThisSection +| | 0000402E | CERR_InvalidMessage +| | 0000402F | CERR_InvalidLanguageName +| | 00004030 | CERR_InvalidBitmapLine +|**X**| 00004031 | CERR_CannotReadBitmapFile +| | 00004032 | CERR_IndexDoesNotPointToAny +| | 00004033 | CERR_ReservedCharacter +|**X**| 00004034 | CERR_InvalidCharacter +| | 00004035 | CERR_InvalidCall +| | 00004036 | CERR_CallInVirtualKeySection +| | 00004037 | CERR_CodeInvalidInKeyStore +|**X**| 00004038 | CERR_CannotLoadIncludeFile +|**X**| 00004039 | CERR_60FeatureOnly_EthnologueCode +|**X**| 0000403A | CERR_60FeatureOnly_MnemonicLayout +| | 0000403B | CERR_60FeatureOnly_OldCharPosMatching +| | 0000403C | CERR_60FeatureOnly_NamedCodes +| | 0000403D | CERR_60FeatureOnly_Contextn +|**X**| 0000403E | CERR_501FeatureOnly_Call +| | 0000403F | CERR_InvalidNamedCode +|**X**| 00004040 | CERR_InvalidSystemStore +|**X**| 00004044 | CERR_60FeatureOnly_VirtualCharKey +|**X**| 00004045 | CERR_VersionAlreadyIncluded +|**X**| 00004046 | CERR_70FeatureOnly +|**X**| 00004047 | CERR_80FeatureOnly +| | 00004048 | CERR_InvalidInVirtualKeySection +|**X**| 00004049 | CERR_InvalidIf +|**X**| 0000404A | CERR_InvalidReset +|**X**| 0000404B | CERR_InvalidSet +|**X**| 0000404C | CERR_InvalidSave +|**X**| 0000404D | CERR_InvalidEthnologueCode +| | 0000804E | CERR_CannotCreateTempfile +|**X**| 0000404F | CERR_90FeatureOnly_IfSystemStores +| | 00004050 | CERR_IfSystemStore_NotFound +| | 00004051 | CERR_90FeatureOnly_SetSystemStores +| | 00004052 | CERR_SetSystemStore_NotFound +| | 00004053 | CERR_90FeatureOnlyVirtualKeyDictionary +| | 00004054 | CERR_NotSupportedInKeymanWebContext +| | 00004055 | CERR_NotSupportedInKeymanWebOutput +| | 00004056 | CERR_NotSupportedInKeymanWebStore +| | 00004057 | CERR_VirtualCharacterKeysNotSupportedInKeymanWeb +| | 00004058 | CERR_VirtualKeysNotValidForMnemonicLayouts +| | 00004059 | CERR_InvalidTouchLayoutFile +| | 0000405A | CERR_TouchLayoutInvalidIdentifier +| | 0000405B | CERR_InvalidKeyCode +|**X**| 0000405C | CERR_90FeatureOnlyLayoutFile +|**X**| 0000405D | CERR_90FeatureOnlyKeyboardVersion +|**X**| 0000405E | CERR_KeyboardVersionFormatInvalid +| | 0000405F | CERR_ContextExHasInvalidOffset +|**X**| 00004060 | CERR_90FeatureOnlyEmbedCSS +|**X**| 00004061 | CERR_90FeatureOnlyTargets +| | 00004062 | CERR_ContextAndIndexInvalidInMatchNomatch +| | 00004063 | CERR_140FeatureOnlyContextAndNotAnyWeb +|**X**| 00004064 | CERR_ExpansionMustFollowCharacterOrVKey +|**X**| 00004065 | CERR_VKeyExpansionMustBeFollowedByVKey +|**X**| 00004066 | CERR_CharacterExpansionMustBeFollowedByCharacter +|**X**| 00004067 | CERR_VKeyExpansionMustUseConsistentShift +|**X**| 00004068 | CERR_ExpansionMustBePositive +|**X**| 00004069 | CERR_CasedKeysMustContainOnlyVirtualKeys +|**X**| 0000406A | CERR_CasedKeysMustNotIncludeShiftStates +|**X**| 0000406B | CERR_CasedKeysNotSupportedWithMnemonicLayout +|**X**| 0000406C | CERR_CannotUseReadWriteGroupFromReadonlyGroup +| | 0000406D | CERR_StatementNotPermittedInReadonlyGroup +|**X**| 0000406E | CERR_OutputInReadonlyGroup +|**X**| 0000406F | CERR_NewContextGroupMustBeReadonly +|**X**| 00004070 | CERR_PostKeystrokeGroupMustBeReadonly +|**X**| 00004071 | CERR_DuplicateGroup +|**X**| 00004072 | CERR_DuplicateStore +|**X**| 00004073 | CERR_RepeatedBegin +|**X**| 000080C0 | CERR_BufferOverflow diff --git a/common/test/keyboards/invalid/source/CERR_400A_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_400A_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..0b7f2ffb0ad3bed4171714aa3780b49e7d5c158c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_400A_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' +c ***** CERR_InvalidToken 0x0000400A ******************************** ++ [CTRL B10] > L'1234' +c ******************************************************************* ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_400D_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_400D_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ade9793ed428e853c7ccb122338987ab26fbf958 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_400D_balochi_phonetic.kmn @@ -0,0 +1,130 @@ + c ***** CERR_InvalidVersion 0x0000400D ***************************** +store(&VERSION) '127.0' +c ******************************************************************* +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_400E_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_400E_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ced7892f0584f46db2fb7aecb5f358716f801392 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_400E_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + c ***** CERR_InvalidLanguageLine 0x0000400E ************************ +store(&LANGUAGE) 'abc123' +c ******************************************************************* +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4013_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4013_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..cd838e36826e9c77539658da95cad512dc20500e --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4013_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +c ***** CERR_InvalidStoreLine 0x00004013 *************************** +store(&VERSION '10.0' +c ****************************************************************** +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4015_k_014___groups_and_virtual_keys.kmn b/common/test/keyboards/invalid/source/CERR_4015_k_014___groups_and_virtual_keys.kmn new file mode 100644 index 0000000000000000000000000000000000000000..da26166e5c3b1707b580cfffc5210d5d9da2d4bd --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4015_k_014___groups_and_virtual_keys.kmn @@ -0,0 +1,30 @@ +store(&NAME) '014 - groups and virtual keys' +c Description: Tests multiple groups and virtual keys with mnemonic layout +c keys: [K_A][SHIFT K_2][K_A][LCTRL K_2][K_A] +c expected: \u03B1\u1FB0\u1FB1 + +store(&VERSION) '9.0' + +begin Unicode > use(UMain) + +store(&MnemonicLayout) "0" + +c ---------------------------------------------- + +group(UMain) using keys + + [K_A] > U+03B1 + +c ***** CERR_InvalidDeadkey 0x00004015 ****************************** + + [CTRL K_q] > deadkey('–') +c ******************************************************************* + + + [ALT K_2] > deadkey(MacronBug) + + [SHIFT K_2] > deadkey(BreveBug) + + match > use(DK1) + +c ---------------------------------------------- + +group(DK1) + deadkey(MacronBug) U+03B1 > U+1FB1 c won't work + deadkey(BreveBug) U+03B1 > U+1FB0 c works \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4016_k_014___groups_and_virtual_keys.kmn b/common/test/keyboards/invalid/source/CERR_4016_k_014___groups_and_virtual_keys.kmn new file mode 100644 index 0000000000000000000000000000000000000000..9a07e0b058d15ddb9a3cb1d210e6a3f60b53ca7c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4016_k_014___groups_and_virtual_keys.kmn @@ -0,0 +1,29 @@ +store(&NAME) '014 - groups and virtual keys' +c Description: Tests multiple groups and virtual keys with mnemonic layout +c keys: [K_A][SHIFT K_2][K_A][LCTRL K_2][K_A] +c expected: \u03B1\u1FB0\u1FB1 + +store(&VERSION) '9.0' + +begin Unicode > use(UMain) + +store(&MnemonicLayout) "0" + +c ---------------------------------------------- + +group(UMain) using keys + + [K_A] > U+03B1 + + [CTRL K_2] > deadkey(MacronBug) + + [ALT K_2] > deadkey(MacronBug) + + [SHIFT K_2] > deadkey(BreveBug) + + match > use(DK1) + +c ---------------------------------------------- + +group(DK1) + deadkey(MacronBug) U+03B1 > U+1FB1 c won't work + + c ***** CERR_InvalidValue 0x00004016 ***************************** + deXadkey(BreveBug) U+03B1 > U+1FB0 c works +c ******************************************************************* \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4017_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4017_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ddc33aeb05752fca68ef3e754acf260f9bb105a5 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4017_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.bmp' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' +c ***** CERR_ZeroLengthString 0x00004017 *************************** ++ [CTRL B10] > +c ****************************************************************** ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4019_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4019_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..48f978a1eb8e24ba1e45d7a80de5a67f51117552 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4019_balochi_phonetic.kmn @@ -0,0 +1,129 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +c ***** CERR_UnterminatedString 0x00004019 ************************* +store(&BITMAP)CERR__balochi_phonetic.ico' +c ****************************************************************** +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_401D_k_034___options_double_set_reset.kmn b/common/test/keyboards/invalid/source/CERR_401D_k_034___options_double_set_reset.kmn new file mode 100644 index 0000000000000000000000000000000000000000..31876eeeef42142fc7f5c0066b8859ce9952b610 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_401D_k_034___options_double_set_reset.kmn @@ -0,0 +1,22 @@ +store(&NAME) '034 - options double set reset' +c Description: Tests basic option rules with save reset+set+reset +c keys: [K_A][K_A] +c expected: foo.foo. +c context: +c option: foo=1 +c expected option: foo=1 + +store(&version) '10.0' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +c ***** CERR_StoreDoesNotExist 0x0000401D *********************************** +if(foo = '1') + 'a' > 'foo.' set(foo='2') reset(ffoo) set(foo='3') reset(foo) +c ******************************************************************* +if(foo = '2') + 'a' > 'bar.' +if(foo = '3') + 'a' > 'baz.' +if(foo = '0') + 'a' > 'no foo.' diff --git a/common/test/keyboards/invalid/source/CERR_4026_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4026_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..6fb2c649de3dd6b1990401ec62fb00a5f385a7bf --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4026_balochi_phonetic.kmn @@ -0,0 +1,17 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +c ***** CERR_GroupDoesNotExist 0x00004026 ************************** + +c ****************************************************************** \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4029_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4029_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..3d093c36d41c0a3283b4b3691ba1c7d058bebd41 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4029_balochi_phonetic.kmn @@ -0,0 +1,128 @@ +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +c ***** CERR_NoTokensFound 0x00004029 ******************************* +begin +c ******************************************************************* +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '§' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_402B_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_402B_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..7da903abe354889807846d265c91abeaaf153a49 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_402B_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +c ***** CERR_LineTooLong 0x0000402B ******************************** +store(©RIGHT) '© 2017-2021 SIL InternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxInternationalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +c ****************************************************************** +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_402D_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_402D_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..37755739b9baf7d766330ebb8093a8b3aa25a8a3 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_402D_balochi_phonetic.kmn @@ -0,0 +1,129 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) +c ***** CERR_CodeInvalidInThisSection 0x0000402D ******************* + +c ****************************************************************** ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4031_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4031_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b9b62c3a42a34402a09aa4d4c0ae811a4b42fd9d --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4031_balochi_phonetic.kmn @@ -0,0 +1,129 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +c ***** CERR_CannotReadBitmapFile 0x00004031 *********************** +store(&BITMAP) 'balochi_phonetic.ico' +c ****************************************************************** +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4034_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4034_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..c5c8306d37ab0d5ca250a37da110fce3aa9b0aa4 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4034_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +c ***** CERR_InvalidCharacter 0x00004034 *************************** +store(©RIGHT) ' 2012 U+1234 -2020 SIL International' +c ****************************************************************** +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4038_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4038_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..7e6eaedb1f9c2097f13854e21ccaa9e2a7d6731c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4038_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' +c ***** CERR_CannotLoadIncludeFile 0x00004038 ********************** +store(&INCLUDECODES) 'XX.txt' +c ****************************************************************** +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4039_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4039_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..7eb9571a143603baa4ca1c4b912bccadf32f1fe7 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4039_balochi_phonetic.kmn @@ -0,0 +1,137 @@ +c ***** This test will produce XXXX Error: *************************** +c ***** 1) it will be marked as FAIL when run in kmcompxtest ******* +c ***** as a XXXXXXX non-existing ErrorNr (0x00040733) is specified ***** +c ******************************************************************store(&VERSION) '10.0' + +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +c ***** CERR_60FeatureOnly_EthnologueCode 0x00004039 **************** +store(&version) '3.0' +store(ÐNOLOGUECODE) 'eng' +c ******************************************************************* + +begin Unicode > use(main) + +group (main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '§' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_403A_014_groups_and_virtual_keys.kmn b/common/test/keyboards/invalid/source/CERR_403A_014_groups_and_virtual_keys.kmn new file mode 100644 index 0000000000000000000000000000000000000000..43981238d7b594f6a4a3ce079a640958b82a25c0 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_403A_014_groups_and_virtual_keys.kmn @@ -0,0 +1,34 @@ +c RegressionTest: Multiple groups and virtual keys +c Font: Arial Unicode MS +c Encoding: Unicode +c Description: Tests multiple groups and virtual keys -- bug Ixxx +c Procedure: Press A, Shift+2, A, Ctrl+2, A +c AutoProc: K_A S+ K_2 S- K_A C+ K_2 C- K_A +c FinalOutput: U+03B1 U+1FB0 U+1FB1 + +c ***** CERR_60FeatureOnly_MnemonicLayout 0x0000403A **************** +store(&VERSION) '3.0' +c ******************************************************************* + +NAME "AltGr Deadkeys Test" + +begin Unicode > use(UMain) c Substitute UMain1 or UMain2 + +c ***** CERR_60FeatureOnly_MnemonicLayout 0x0000403A **************** +store(&MnemonicLayout) "1" +c ******************************************************************* + + +c ---------------------------------------------- +group(UMain) using keys + + ['a'] > U+03B1 + + [CTRL '2'] > deadkey(MacronBug) + + [ALT '2'] > deadkey(MacronBug) + + [SHIFT '2'] > deadkey(BreveBug) + match > use(DK1) + +c ---------------------------------------------- + +group(DK1) + deadkey(MacronBug) U+03B1 > U+1FB1 c won't work + deadkey(BreveBug) U+03B1 > U+1FB0 c works diff --git a/common/test/keyboards/invalid/source/CERR_403E_imsample.kmn b/common/test/keyboards/invalid/source/CERR_403E_imsample.kmn new file mode 100644 index 0000000000000000000000000000000000000000..93c90cfcea9ad0d78fc964fc2fc109306f3d7d77 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_403E_imsample.kmn @@ -0,0 +1,17 @@ +c ***** CERR_501FeatureOnly_Call 0x0000403E ************************* +store(&VERSION) '3.0' +c ******************************************************************* +store(&NAME) 'IMTest' +store(DLLFunction) "imsample.dll:DF" + +begin Unicode > use(main) + +group(main) using keys + +c ***** CERR_501FeatureOnly_Call 0x0000403E ************************* + ++ [K_BKSP] > call(DLLFunction) ++ [K_ESC] > call(DLLFunction) + +nomatch > call(DLLFunction) +c ******************************************************************* \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4040_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4040_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..4e6e7b41e3690d06de24114c98218bde2d3eae5e --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4040_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +c ***** CERR_InvalidSystemStore 0x00004040 ************************* +store(&NXXAME) 'Balochi Phonetic' +c ****************************************************************** +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4044_014_groups_and_virtual_keys.kmn b/common/test/keyboards/invalid/source/CERR_4044_014_groups_and_virtual_keys.kmn new file mode 100644 index 0000000000000000000000000000000000000000..42a0f87ee027286e4a584a14949226ade314d2e2 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4044_014_groups_and_virtual_keys.kmn @@ -0,0 +1,33 @@ +c RegressionTest: Multiple groups and virtual keys +c Font: Arial Unicode MS +c Encoding: Unicode +c Description: Tests multiple groups and virtual keys -- bug Ixxx +c Procedure: Press A, Shift+2, A, Ctrl+2, A +c AutoProc: K_A S+ K_2 S- K_A C+ K_2 C- K_A +c FinalOutput: U+03B1 U+1FB0 U+1FB1 + +c ***** CERR_501FeatureOnly_Call 0x0000403E ************************* +store(&VERSION) '3.0' +c ******************************************************************* + +NAME "AltGr Deadkeys Test" + +begin Unicode > use(UMain) c Substitute UMain1 or UMain2 + +store(&MnemonicLayout) "1" + +c ---------------------------------------------- +c ***** CERR_60FeatureOnly_VirtualCharKey 0x00004044 **************** +group(UMain) using keys + + ['a'] > U+03B1 + + [CTRL '2'] > deadkey(MacronBug) + + [ALT '2'] > deadkey(MacronBug) + + [SHIFT '2'] > deadkey(BreveBug) +c ******************************************************************* + match > use(DK1) + +c ---------------------------------------------- + +group(DK1) + deadkey(MacronBug) U+03B1 > U+1FB1 c won't work + deadkey(BreveBug) U+03B1 > U+1FB0 c works diff --git a/common/test/keyboards/invalid/source/CERR_4045_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4045_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b50a62ced3d79c202444d2e78ee72fc87f39c853 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4045_balochi_phonetic.kmn @@ -0,0 +1,134 @@ +store(&VERSION) '10.0' + +c ***** CERR_VersionAlreadyIncluded 0x00004045 ****************************** +store(&VERSION) '110.0' +c ******************************************************************* + +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_4046_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4046_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..69b4ce96f60b103118c13352af0b9026ca5cb67b --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4046_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +c ***** CERR_70FeatureOnly 0x00004046 ****************************** +store(&VERSION) '4.0' +c ****************************************************************** +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4047_k_022___options_with_preset.kmn b/common/test/keyboards/invalid/source/CERR_4047_k_022___options_with_preset.kmn new file mode 100644 index 0000000000000000000000000000000000000000..174a768ee55235664cf2add63c231a9e71b12fc0 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4047_k_022___options_with_preset.kmn @@ -0,0 +1,26 @@ +store(&NAME) '022 - options with preset' +c Description: Tests basic option rules with a preset supplied +c keys: [K_A][K_1][K_A][K_0][K_A] +c expected: foo.foo.no foo. +c context: +c option: foo=1 +c expected option: foo=0 +c saved option: foo=0 + +c ***** CERR_80FeatureOnly 0x00004047 ******************************* +store(&VERSION) '3.0' +c ******************************************************************* + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +if(foo = '1') + 'a' > 'foo.' +if(foo = '0') + 'a' > 'no foo.' + +c ***** CERR_80FeatureOnly 0x00004047 ******************************* ++ '1' > set(foo = '1') save(foo) ++ '0' > set(foo = '0') save(foo) +c ******************************************************************* \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4049_k_022___options_with_preset.kmn b/common/test/keyboards/invalid/source/CERR_4049_k_022___options_with_preset.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ea423987a4fe8efe39549d7e2ad069fe322bdb61 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4049_k_022___options_with_preset.kmn @@ -0,0 +1,24 @@ +store(&NAME) '022 - options with preset' +c Description: Tests basic option rules with a preset supplied +c keys: [K_A][K_1][K_A][K_0][K_A] +c expected: foo.foo.no foo. +c context: +c option: foo=1 +c expected option: foo=0 +c saved option: foo=0 + +store(&VERSION) '8.0' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +c ***** CERR_InvalidIf 0x00004049 *********************************** +if(foo '1') + 'a' > 'foo.' +c ******************************************************************* +if(foo = '0') + 'a' > 'no foo.' + ++ '1' > set(foo = '1') save(foo) ++ '0' > set(foo = '0') save(foo) \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_404A_k_025___options_with_reset.kmn b/common/test/keyboards/invalid/source/CERR_404A_k_025___options_with_reset.kmn new file mode 100644 index 0000000000000000000000000000000000000000..9dea1a51e4a62961e7da2b88427758dcd5eca10b --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_404A_k_025___options_with_reset.kmn @@ -0,0 +1,23 @@ +store(&NAME) '025 - options with reset' +c Description: Tests basic option rules with reset +c keys: [K_A][K_1][K_A][K_0][K_A][K_3] +c expected: foo.foo.no foo. +c context: +c option: foo=1 +c expected option: foo=1 + +store(&version) '10.0' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +if(foo = '1') + 'a' > 'foo.' +if(foo = '0') + 'a' > 'no foo.' ++ '1' > set(foo = '1') ++ '0' > set(foo = '0') +c ***** CERR_InvalidReset 0x0000404A ******************************** ++ '3' > reset +c ******************************************************************* diff --git a/common/test/keyboards/invalid/source/CERR_404B_k_025___options_with_reset.kmn b/common/test/keyboards/invalid/source/CERR_404B_k_025___options_with_reset.kmn new file mode 100644 index 0000000000000000000000000000000000000000..70b534fc7ec6079a419d3bdad044d974bb2947b2 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_404B_k_025___options_with_reset.kmn @@ -0,0 +1,25 @@ +store(&NAME) '025 - options with reset' +c Description: Tests basic option rules with reset +c keys: [K_A][K_1][K_A][K_0][K_A][K_3] +c expected: foo.foo.no foo. +c context: +c option: foo=1 +c expected option: foo=1 + +store(&version) '10.0' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +if(foo = '1') + 'a' > 'foo.' +if(foo = '0') + 'a' > 'no foo.' + +c ***** CERR_InvalidReset 0x0000404B ******************************** ++ '1' > set( ++ '0' > set +c ******************************************************************* + ++ '3' > reset(foo) diff --git a/common/test/keyboards/invalid/source/CERR_404C_k_023___options_with_save.kmn b/common/test/keyboards/invalid/source/CERR_404C_k_023___options_with_save.kmn new file mode 100644 index 0000000000000000000000000000000000000000..a160ace737fe6e66a26c221582ba17e63a272981 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_404C_k_023___options_with_save.kmn @@ -0,0 +1,23 @@ +store(&NAME) '023 - options with save' +c Description: Tests basic option rules with save +c keys: [K_A][K_1][K_A][K_0][K_A][K_2] +c expected: no foo.foo.no foo. +c context: +c expected option: foo=0 +c saved option: foo=0 + +store(&version) '10.0' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +if(foo = '1') + 'a' > 'foo.' +if(foo = '0') + 'a' > 'no foo.' ++ '1' > set(foo = '1') ++ '0' > set(foo = '0') +c ***** CERR_InvalidSave 0x0000404C ********************************* ++ '2' > save +c ******************************************************************* diff --git a/common/test/keyboards/invalid/source/CERR_404D_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_404D_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..7aadf5567236c4ba363f4c10fd5131b7602c996e --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_404D_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' +c ***** CERR_InvalidEthnologueCode 0x0000404D ********************** +store(&EthnologueCode) "xxxx" +c ****************************************************************** +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_404F_k_026___system_stores.kmn b/common/test/keyboards/invalid/source/CERR_404F_k_026___system_stores.kmn new file mode 100644 index 0000000000000000000000000000000000000000..e336ac5d789c0ac505e235998838905724c58ab7 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_404F_k_026___system_stores.kmn @@ -0,0 +1,29 @@ +store(&NAME) '026 - system stores' +c Description: Tests platform and baselayout system store rules +c keys: [K_1][K_2][K_3][K_4][K_A][K_B] +c expected: windows.desktop.hardware.native.en-US.kbdus.dll. +c context: +c (default) option: &platform=windows desktop hardware native <-- ignored by test +c (default) option: &baseLayout=kbdus.dll <-- ignored by test +c (default) option: &baseLayoutAlt=en-US <-- ignored by test + +c ***** CERR_90FeatureOnly_IfSystemStores 0x0000404F **************** +store(&version) '3.0' +c ******************************************************************* + +store(digit) '1234567890' +store(letter) 'abcdefghij' + +begin Unicode > use(Main) + +group(Main) using keys + +platform('windows') + '1' > 'windows.' +platform('desktop') + '2' > 'desktop.' +platform('hardware') + '3' > 'hardware.' +platform('native') + '4' > 'native.' ++ any(digit) > 'platform-fail.' + +baselayout('en-US') + 'a' > 'en-US.' +baselayout('kbdus.dll') + 'b' > 'kbdus.dll.' ++ any(letter) > 'baselayout-fail.' diff --git a/common/test/keyboards/invalid/source/CERR_405C_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_405C_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ad777616033d925f4feec7502e1b70666fafeafd --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_405C_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +c ***** CERR_90FeatureOnlyLayoutFile 0x0000405C ******************** +store(&VERSION) '4.0' +c ****************************************************************** +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_405D_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_405D_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..e1224961ddd0c80fffdcb53419ea3159c6212793 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_405D_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +c ***** CERR_90FeatureOnlyKeyboardVersion 0x0000405D *************** +store(&VERSION) '4.0' +c ****************************************************************** +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_405E_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_405E_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..c255c0f5e7c8ca1c3a58cab350ab2793c886d5b8 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_405E_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +c ***** CERR_KeyboardVersionFormatInvalid 0x0000405E *************** +store(&KEYBOARDVERSION) 'abc' +c ****************************************************************** +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4060_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4060_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..6fc5ebe1ae72fa40af46af5edef5583559953d51 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4060_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +store(&VERSION) '4.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' +c ***** CERR_90FeatureOnlyEmbedCSS 0x00004060 ********************** +store(&KMW_EMBEDCSS) 'naijatype.css' +c ****************************************************************** +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4061_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4061_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..47b224d2c6a281ef1ca6c84df6f3b9c51eb79317 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4061_balochi_phonetic.kmn @@ -0,0 +1,130 @@ +c ***** CERR_90FeatureOnlyTargets 0x00004061 *********************** +store(&VERSION) '4.0' +c ****************************************************************** +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' \ No newline at end of file diff --git a/common/test/keyboards/invalid/source/CERR_4064_caps_lock_layer_3620.kmn b/common/test/keyboards/invalid/source/CERR_4064_caps_lock_layer_3620.kmn new file mode 100644 index 0000000000000000000000000000000000000000..dbc3b65a80fd5e6a5305221ba5e811c99da7ab76 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4064_caps_lock_layer_3620.kmn @@ -0,0 +1,22 @@ +c caps_lock_layer_3620 generated from template at 2021-11-30 14:58:52 +c with name "Caps Lock Layer 3620" +store(&VERSION) '10.0' +store(&NAME) 'Caps Lock Layer 3620' +store(©RIGHT) '©' +store(&KEYBOARDVERSION) '1.0' +store(&TARGETS) 'any' +store(&VISUALKEYBOARD) 'caps_lock_layer_3620.kvks' +store(&LAYOUTFILE) 'caps_lock_layer_3620.keyman-touch-layout' + +begin Unicode > use(main) + +store(key) [K_A] .. [K_Z] [SHIFT K_A] .. [SHIFT K_Z] +c ***** CERR_ExpansionMustFollowCharacterOrVKey 0x00004064 ********** +store(out) .. 'z' 'A' .. 'Z' +c ******************************************************************* +store(&CasedKeys) [K_A] .. [K_Z] + +group(main) using keys + ++ any(key) > index(out, 1) + diff --git a/common/test/keyboards/invalid/source/CERR_4065_test_expansion_invalid.kmn b/common/test/keyboards/invalid/source/CERR_4065_test_expansion_invalid.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b721946537f14c4f9ce0e21cd2b72303ab1b404c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4065_test_expansion_invalid.kmn @@ -0,0 +1,34 @@ +c This file tests that invalid expansions are caught +store(&NAME) 'Test Invalid Expansions' +begin Unicode > use(main) + +group(main) using keys + +c Backward ranges + +store(char_range_backward) 'c' .. 'a' +store(key_range_backward) [K_E] .. [K_A] + +store(char_range_empty) 'c' .. 'c' +store(key_range_empty) [K_E] .. [K_E] + +c Incomplete ranges + +store(range_open) .. +store(char_range_open_end) 'a' .. +store(char_range_open_start) .. 'z' + +store(key_range_open_end) [K_A] .. +store(key_range_open_start) .. [K_Z] + +c Inconsistent ranges + +store(char_range_to_key) 'a' .. [K_Z] +store(key_range_to_char) [K_A] .. 'z' + +store(char_range_to_statement) 'a' .. nul +store(key_range_to_statement) [K_A] .. nul +store(char_range_from_statement) nul .. 'a' +store(key_range_from_statement) nul .. [K_A] + +store(key_range_shift_inconsistent) [SHIFT K_A] .. [K_Z] diff --git a/common/test/keyboards/invalid/source/CERR_4066_test_expansion_invalid.kmn b/common/test/keyboards/invalid/source/CERR_4066_test_expansion_invalid.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b721946537f14c4f9ce0e21cd2b72303ab1b404c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4066_test_expansion_invalid.kmn @@ -0,0 +1,34 @@ +c This file tests that invalid expansions are caught +store(&NAME) 'Test Invalid Expansions' +begin Unicode > use(main) + +group(main) using keys + +c Backward ranges + +store(char_range_backward) 'c' .. 'a' +store(key_range_backward) [K_E] .. [K_A] + +store(char_range_empty) 'c' .. 'c' +store(key_range_empty) [K_E] .. [K_E] + +c Incomplete ranges + +store(range_open) .. +store(char_range_open_end) 'a' .. +store(char_range_open_start) .. 'z' + +store(key_range_open_end) [K_A] .. +store(key_range_open_start) .. [K_Z] + +c Inconsistent ranges + +store(char_range_to_key) 'a' .. [K_Z] +store(key_range_to_char) [K_A] .. 'z' + +store(char_range_to_statement) 'a' .. nul +store(key_range_to_statement) [K_A] .. nul +store(char_range_from_statement) nul .. 'a' +store(key_range_from_statement) nul .. [K_A] + +store(key_range_shift_inconsistent) [SHIFT K_A] .. [K_Z] diff --git a/common/test/keyboards/invalid/source/CERR_4067_test_expansion_invalid.kmn b/common/test/keyboards/invalid/source/CERR_4067_test_expansion_invalid.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b721946537f14c4f9ce0e21cd2b72303ab1b404c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4067_test_expansion_invalid.kmn @@ -0,0 +1,34 @@ +c This file tests that invalid expansions are caught +store(&NAME) 'Test Invalid Expansions' +begin Unicode > use(main) + +group(main) using keys + +c Backward ranges + +store(char_range_backward) 'c' .. 'a' +store(key_range_backward) [K_E] .. [K_A] + +store(char_range_empty) 'c' .. 'c' +store(key_range_empty) [K_E] .. [K_E] + +c Incomplete ranges + +store(range_open) .. +store(char_range_open_end) 'a' .. +store(char_range_open_start) .. 'z' + +store(key_range_open_end) [K_A] .. +store(key_range_open_start) .. [K_Z] + +c Inconsistent ranges + +store(char_range_to_key) 'a' .. [K_Z] +store(key_range_to_char) [K_A] .. 'z' + +store(char_range_to_statement) 'a' .. nul +store(key_range_to_statement) [K_A] .. nul +store(char_range_from_statement) nul .. 'a' +store(key_range_from_statement) nul .. [K_A] + +store(key_range_shift_inconsistent) [SHIFT K_A] .. [K_Z] diff --git a/common/test/keyboards/invalid/source/CERR_4068_test_expansion_invalid.kmn b/common/test/keyboards/invalid/source/CERR_4068_test_expansion_invalid.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b721946537f14c4f9ce0e21cd2b72303ab1b404c --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4068_test_expansion_invalid.kmn @@ -0,0 +1,34 @@ +c This file tests that invalid expansions are caught +store(&NAME) 'Test Invalid Expansions' +begin Unicode > use(main) + +group(main) using keys + +c Backward ranges + +store(char_range_backward) 'c' .. 'a' +store(key_range_backward) [K_E] .. [K_A] + +store(char_range_empty) 'c' .. 'c' +store(key_range_empty) [K_E] .. [K_E] + +c Incomplete ranges + +store(range_open) .. +store(char_range_open_end) 'a' .. +store(char_range_open_start) .. 'z' + +store(key_range_open_end) [K_A] .. +store(key_range_open_start) .. [K_Z] + +c Inconsistent ranges + +store(char_range_to_key) 'a' .. [K_Z] +store(key_range_to_char) [K_A] .. 'z' + +store(char_range_to_statement) 'a' .. nul +store(key_range_to_statement) [K_A] .. nul +store(char_range_from_statement) nul .. 'a' +store(key_range_from_statement) nul .. [K_A] + +store(key_range_shift_inconsistent) [SHIFT K_A] .. [K_Z] diff --git a/common/test/keyboards/invalid/source/CERR_4069_test_casedkeys_invalid_1.kmn b/common/test/keyboards/invalid/source/CERR_4069_test_casedkeys_invalid_1.kmn new file mode 100644 index 0000000000000000000000000000000000000000..8bc65cced65cdb06504bd374dc707866e88155cc --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4069_test_casedkeys_invalid_1.kmn @@ -0,0 +1,10 @@ +c Tests cased keys - invalid codes +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) U+1000 + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/common/test/keyboards/invalid/source/CERR_406A_test_casedkeys_invalid_2.kmn b/common/test/keyboards/invalid/source/CERR_406A_test_casedkeys_invalid_2.kmn new file mode 100644 index 0000000000000000000000000000000000000000..508466c07e53ddddd21139e25eaf115d3513dff3 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_406A_test_casedkeys_invalid_2.kmn @@ -0,0 +1,10 @@ +c Tests cased keys - invalid codes +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [SHIFT K_1] + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/common/test/keyboards/invalid/source/CERR_406B_test_casedkeys_mnemonic_1.kmn b/common/test/keyboards/invalid/source/CERR_406B_test_casedkeys_mnemonic_1.kmn new file mode 100644 index 0000000000000000000000000000000000000000..40b0bbb9668283bea355f6babeb7dea0a5936dd3 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_406B_test_casedkeys_mnemonic_1.kmn @@ -0,0 +1,11 @@ +c Tests cased keys - invalid with mnemonic layout +store(&NAME) 'Test &CasedKeys' +store(&mnemoniclayout) '1' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [K_A] [K_E] [K_I] [K_O] [K_U] [K_1] .. [K_4] + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/common/test/keyboards/invalid/source/CERR_406C_test_5963_readonlygroup_usenonreadonly.kmn b/common/test/keyboards/invalid/source/CERR_406C_test_5963_readonlygroup_usenonreadonly.kmn new file mode 100644 index 0000000000000000000000000000000000000000..8ced461fc755ab4db76b43c9b5728d529b29b4ae --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_406C_test_5963_readonlygroup_usenonreadonly.kmn @@ -0,0 +1,12 @@ +c should fail to compile, readonly group uses() non-readonly group + +begin Unicode > use(main) + +group(main) using keys + +group(foo) readonly + +'a' > use(bar) + +group(bar) + diff --git a/common/test/keyboards/invalid/source/CERR_406E_test_5963_readonlygroup_misplacedcontext.kmn b/common/test/keyboards/invalid/source/CERR_406E_test_5963_readonlygroup_misplacedcontext.kmn new file mode 100644 index 0000000000000000000000000000000000000000..251a3d1033c7fa2593ed684cb35e8138db89fd64 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_406E_test_5963_readonlygroup_misplacedcontext.kmn @@ -0,0 +1,11 @@ +c Should fail to compile, `context` must be omitted or first statement in output + +begin Unicode > use(main) + +group(main) using keys + +group(foo) readonly + +'a' > use(bar) context + +group(bar) readonly diff --git a/common/test/keyboards/invalid/source/CERR_406F_test_5963_newcontext_2.kmn b/common/test/keyboards/invalid/source/CERR_406F_test_5963_newcontext_2.kmn new file mode 100644 index 0000000000000000000000000000000000000000..a2916de43a66b86bd2cdf3f158fca3a7da887094 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_406F_test_5963_newcontext_2.kmn @@ -0,0 +1,9 @@ +c Should fail to compile, group is not readonly + +begin Unicode > use(main) +begin NewContext > use(NewContext) + +group(NewContext) + +group(main) using keys + diff --git a/common/test/keyboards/invalid/source/CERR_4070_test_5963_postkeystroke_2.kmn b/common/test/keyboards/invalid/source/CERR_4070_test_5963_postkeystroke_2.kmn new file mode 100644 index 0000000000000000000000000000000000000000..12bb3661501c9feb86cd059ac063b5423379cc1f --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4070_test_5963_postkeystroke_2.kmn @@ -0,0 +1,8 @@ +c Should fail to compile, group is not readonly + +begin Unicode > use(main) +begin PostKeystroke > use(PostKeystroke) + +group(PostKeystroke) + +group(main) using keys diff --git a/common/test/keyboards/invalid/source/CERR_4071_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4071_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..68a8a542469eed6765306ba3e326bbf5384e7669 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4071_balochi_phonetic.kmn @@ -0,0 +1,131 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +c ***** CERR_DuplicateGroup 0x00004071 ****************************** +group(main) using keys +c ******************************************************************* +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_4072_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4072_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ca9f15d3f3a6f1afa98285e1b6944216b19538bd --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4072_balochi_phonetic.kmn @@ -0,0 +1,133 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' + +c ***** DuplicateStore 0x00004072 *********************************** +store(©RIGHT) '© ABC' +c ******************************************************************* + +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_4073_balochi_phonetic.kmn b/common/test/keyboards/invalid/source/CERR_4073_balochi_phonetic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..9483a2196dc4777083e3150d075fc34d983a6965 --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_4073_balochi_phonetic.kmn @@ -0,0 +1,132 @@ +store(&VERSION) '10.0' +store(&NAME) 'Balochi Phonetic' +store(&TARGETS) 'any' +store(&KMW_RTL) '1' +store(&LAYOUTFILE) 'balochi_phonetic.keyman-touch-layout' +store(&KEYBOARDVERSION) '1.4.2' +store(©RIGHT) '© 2017-2021 SIL International' +store(&MESSAGE) 'The Balochi Phonetic keyboard is distributed under The MIT License (MIT).' +store(&KMW_HELPTEXT) 'This is a phonetically organized Balochi keyboard.' +store(&VISUALKEYBOARD) 'balochi_phonetic.kvks' +store(&BITMAP) 'CERR__balochi_phonetic.ico' + +begin Unicode > use(main) + +c ***** CERR_RepeatedBegin 0x00004073 ******************************* +begin Unicode > use(main) +c ******************************************************************* + +group(main) using keys ++ [CTRL B08] > '‎' ++ [CTRL B09] > '‏' ++ [CTRL B10] > '–' ++ [SHIFT CTRL E11] > '“' ++ [SHIFT CTRL E12] > '”' ++ [CTRL E11] > '‘' ++ [CTRL E12] > '’' ++ [CTRL K_K] > 'ء' ++ [CTRL K_D] > 'ذ' ++ [K_EQUAL] > '=' ++ [SHIFT K_8] > '*' ++ [SHIFT K_7] > '&' ++ [SHIFT K_5] > '%' ++ [SHIFT K_4] > '$' ++ [SHIFT K_3] > '#' ++ [SHIFT K_2] > '@' ++ [SHIFT K_1] > '!' ++ [SHIFT K_N] > 'ں' ++ [K_BKSLASH] > '۔' ++ [K_LBRKT] > ']' ++ [K_RBRKT] > '[' ++ [K_F] > 'َ' ++ [K_V] > 'ِ' ++ [K_X] > 'ش' ++ [SHIFT K_H] > 'ح' ++ [SHIFT K_G] > 'ص' ++ [SHIFT K_Y] > 'ي' ++ [CTRL K_0] > '۰' ++ [CTRL K_9] > '۹' ++ [CTRL K_8] > '۸' ++ [CTRL K_7] > '۷' ++ [CTRL K_6] > '۶' ++ [CTRL K_5] > '۵' ++ [CTRL K_4] > '۴' ++ [CTRL K_3] > '۳' ++ [CTRL K_2] > '۲' ++ [CTRL K_1] > '۱' ++ [K_SLASH] > '/' ++ [K_HYPHEN] > '-' ++ [SHIFT K_COLON] > ':' ++ [SHIFT K_QUOTE] > '"' ++ [K_QUOTE] > U+0027 ++ [K_O] > 'ُ' ++ [SHIFT K_O] > 'ْ' ++ [K_BKQUOTE] > 'ء' ++ [SHIFT K_W] > 'أ' ++ [SHIFT K_P] > 'ّ' ++ [SHIFT K_BKQUOTE] > 'ٔ' ++ [SHIFT K_J] > 'ض' ++ [SHIFT K_9] > ')' ++ [SHIFT K_RBRKT] > '{' ++ [SHIFT K_LBRKT] > '}' ++ [SHIFT K_0] > '(' ++ [SHIFT K_SLASH] > '؟' ++ [SHIFT K_K] > 'ع' ++ [SHIFT K_EQUAL] > '«' ++ [SHIFT K_HYPHEN] > '»' ++ [SHIFT K_I] > 'ً' + ++ [SHIFT K_PERIOD] > '‹' ++ [SHIFT K_COMMA] > '›' ++ [SHIFT K_L] > 'ـ' ++ [SHIFT K_U] > 'ۀ' ++ [SHIFT K_B] > 'ة' ++ [SHIFT K_M] > 'غ' ++ [SHIFT K_F] > 'ف' ++ [SHIFT K_E] > 'ۓ' ++ [SHIFT K_Q] > 'ق' ++ [K_0] > '0' ++ [K_9] > '9' ++ [K_8] > '8' ++ [K_7] > '7' ++ [K_6] > '6' ++ [K_5] > '5' ++ [K_4] > '4' ++ [K_3] > '3' ++ [K_2] > '2' ++ [K_1] > '1' ++ [K_I] > 'ێ' ++ [SHIFT K_D] > 'ڈ' ++ [K_G] > 'گ' ++ [K_U] > 'ۆ' ++ [SHIFT K_R] > 'ڑ' ++ [SHIFT K_6] > '‌' ++ [SHIFT K_T] > 'ٹ' ++ [SHIFT K_V] > 'ظ' ++ [SHIFT K_C] > 'ط' ++ [SHIFT K_X] > 'خ' ++ [SHIFT K_Z] > 'ژ' ++ [SHIFT K_S] > 'ث' ++ [SHIFT K_A] > 'آ' ++ [K_PERIOD] > '.' ++ [K_COMMA] > '،' ++ [K_M] > 'م' ++ [K_N] > 'ن' ++ [K_B] > 'ب' ++ [K_C] > 'چ' ++ [K_Z] > 'ز' ++ [K_P] > 'پ' ++ [K_Y] > 'ی' ++ [K_T] > 'ت' ++ [K_R] > 'ر' ++ [K_E] > 'ے' ++ [K_W] > 'و' ++ [K_Q] > 'ئ' ++ [K_COLON] > '؛' ++ [K_L] > 'ل' ++ [K_K] > 'ک' ++ [K_J] > 'ج' ++ [K_H] > 'ه' ++ [K_D] > 'د' ++ [K_S] > 'س' ++ [K_A] > 'ا' diff --git a/common/test/keyboards/invalid/source/CERR_80C0_test_expansion_absurd.kmn b/common/test/keyboards/invalid/source/CERR_80C0_test_expansion_absurd.kmn new file mode 100644 index 0000000000000000000000000000000000000000..905093bc87ba33ce886f45e6428289af0d0e281d --- /dev/null +++ b/common/test/keyboards/invalid/source/CERR_80C0_test_expansion_absurd.kmn @@ -0,0 +1,7 @@ +c This file tests absurdly long expansions +store(&NAME) 'Test Expansions' +begin Unicode > use(main) + +group(main) using keys + +store(schar_range) U+0020 .. U+10FFFF diff --git a/common/test/keyboards/invalid/source/CERR__balochi_phonetic.ico b/common/test/keyboards/invalid/source/CERR__balochi_phonetic.ico new file mode 100644 index 0000000000000000000000000000000000000000..3fc6a224294396c55920dba4f44210059df71bef Binary files /dev/null and b/common/test/keyboards/invalid/source/CERR__balochi_phonetic.ico differ diff --git a/common/test/keyboards/the_99/README.md b/common/test/keyboards/issue/6254-invalid-version-99/README.md similarity index 100% rename from common/test/keyboards/the_99/README.md rename to common/test/keyboards/issue/6254-invalid-version-99/README.md diff --git a/common/test/keyboards/the_99/build.sh b/common/test/keyboards/issue/6254-invalid-version-99/build.sh similarity index 92% rename from common/test/keyboards/the_99/build.sh rename to common/test/keyboards/issue/6254-invalid-version-99/build.sh index aae41c2cb5273d95c9b6d50779fd85707a16a6b3..68577d0dc9a798bd9b2f3467b49b439d22783736 100755 --- a/common/test/keyboards/the_99/build.sh +++ b/common/test/keyboards/issue/6254-invalid-version-99/build.sh @@ -6,7 +6,7 @@ set -u ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh" # . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" ## END STANDARD BUILD SCRIPT INCLUDE THIS_DIR="$(dirname "$THIS_SCRIPT")" @@ -49,6 +49,8 @@ while [[ $# -gt 0 ]] ; do shift done +cd "$THIS_DIR" + if $CLEAN; then if ! $QUIET; then echo Cleaning the_99.kmp from build/ diff --git a/common/test/keyboards/the_99/the_99.kmp b/common/test/keyboards/issue/6254-invalid-version-99/the_99.kmp similarity index 100% rename from common/test/keyboards/the_99/the_99.kmp rename to common/test/keyboards/issue/6254-invalid-version-99/the_99.kmp diff --git a/common/test/resources/keyboards/options_with_save.js b/common/test/resources/keyboards/options_with_save.js index 301ae1c673b602780a26ed0c71215fae44ccfbf7..8252b5166041aeea7d27f8100e72267fd269543b 100644 --- a/common/test/resources/keyboards/options_with_save.js +++ b/common/test/resources/keyboards/options_with_save.js @@ -1,4 +1,4 @@ -// Original source may be found within the repo at /core/tests/unit/kmx/ as #023. +// Original source may be found within the repo at /common/tests/keyboards/baseline/k_023___options_with_save.kmn if(typeof keyman === 'undefined') { console.log('Keyboard requires KeymanWeb 10.0 or later'); diff --git a/common/web/eslint/eslintNoNodeImports.js b/common/web/eslint/eslintNoNodeImports.js new file mode 100644 index 0000000000000000000000000000000000000000..e9c022439a752f43dc10272df5b8cd048f284245 --- /dev/null +++ b/common/web/eslint/eslintNoNodeImports.js @@ -0,0 +1,13 @@ +module.exports = { + // Prevents use of Node's standard imports so we can make sure we can also run + // on web. Comes from eslint docs but not sure how reliable it is: + // https://eslint.org/docs/latest/rules/no-restricted-imports + // TODO: Consider moving to + // https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-nodejs-modules.md + // which uses https://npmjs.com/package/is-core-module + rules: { + "no-restricted-imports": ["error", + "assert","buffer","child_process","cluster","crypto","dgram","dns","domain","events","freelist","fs","http","https","module","net","os","path","punycode","querystring","readline","repl","smalloc","stream","string_decoder","sys","timers","tls","tracing","tty","url","util","vm","zlib" + ], + }, +}; diff --git a/common/web/input-processor/build.sh b/common/web/input-processor/build.sh index c7995e5d42933d913475072883bf6b0731c40a9f..11efb33fbaa300f0329f019a0e54732efc7f4dbc 100755 --- a/common/web/input-processor/build.sh +++ b/common/web/input-processor/build.sh @@ -8,35 +8,29 @@ set -eu # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" ## END STANDARD BUILD SCRIPT INCLUDE +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + # This script runs from its own folder -cd "$(dirname "$THIS_SCRIPT")" +cd "$THIS_SCRIPT_PATH" ################################ Main script ################################ -# TODO: for predictive-text, we only need :headless, perhaps we should be splitting modules? -# TODO: remove :tools once kmlmc is a dependency for test:module - builder_describe "Builds the standalone, headless form of Keyman Engine for Web's input-processor module" \ "@/common/web/keyman-version" \ "@/common/web/keyboard-processor" \ "@/common/predictive-text" \ + "@/developer/src/kmc-model test" \ "clean" \ "configure" \ "build" \ "test" \ - ":module A headless, Node-oriented version of the module useful for unit tests" \ - ":tools Related tools useful for development and testing of this module" \ "--ci Sets $(builder_term test) action to use CI-based test configurations & reporting" builder_describe_outputs \ configure /node_modules \ - configure:module /node_modules \ - configure:tools /node_modules \ - build:module /common/web/input-processor/build/index.js \ - build:tools /developer/src/kmc/build/src/kmlmc.js # TODO: remove this once kmlmc is a dependency + build /common/web/input-processor/build/index.js builder_parse "$@" @@ -56,27 +50,14 @@ fi ### BUILD ACTIONS -if builder_start_action build:tools; then - # Used by test:module - # TODO: convert to a dependency once we have updated kmlmc to use builder script - pushd "$KEYMAN_ROOT/developer/src/kmc-model" - ./build.sh - popd - pushd "$KEYMAN_ROOT/developer/src/kmc" - ./build.sh - popd - - builder_finish_action success build:tools -fi - -if builder_start_action build:module; then - tsc -b src/tsconfig.json - builder_finish_action success build:module +if builder_start_action build; then + tsc --build src/tsconfig.json + builder_finish_action success build fi # TEST ACTIONS -if builder_start_action test:module; then +if builder_start_action test; then FLAGS= if builder_has_option --ci; then FLAGS="--reporter mocha-teamcity-reporter" @@ -87,5 +68,5 @@ if builder_start_action test:module; then mocha --recursive $FLAGS ./tests/cases/ - builder_finish_action success test:module + builder_finish_action success test fi \ No newline at end of file diff --git a/common/web/keyman-version/.npmignore b/common/web/keyman-version/.npmignore deleted file mode 100644 index c22b1d8223046f94c6cc19600a662e0ca0074413..0000000000000000000000000000000000000000 --- a/common/web/keyman-version/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -build/index.tsbuildinfo -build/tsconfig.esm.tsbuildinfo -build.sh -index.ts -tsconfig.json \ No newline at end of file diff --git a/common/web/keyman-version/build.sh b/common/web/keyman-version/build.sh index 7cd5b25e08a02e46413a9d82fb180e8e542f90f7..07fb6220601af5e32214e0bc8db292bbee3052f8 100755 --- a/common/web/keyman-version/build.sh +++ b/common/web/keyman-version/build.sh @@ -23,7 +23,8 @@ builder_describe "Build the include script for current Keyman version" \ configure \ clean \ build \ - publish \ + "pack build a local .tgz pack for testing" \ + "publish publish to npm" \ test \ --dry-run @@ -97,4 +98,8 @@ if builder_start_action publish; then . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_publish_to_npm builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/common/web/keyman-version/package.json b/common/web/keyman-version/package.json index 1fd26b2898c8a929fdd3a18ca016e0a4d5c84fa3..ef94e58750ad4187925ff01f19b54b702d56e426 100644 --- a/common/web/keyman-version/package.json +++ b/common/web/keyman-version/package.json @@ -8,6 +8,9 @@ "require": "./build/index.js" } }, + "files": [ + "/build/" + ], "scripts": { "build": "echo 'Building @keymanapp/keyman-version' && tsc -b", "clean": "tsc -b --clean" diff --git a/common/web/types/.eslintrc.cjs b/common/web/types/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..21618ea66ffe4d7d61edbe0b49aa076b60927417 --- /dev/null +++ b/common/web/types/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + ignorePatterns: [ + ".*/*", + "build/*", + "coverage/*", + "node_modules/*", + "test/fixtures/*", + ], + overrides: [ + { + files: "src/**/*.ts", + extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + }, + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/common/web/types/build.sh b/common/web/types/build.sh index 3c7e69b2167207c76b3d89f882305dd700f09170..eb983acec8514b873792f8ae4661b8b27c260470 100755 --- a/common/web/types/build.sh +++ b/common/web/types/build.sh @@ -22,6 +22,7 @@ builder_describe "Build Keyman common file types module" \ "build" \ "clean" \ "test" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ @@ -35,7 +36,9 @@ builder_parse "$@" if builder_start_action clean; then rm -rf ./build/ ./tsconfig.tsbuildinfo builder_finish_action success clean -else +fi + +function copy_schemas() { # We need the schema files at runtime and bundled, so always copy it for all actions except `clean` mkdir -p "$THIS_SCRIPT_PATH/build/src/" cp "$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard.schema.json" "$THIS_SCRIPT_PATH/build/src/" @@ -54,7 +57,7 @@ else # TODO-LDML: When these are copied, the DOCTYPE will break due to the wrong path. We don't use the DTD so it should be OK. cp "$CLDR_INFO_PATH" "$CLDR_PATH/import/"*.xml "$THIS_SCRIPT_PATH/build/src/import/$CLDR_VER/" done -fi +} #------------------------------------------------------------------------------------------------------------------- @@ -66,6 +69,7 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action build; then + copy_schemas npm run build builder_finish_action success build fi @@ -73,6 +77,7 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action test; then + copy_schemas npm test builder_finish_action success test fi @@ -80,7 +85,13 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish + copy_schemas + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm builder_finish_action success publish +elif builder_start_action pack; then + copy_schemas + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/common/web/types/package.json b/common/web/types/package.json index b1392c77622278b7a4a8f1777ebf7c381092863c..4cff8739a584ae8cd5c583b248a8b109a6bca330 100644 --- a/common/web/types/package.json +++ b/common/web/types/package.json @@ -11,9 +11,13 @@ "exports": { ".": "./build/src/main.js" }, + "files": [ + "/build/src/" + ], "scripts": { "build": "tsc -b", - "test": "cd test && tsc -b && cd .. && c8 --skip-full --reporter=lcov --reporter=text mocha", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --skip-full --reporter=lcov --reporter=text mocha", "prepublishOnly": "npm run build" }, "author": "Marc Durdin (https://github.com/mcdurdin)", diff --git a/common/web/types/src/consts/virtual-key-constants.ts b/common/web/types/src/consts/virtual-key-constants.ts index 199060312e48244381bda12dcf77d39ad0a0130e..b14023f9cd2ec1b8119603cae59eedc3fef73616 100644 --- a/common/web/types/src/consts/virtual-key-constants.ts +++ b/common/web/types/src/consts/virtual-key-constants.ts @@ -110,10 +110,17 @@ export const USVirtualKeyCodes = { K_SLASH:191, K_BKQUOTE:192, K_LBRKT:219, + /** + * == K_OEM_5, 0xDC + */ K_BKSLASH:220, K_RBRKT:221, K_QUOTE:222, - K_oE2:226, // ISO B00, key to right of left shift, not on US keyboard + /** + * ISO B00, key to right of left shift, not on US keyboard, + * 0xE2, K_OEM_102 + */ + K_oE2:226, K_OE2:226, k_oC1:193, // ISO B11, ABNT-2 key to left of right shift, not on US keyboard k_OC1:193, @@ -161,16 +168,42 @@ export const ISOVirtualKeyMap: KeyMap = [ [ k.K_SPACE ], ]; +export const JISVirtualKeyMap: KeyMap = [ + // [Hankaku/Zenkaku] 1 2 3 4 5 6 7 8 9 0 - ^ ¥ [bksp] + [ k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL, k.K_BKSLASH /* YEN */ ], + // [tab] Q W E R T Y U I O P @«`» [ [enter] + [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_BKQUOTE, k.K_LBRKT ], + // [caps] A S D F G H J K L ; : ] [enter] + [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE, k.K_RBRKT ], + // [shift] Z X C V B N M , . / _ [shift] + [ k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH, k.K_oE2 /* ろ */ ], + // space + [ k.K_SPACE ], +]; + +export const ABNT2VirtualKeyMap: KeyMap = [ + // ' 1 2 3 4 5 6 7 8 9 0 - = [bksp] + [ k.K_BKQUOTE, k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL ], + // [tab] Q W E R T Y U I O P ´ [ + [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_LBRKT, k.K_RBRKT ], + // [caps] A S D F G H J K L ç ~ ] [enter] + [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE, k.K_BKSLASH ], + // [shift] \ Z X C V B N M , . ; / [shift] + [ k.K_oE2, k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH, k.k_oC1 /* ABNT2 */ ], + // space + [ k.K_SPACE ], +]; + /** * Map from a hardware constant to a keymap * For the 'key' see constants.layr_list_hardware_map */ export const HardwareToKeymap: Map = new Map( [ - ["us", USVirtualKeyMap], - ["iso", ISOVirtualKeyMap], - //TODO-LDML: jis #8161 - //TODO-LDML: abnt2 #8161 + ["us", USVirtualKeyMap], + ["iso", ISOVirtualKeyMap], + ["jis", JISVirtualKeyMap], + ["abnt2", ABNT2VirtualKeyMap], ] ); diff --git a/common/web/types/src/kmx/kmx-builder.ts b/common/web/types/src/kmx/kmx-builder.ts index 5b41c1d43879ea73563c73ea53b2ec2d1fa53e81..d89c7ba33ce8aaf77772c2f840b5a9ac06fa67fe 100644 --- a/common/web/types/src/kmx/kmx-builder.ts +++ b/common/web/types/src/kmx/kmx-builder.ts @@ -1,68 +1,8 @@ import * as r from 'restructure'; -import { KMXFile, GROUP, KEY, STORE } from './kmx.js'; +import { KMXFile, GROUP, KEY, STORE, BUILDER_COMP_KEYBOARD, BUILDER_COMP_KEYBOARD_KMXPLUSINFO, BUILDER_COMP_STORE, BUILDER_COMP_GROUP, BUILDER_COMP_KEY } from './kmx.js'; import { KMXPlusFile } from './kmx-plus.js'; import KMXPlusBuilder from './kmx-plus-builder/kmx-plus-builder.js'; -// These type-checking structures are here to ensure that -// we match the structures from kmx.ts in the generator -// -// They are used internally when building a .kmx file - -interface BUILDER_COMP_KEYBOARD { - dwIdentifier: number; - dwFileVersion: number; - dwCheckSum: number; - KeyboardID: number; - IsRegistered: number; - version: number; - - cxStoreArray: number; - cxGroupArray: number; - - dpStoreArray: number; - dpGroupArray: number; - - StartGroup_ANSI: number; - StartGroup_Unicode: number; - - dwFlags: number; - - dwHotKey: number; - - dpBitmapOffset: number; - dwBitmapSize: number; -}; - -interface BUILDER_COMP_KEYBOARD_KMXPLUSINFO { - dpKMXPlus: number; - dwKMXPlusSize: number; -}; - -interface BUILDER_COMP_STORE { - dwSystemID: number; - dpName: number; - dpString: number; -}; - -interface BUILDER_COMP_KEY { - Key: number; - _padding: number; - Line: number; - ShiftFlags: number; - dpOutput: number; - dpContext: number; -}; - -interface BUILDER_COMP_GROUP { - dpName: number; - dpKeyArray: number; - dpMatch: number; - dpNoMatch: number; - cxKeyArray: number; - fUsingKeys: number; -}; - - export default class KMXBuilder { file: KMXFile; base_keyboard: number = 0; diff --git a/common/web/types/src/kmx/kmx-file-reader.ts b/common/web/types/src/kmx/kmx-file-reader.ts new file mode 100644 index 0000000000000000000000000000000000000000..46cfc8eb3926288329b739cb9f831af55c4328a9 --- /dev/null +++ b/common/web/types/src/kmx/kmx-file-reader.ts @@ -0,0 +1,44 @@ +import { KMXFile, BUILDER_COMP_KEYBOARD, KEYBOARD, STORE } from "./kmx.js"; +import * as r from 'restructure'; + +export class KmxFileReader { + private readonly rString = new r.String(null, 'utf16le'); + + private readString(source: Uint8Array, offset: number): string { + if(offset == 0) { + // the file has a null string here, which is not the same as an empty + // string ('') + return null; + } + return this.rString.fromBuffer(source.slice(offset)); + } + + public read(source: Uint8Array): KEYBOARD { + let binaryKeyboard: BUILDER_COMP_KEYBOARD; + let kmx = new KMXFile(); + binaryKeyboard = kmx.COMP_KEYBOARD.fromBuffer(source); + if(binaryKeyboard.dwIdentifier != KMXFile.FILEID_COMPILED) { + return null; + } + + // TODO: all header fields + + let result = new KEYBOARD(); + let offset = binaryKeyboard.dpStoreArray; + for(let i = 0; i < binaryKeyboard.cxStoreArray; i++) { + let binaryStore = kmx.COMP_STORE.fromBuffer(source.slice(offset)); + let store = new STORE(); + store.dwSystemID = binaryStore.dwSystemID; + store.dpName = this.readString(source, binaryStore.dpName); + store.dpString = this.readString(source, binaryStore.dpString); + result.stores.push(store); + offset += KMXFile.COMP_STORE_SIZE; + } + + // TODO: groups + + // TODO: KMXPlusFile + + return result; + } +}; \ No newline at end of file diff --git a/common/web/types/src/kmx/kmx.ts b/common/web/types/src/kmx/kmx.ts index 0c7c05aa08c00dd31d899b6112eb412820e02e8a..2f47bddcdd20942eb4cf971e84a625044a8a691c 100644 --- a/common/web/types/src/kmx/kmx.ts +++ b/common/web/types/src/kmx/kmx.ts @@ -9,8 +9,8 @@ import * as r from 'restructure'; export class KEYBOARD { //TODO: additional header fields - groups: GROUP[]; - stores: STORE[]; + groups: GROUP[] = []; + stores: STORE[] = []; }; export class STORE { @@ -35,6 +35,64 @@ export class KEY { dpContext: string; }; +// These type-checking structures are here to ensure that +// we match the structures from kmx.ts in the generator +// +// They are used internally when reading and writing .kmx files + +export interface BUILDER_COMP_KEYBOARD { + dwIdentifier: number; + dwFileVersion: number; + dwCheckSum: number; + KeyboardID: number; + IsRegistered: number; + version: number; + + cxStoreArray: number; + cxGroupArray: number; + + dpStoreArray: number; + dpGroupArray: number; + + StartGroup_ANSI: number; + StartGroup_Unicode: number; + + dwFlags: number; + + dwHotKey: number; + + dpBitmapOffset: number; + dwBitmapSize: number; +}; + +export interface BUILDER_COMP_KEYBOARD_KMXPLUSINFO { + dpKMXPlus: number; + dwKMXPlusSize: number; +}; + +export interface BUILDER_COMP_STORE { + dwSystemID: number; + dpName: number; + dpString: number; +}; + +export interface BUILDER_COMP_KEY { + Key: number; + _padding: number; + Line: number; + ShiftFlags: number; + dpOutput: number; + dpContext: number; +}; + +export interface BUILDER_COMP_GROUP { + dpName: number; + dpKeyArray: number; + dpMatch: number; + dpNoMatch: number; + cxKeyArray: number; + fUsingKeys: number; +}; export class KMXFile { diff --git a/common/web/types/src/kpj/keyman-developer-project.ts b/common/web/types/src/kpj/keyman-developer-project.ts index 898c6f2b3f84f62402a9933537122a84e00cd738..fd4915a228204dd5ebb0d73528ce2483864961b1 100644 --- a/common/web/types/src/kpj/keyman-developer-project.ts +++ b/common/web/types/src/kpj/keyman-developer-project.ts @@ -2,53 +2,53 @@ // Version 1.0 of Keyman Developer Project .kpj file // -import * as path from 'path'; -import * as fs from 'fs'; +import { CompilerCallbacks } from '../util/compiler-interfaces.js'; export class KeymanDeveloperProject { options: KeymanDeveloperProjectOptions; files: KeymanDeveloperProjectFile[]; - constructor(version: KeymanDeveloperProjectVersion) { + projectPath: string = ''; + + constructor(private projectFilename: string, version: KeymanDeveloperProjectVersion, private callbacks: CompilerCallbacks) { + this.projectPath = this.callbacks.path.dirname(this.projectFilename); this.options = new KeymanDeveloperProjectOptions(version); this.files = []; } /** * Adds .kmn, .xml, .kps to project based on options.sourcePath - * @param projectPath Full path to project.kpj (even if the file doesn't exist) + * @param projectFilename Full path to project.kpj (even if the file doesn't exist) */ - populateFiles(projectPath: string) { + populateFiles() { if(this.options.version != '2.0') { throw new Error('populateFiles can only be called on a v2.0 project'); } - - let sourcePath = this.resolveProjectPath(projectPath, this.options.sourcePath); - let files = fs.readdirSync(sourcePath); + let sourcePath = this.resolveProjectPath(this.options.sourcePath); + let files = this.callbacks.fs.readdirSync(sourcePath); for(let filename of files) { - let fullPath = path.join(sourcePath, filename); + let fullPath = this.callbacks.path.join(sourcePath, filename); if(filename.match(/\.xml$/i)) { - if(!fs.readFileSync(fullPath, 'utf-8').match(/ldmlKeyboard\.dtd/)) { + if(!this.callbacks.fs.readFileSync(fullPath, 'utf-8').match(/ldmlKeyboard\.dtd/)) { // Skip this .xml because we assume it isn't really a keyboard .xml continue; } } if(filename.match(/\.(kmn|kps|xml|model\.ts)$/i)) { - let file = new KeymanDeveloperProjectFile20(fullPath); + let file = new KeymanDeveloperProjectFile20(fullPath, this.callbacks); this.files.push(file); } } } - resolveProjectPath(projectPath: string, p: string): string { + private resolveProjectPath(p: string): string { // Replace placeholders in the target path - return p.replace('$PROJECTPATH', path.dirname(projectPath)); + return p.replace('$PROJECTPATH', this.projectPath); } - resolveInputFilePath(projectPath: string, file: KeymanDeveloperProjectFile): string { - let p = path.dirname(projectPath); - return path.normalize(path.resolve(p, file.filePath)); + resolveInputFilePath(file: KeymanDeveloperProjectFile): string { + return this.callbacks.resolveFilename(this.projectFilename, file.filePath); } - resolveOutputFilePath(projectPath: string, file: KeymanDeveloperProjectFile, sourceExt: string, targetExt: string): string { + resolveOutputFilePath(file: KeymanDeveloperProjectFile, sourceExt: string, targetExt: string): string { // Roughly corresponds to Delphi TProject.GetTargetFileName let p = this.options.version == '1.0' ? this.options.buildPath || '$SOURCEPATH' : @@ -58,13 +58,13 @@ export class KeymanDeveloperProject { if(this.options.version == '1.0') { // TODO: do we need to support $VERSION? // $SOURCEPATH only supported in 1.0 projects - p = p.replace('$SOURCEPATH', path.dirname(this.resolveInputFilePath(projectPath, file))); + p = p.replace('$SOURCEPATH', this.callbacks.path.dirname(this.resolveInputFilePath(file))); } - p = this.resolveProjectPath(projectPath, p); + p = this.resolveProjectPath(p); let f = file.filename.replace(new RegExp(`\\${sourceExt}$`, 'i'), targetExt); - return path.normalize(path.join(p, f)); + return this.callbacks.path.normalize(this.callbacks.path.join(p, f)); } }; @@ -130,14 +130,14 @@ export class KeymanDeveloperProjectFile20 { readonly filename: string; readonly filePath: string; readonly fileType: string; // file extension of filename, but .model.ts is technically not the ext because of 2 periods - constructor(filePath: string) { - this.filename = path.basename(filePath); + constructor(filePath: string, private callbacks: CompilerCallbacks) { + this.filename = this.callbacks.path.basename(filePath); this.filePath = filePath; if(this.filename.match(/\.model\.ts$/)) { // .model.ts is a bit of a hassle... this.fileType = '.model.ts'; } else { - this.fileType = path.extname(this.filename); + this.fileType = this.callbacks.path.extname(this.filename); } } }; diff --git a/common/web/types/src/kpj/kpj-file-reader.ts b/common/web/types/src/kpj/kpj-file-reader.ts index f8d27c6a70114f6ffe6b62df8e3c360be1a02be2..5f768c54f1ea159dccd318e1eb997be1d1d5e5c6 100644 --- a/common/web/types/src/kpj/kpj-file-reader.ts +++ b/common/web/types/src/kpj/kpj-file-reader.ts @@ -4,8 +4,13 @@ import { default as AjvModule } from 'ajv'; const Ajv = AjvModule.default; // The actual expected Ajv type. import { boxXmlArray } from '../util/util.js'; import { KeymanDeveloperProject, KeymanDeveloperProjectFile10, KeymanDeveloperProjectType } from './keyman-developer-project.js'; +import { CompilerCallbacks } from '../util/compiler-interfaces.js'; export class KPJFileReader { + constructor(private callbacks: CompilerCallbacks) { + + } + public read(file: Uint8Array): KPJFile { let data: KPJFile; @@ -37,12 +42,12 @@ export class KPJFileReader { return def; } - public transform(projectPath: string, source: KPJFile): KeymanDeveloperProject { + public transform(projectFilename: string, source: KPJFile): KeymanDeveloperProject { // NOTE: at this point, the xml should have been validated // and matched the schema result so we can assume the source // is a valid shape let project = source.KeymanDeveloperProject; - let result: KeymanDeveloperProject = new KeymanDeveloperProject(project.Options?.Version || "1.0"); + let result: KeymanDeveloperProject = new KeymanDeveloperProject(projectFilename, project.Options?.Version || "1.0", this.callbacks); if(result.options.version == '2.0') { result.options.buildPath = (project.Options?.BuildPath || result.options.buildPath).replace(/\\/g, '/'); result.options.sourcePath = (project.Options?.SourcePath || result.options.sourcePath).replace(/\\/g, '/'); @@ -60,7 +65,7 @@ export class KPJFileReader { if(result.options.version == '1.0') { this.transformFilesVersion10(project, result); } else { - result.populateFiles(projectPath); + result.populateFiles(); } return result; diff --git a/common/web/types/src/kvk/kvk-file.ts b/common/web/types/src/kvk/kvk-file.ts index b33117e0f0f2b9ae006643e393cae7dde901e3bd..b3075e5931afea5e6dfaa4295080f36e46bbc4e8 100644 --- a/common/web/types/src/kvk/kvk-file.ts +++ b/common/web/types/src/kvk/kvk-file.ts @@ -5,6 +5,7 @@ import * as r from 'restructure'; // export const BUILDER_KVK_HEADER_IDENTIFIER = 0x464B564B; // 'KVKF', little-endian +export const KVK_HEADER_IDENTIFIER_BYTES = [0x4B, 0x56, 0x4B, 0x46]; export const BUILDER_KVK_HEADER_VERSION = 0x0600; // Keyman 6.0 export interface BUILDER_KVK_STRING { diff --git a/common/web/types/src/kvk/kvks-file-reader.ts b/common/web/types/src/kvk/kvks-file-reader.ts index 1a4c5a8b056646a608b1868125d6d0e4a1d76663..c12b7036e4a3828098dd05ae5f99536d9e081783 100644 --- a/common/web/types/src/kvk/kvks-file-reader.ts +++ b/common/web/types/src/kvk/kvks-file-reader.ts @@ -35,10 +35,17 @@ export default class KVKSFileReader { parser.parseString(file, (e: unknown, r: unknown) => { source = r as KVKSourceFile }); source = this.boxArrays(source); + this.cleanupFlags(source); this.cleanupUnderscore('visualkeyboard', source.visualkeyboard); return source; } + private cleanupFlags(source: any) { + if(source.visualkeyboard?.header?.flags === '') { + source.visualkeyboard.header.flags = {}; + } + } + /** * The only element that allows spaces is . Remove * all other empty whitespace-only values. diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts index ac470f8589ab21b9eec9429224ea1135b17e8141..35d7f627956417b508e9b358473ce019147afc80 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts @@ -24,8 +24,7 @@ export default class LDMLKeyboardXMLSourceFileReader { readImportFile(version: string, subpath: string): Buffer { // TODO-LDML: sanitize input string let importPath = new URL(`../import/${version}/${subpath}`, import.meta.url); - // TODO-LDML: support baseFileName? - return this.callbacks.loadFile(importPath.pathname, importPath); + return this.callbacks.loadFile(importPath); } /** @@ -69,12 +68,20 @@ export default class LDMLKeyboardXMLSourceFileReader { boxXmlArray(flicks, 'flick'); } } + if(source?.keyboard?.variables) { + boxXmlArray(source?.keyboard?.variables, 'set'); + boxXmlArray(source?.keyboard?.variables, 'string'); + boxXmlArray(source?.keyboard?.variables, 'unicodeSet'); + } if(source?.keyboard?.transforms) { - for(let transform of source.keyboard.transforms) { - boxXmlArray(transform, 'transform'); + for(let transforms of source.keyboard.transforms) { + boxXmlArray(transforms, 'transformGroup'); + for (let transformGroup of transforms.transformGroup) { + boxXmlArray(transformGroup, 'transform'); + boxXmlArray(transformGroup, 'reorder'); + } } } - boxXmlArray(source?.keyboard?.reorders, 'reorder'); return this.boxImportsAndSpecials(source, 'keyboard'); } diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts index f3d20402e7f588134330a68d0b99a3748f690f14..4e7755038403dc3b57ceecc96d2dbd9470a19aa9 100644 --- a/common/web/types/src/main.ts +++ b/common/web/types/src/main.ts @@ -2,12 +2,15 @@ export * as KMX from './kmx/kmx.js'; export * as KMXPlus from './kmx/kmx-plus.js'; export { default as KMXBuilder } from './kmx/kmx-builder.js'; +export { KmxFileReader } from './kmx/kmx-file-reader.js'; export * as VisualKeyboard from './kvk/visual-keyboard.js'; export { default as KMXPlusBuilder} from './kmx/kmx-plus-builder/kmx-plus-builder.js'; export { default as KvkFileReader } from './kvk/kvk-file-reader.js'; export { default as KvksFileReader } from './kvk/kvks-file-reader.js'; export { default as KvkFileWriter } from './kvk/kvk-file-writer.js'; +export * as KvkFile from './kvk/kvk-file.js'; +export * as KvksFile from './kvk/kvk-file.js'; export * as LDMLKeyboard from './ldml-keyboard/ldml-keyboard-xml.js'; export { LDMLKeyboardTestDataXMLSourceFile } from './ldml-keyboard/ldml-keyboard-testdata-xml.js'; @@ -15,7 +18,7 @@ export { default as LDMLKeyboardXMLSourceFileReader } from './ldml-keyboard/ldml export * as Constants from './consts/virtual-key-constants.js'; -export { CompilerCallbacks, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec } from './util/compiler-interfaces.js'; +export { CompilerCallbacks, CompilerSchema, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerMessageSpec, compilerErrorSeverityName, compilerExceptionToString, compilerErrorFormatCode } from './util/compiler-interfaces.js'; export { CommonTypesMessages } from './util/common-events.js'; export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js'; @@ -25,3 +28,9 @@ export { TouchLayoutFileWriter, TouchLayoutFileWriterOptions } from './keyman-to export * as KPJ from './kpj/kpj-file.js'; export { KPJFileReader } from './kpj/kpj-file-reader.js'; export { KeymanDeveloperProject } from './kpj/keyman-developer-project.js'; + +export * as KpsFile from './package/kps-file.js'; +export * as KmpJsonFile from './package/kmp-json-file.js'; + +export * as util from './util/util.js'; + diff --git a/developer/src/kmc-package/src/kmp-json-file.ts b/common/web/types/src/package/kmp-json-file.ts similarity index 100% rename from developer/src/kmc-package/src/kmp-json-file.ts rename to common/web/types/src/package/kmp-json-file.ts diff --git a/developer/src/kmc-package/src/kps-file.ts b/common/web/types/src/package/kps-file.ts similarity index 94% rename from developer/src/kmc-package/src/kps-file.ts rename to common/web/types/src/package/kps-file.ts index 8d917e377077f60f0b9c7a324ee065d5079a8877..3e9ff53bc443963e653241973cd64b92bb7a74f0 100644 --- a/developer/src/kmc-package/src/kps-file.ts +++ b/common/web/types/src/package/kps-file.ts @@ -89,8 +89,8 @@ export interface KpsFileLanguage { } export interface KpsFileKeyboard { - name: string; - iD: string; + name: string; /// the descriptive name of the keyboard + iD: string; /// the keyboard identifier, equal to the basename of the keyboard file sans extension version: string; oSKFont?: string; displayFont?: string; diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts index 8375e0ea43ffc873f03ab7b40d9af3f7af5da23c..849e945321fb5b811f2c81b9b05b7d5d74455381 100644 --- a/common/web/types/src/util/compiler-interfaces.ts +++ b/common/web/types/src/util/compiler-interfaces.ts @@ -2,6 +2,8 @@ * Abstract interface for compiler error and warning messages */ export interface CompilerEvent { + filename?: string; + line?: number; code: number; message: string; }; @@ -17,32 +19,128 @@ export enum CompilerErrorSeverity { Error_Mask = 0x0FFFFF, }; +export function compilerErrorSeverityName(code: number): string { + let severity = code & CompilerErrorSeverity.Severity_Mask; + switch(severity) { + case CompilerErrorSeverity.Info: return 'INFO'; + case CompilerErrorSeverity.Hint: return 'HINT'; + case CompilerErrorSeverity.Warn: return 'WARN'; + case CompilerErrorSeverity.Error: return 'ERROR'; + case CompilerErrorSeverity.Fatal: return 'FATAL'; + /* istanbul ignore next */ + default: return 'UNKNOWN'; + } +} + +/** + * Format the error code number + * example: "FATAL:0x03004" + */ +export function compilerErrorFormatCode(code: number): string { + const severity = code & CompilerErrorSeverity.Severity_Mask; + const severityName = compilerErrorSeverityName(severity); + const errorCode = code & CompilerErrorSeverity.Error_Mask; + const errorCodeString = Number(errorCode).toString(16).padStart(5,'0'); + return `${severityName}:0x${errorCodeString}`; +} + +/** + * Defines the error code ranges for various compilers. Once defined, these + * ranges must not be changed as external modules may depend on specific error + * codes. Individual errors are defined at a compiler level, for example, + * kmc-ldml/src/compiler/messages.ts. + */ export enum CompilerErrorNamespace { /** - * kmc-keyboard errors between 0x0000…0x0FFF + * kmc-ldml errors between 0x0000…0x0FFF */ - KeyboardCompiler = 0x0000, + LdmlKeyboardCompiler = 0x0000, /** * common/web/types errors between 0x1000…0x1FFF */ CommonTypes = 0x1000, + /** + * kmc-kmn errors between 0x2000…0x2FFF; these map to + * the base codes found in kmn_compiler_errors.h, exclusive severity flags + */ + KmnCompiler = 0x2000, + /** + * kmc-model errors between 0x3000…0x3FFF + */ + ModelCompiler = 0x3000, + /** + * kmc-package errors between 0x4000…0x4FFF + */ + PackageCompiler = 0x4000, + /** + * kmc and related infrastructure errors between 0x5000…0x5FFF; + */ + Infrastructure = 0x5000, }; +export type CompilerSchema = + 'ldml-keyboard' | + 'ldml-keyboardtest' | + 'kvks' | + 'kpj'; + // | 'keyman-touch-layout.clean'; TODO this has the wrong name pattern, .spec.json instead of .schema.json + +/** + * A mapping for common path operations, maps to Node path module. This only + * defines the functions we are actually using, so that we can port more easily + * between different systems. + */ +export interface CompilerPathCallbacks { + dirname(name: string): string; + extname(name: string): string; + basename(name: string, ext?: string): string; + isAbsolute(name: string): boolean; + join(...paths: string[]): string; + normalize(p: string): string; +} + +/** + * A mapping for common filesystem operations, maps to Node fs module. This only + * defines the functions we are actually using, so that we can port more easily + * between different systems. + */ +export interface CompilerFileSystemCallbacks { + readdirSync(name: string): string[]; + readFileSync(path: string, options?: { encoding?: null; flag?: string; } | null): Uint8Array; + readFileSync(path: string, options: { encoding: string; flag?: string; } | string): string; + readFileSync(path: string, options?: { encoding?: string | null; flag?: string; } | string | null): string | Uint8Array; + writeFileSync(path: string, data: Uint8Array): void; + + existsSync(name: string): boolean; +} + /** * Abstract interface for callbacks, to abstract out file i/o */ export interface CompilerCallbacks { /** * Attempt to load a file. Return falsy if not found. + * TODO: accept only string + * TODO: never return falsy, just throw if not found? + * TODO: Buffer is Node-only. + * @param baseFilename + * @param filename + */ + loadFile(filename: string | URL): Buffer; + + get path(): CompilerPathCallbacks; + get fs(): CompilerFileSystemCallbacks; + + /** + * Resolves a file path relative to the baseFilename * @param baseFilename * @param filename */ - loadFile(baseFilename: string, filename: string | URL): Buffer; - loadLdmlKeyboardSchema(): Buffer; - loadLdmlKeyboardTestSchema(): Buffer; + resolveFilename(baseFilename: string, filename: string): string; + + loadSchema(schema: CompilerSchema): Buffer; reportMessage(event: CompilerEvent): void; - loadKvksJsonSchema(): Buffer; - loadKpjJsonSchema(): Buffer; + debug(msg: string): void; }; /** @@ -52,3 +150,10 @@ export interface CompilerCallbacks { * @returns */ export const CompilerMessageSpec = (code: number, message: string) : CompilerEvent => { return { code, message } }; + +/** + * @param e Error-like + */ +export function compilerExceptionToString(e?: any) : string { + return `${(e ?? 'unknown error').toString()}\n\nCall stack:\n${(e instanceof Error ? e.stack : (new Error()).stack)}`; +} diff --git a/common/web/types/test/fixtures/kmx/khmer_angkor.kmx b/common/web/types/test/fixtures/kmx/khmer_angkor.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d8495d187c4ea369dfa0b608d8a630e196360f16 Binary files /dev/null and b/common/web/types/test/fixtures/kmx/khmer_angkor.kmx differ diff --git a/common/web/types/test/helpers/TestCompilerCallbacks.ts b/common/web/types/test/helpers/TestCompilerCallbacks.ts new file mode 100644 index 0000000000000000000000000000000000000000..596a731d58880a1fbf4d7dab95a81c795bf212e1 --- /dev/null +++ b/common/web/types/test/helpers/TestCompilerCallbacks.ts @@ -0,0 +1,62 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { loadFile, loadSchema, resolveFilename } from '../helpers/index.js'; +import { CompilerCallbacks, CompilerEvent, CompilerFileSystemCallbacks, CompilerPathCallbacks, CompilerSchema } from '../../src/util/compiler-interfaces.js'; + +// This is related to developer/src/common/web/test-helpers/index.ts but has a slightly different API surface +// as this runs at a lower level than the compiler. +/** + * A CompilerCallbacks implementation for testing + */ +export class TestCompilerCallbacks implements CompilerCallbacks { + loadSchema(schema: CompilerSchema): Buffer { + switch (schema) { + case 'kpj': + throw new Error('loadKpjJsonSchema not implemented.'); // not needed for this test + case 'kvks': + throw new Error('loadKvksJsonSchema not implemented.'); + case 'ldml-keyboard': + return loadSchema(schema); + case 'ldml-keyboardtest': + return loadSchema(schema); + } + } + clear() { + this.messages = []; + } + debug(msg: string): void { + console.debug(msg); + } + + messages: CompilerEvent[] = []; + + get path(): CompilerPathCallbacks { + return path; + } + + get fs(): CompilerFileSystemCallbacks { + return fs; + } + + resolveFilename(baseFilename: string, filename: string): string { + return resolveFilename(baseFilename, filename); + } + + loadFile(filename: string | URL): Buffer { + // TODO: error management, does it belong here? + try { + return loadFile(filename); + } catch (e) { + if (e.code === 'ENOENT') { + return null; + } else { + throw e; + } + } + } + + reportMessage(event: CompilerEvent): void { + // console.log(event.message); + this.messages.push(event); + } +} diff --git a/common/web/types/test/helpers/index.ts b/common/web/types/test/helpers/index.ts index a6848021ef7b0d51b89e45b0015200282619a3b8..58dc32dc9c1618da13acb027d39ad3a379a53394 100644 --- a/common/web/types/test/helpers/index.ts +++ b/common/web/types/test/helpers/index.ts @@ -1,6 +1,7 @@ import path from "path"; import fs from "fs"; import { fileURLToPath } from "url"; +import { CompilerSchema } from "../../src/util/compiler-interfaces.js"; /** * Builds a path to the fixture with the given path components. @@ -13,27 +14,31 @@ export function makePathToFixture(...components: string[]): string { return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url)); } -export function loadKvksJsonSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'kvks.schema.json'), import.meta.url)); -} - export function loadKeymanTouchLayoutCleanJsonSchema(): Buffer { + // TODO: this has the Wrong Name Pattern! return fs.readFileSync(new URL(path.join('..', '..', 'src', 'keyman-touch-layout.clean.spec.json'), import.meta.url)); } -export function loadFile(baseFilename: string, filename: string | URL): Buffer { - // TODO: translate filename based on the baseFilename +export function loadFile(filename: string | URL): Buffer { return fs.readFileSync(filename); } -export function loadLdmlKeyboardSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'ldml-keyboard.schema.json'), import.meta.url)); -} - -export function loadLdmlKeyboardTestDataSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'ldml-keyboardtest.schema.json'), import.meta.url)); +export function loadSchema(schema: CompilerSchema): Buffer { + return fs.readFileSync(new URL(path.join('..', '..', 'src', schema + '.schema.json'), import.meta.url)); } -export function loadKpjJsonSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'kpj.schema.json'), import.meta.url)); +export function resolveFilename(baseFilename: string, filename: string) { + const basePath = path.dirname(baseFilename); + // Transform separators to platform separators -- we are agnostic + // in our use here but path prefers files may use + // either / or \, although older kps files were always \. + if(path.sep == '/') { + filename = filename.replace(/\\/g, '/'); + } else { + filename = filename.replace(/\//g, '\\'); + } + if(!path.isAbsolute(filename)) { + filename = path.resolve(basePath, filename); + } + return filename; } diff --git a/common/web/types/test/helpers/reader-callback-test.ts b/common/web/types/test/helpers/reader-callback-test.ts index 983db078f60f8254cab3f20ea79b7989f055724b..8c85a759acf270a8a99e5eb947faf8b51db8bb93 100644 --- a/common/web/types/test/helpers/reader-callback-test.ts +++ b/common/web/types/test/helpers/reader-callback-test.ts @@ -1,50 +1,11 @@ import 'mocha'; import {assert} from 'chai'; -import {loadLdmlKeyboardSchema, loadFile, makePathToFixture, loadLdmlKeyboardTestDataSchema} from '../helpers/index.js'; +import { loadFile, makePathToFixture, loadSchema } from '../helpers/index.js'; import LDMLKeyboardXMLSourceFileReader from '../../src/ldml-keyboard/ldml-keyboard-xml-reader.js'; -import { CompilerCallbacks, CompilerEvent } from '../../src/util/compiler-interfaces.js'; +import { CompilerEvent } from '../../src/util/compiler-interfaces.js'; import { LDMLKeyboardXMLSourceFile } from '../../src/ldml-keyboard/ldml-keyboard-xml.js'; import { LDMLKeyboardTestDataXMLSourceFile } from '../ldml-keyboard/ldml-keyboard-testdata-xml.js'; - -// This is related to developer/src/kmc-keyboard/test/helpers/index.ts but has a slightly different API surface -// as this runs at a lower level than the compiler. - -/** - * A CompilerCallbacks implementation for testing - */ -class TestCompilerCallbacks implements CompilerCallbacks { - loadKpjJsonSchema(): Buffer { - throw new Error('loadKpjJsonSchema not implemented.'); // not needed for this test - } - loadLdmlKeyboardTestSchema(): Buffer { - return loadLdmlKeyboardTestDataSchema(); - } - loadLdmlKeyboardSchema(): Buffer { - return loadLdmlKeyboardSchema(); - } - loadKvksJsonSchema(): Buffer { - throw new Error('loadKvksJsonSchema not implemented.'); - } - clear() { - this.messages = []; - } - messages: CompilerEvent[] = []; - loadFile(baseFilename: string, filename: string | URL): Buffer { - try { - return loadFile(baseFilename, filename); - } catch(e) { - if (e.code === 'ENOENT') { - return null; - } else { - throw e; - } - } - } - reportMessage(event: CompilerEvent): void { - // console.log(event.message); - this.messages.push(event); - } -} +import { TestCompilerCallbacks } from './TestCompilerCallbacks.js'; export interface CompilationCase { /** @@ -118,7 +79,7 @@ export function testReaderCases(cases : CompilationCase[]) { it(testHeading, function () { callbacks.clear(); - const data = loadFile(testcase.subpath, makePathToFixture(testcase.subpath)); + const data = loadFile(makePathToFixture(testcase.subpath)); assert.ok(data, `reading ${testcase.subpath}`); const source = reader.load(data); if (!testcase.loadfail) { @@ -128,9 +89,9 @@ export function testReaderCases(cases : CompilationCase[]) { } // special case for an expected exception if (testcase.throws) { - assert.throws(() => reader.validate(source, loadLdmlKeyboardSchema()), testcase.throws); + assert.throws(() => reader.validate(source, loadSchema('ldml-keyboard')), testcase.throws); } else { - assert.doesNotThrow(() => reader.validate(source, loadLdmlKeyboardSchema()), `validating ${testcase.subpath}`); + assert.doesNotThrow(() => reader.validate(source, loadSchema('ldml-keyboard')), `validating ${testcase.subpath}`); // if we expected errors or warnings, show them if (testcase.errors) { assert.includeDeepMembers(callbacks.messages, testcase.errors, 'expected errors to be included'); @@ -169,7 +130,7 @@ export function testTestdataReaderCases(cases : TestDataCase[]) { it(testHeading, function () { callbacks.clear(); - const data = loadFile(testcase.subpath, makePathToFixture(testcase.subpath)); + const data = loadFile(makePathToFixture(testcase.subpath)); assert.ok(data, `reading ${testcase.subpath}`); const source = reader.loadTestData(data); if (!testcase.loadfail) { diff --git a/common/web/types/test/kmx/test-kmx-file.ts b/common/web/types/test/kmx/test-kmx-file.ts new file mode 100644 index 0000000000000000000000000000000000000000..b50521427c6f06cc5fd4c58d2e53dc0d1e9c8c7a --- /dev/null +++ b/common/web/types/test/kmx/test-kmx-file.ts @@ -0,0 +1,29 @@ +import * as fs from 'fs'; +import 'mocha'; +import { assert } from 'chai'; +import { makePathToFixture } from '../helpers/index.js'; +import { KmxFileReader } from "../../src/kmx/kmx-file-reader.js"; +import { KMXFile } from "../../src/kmx/kmx.js"; + +describe('kmx-file-reader', function () { + it('should read a valid file', function() { + const path = makePathToFixture('kmx', 'khmer_angkor.kmx'); + const input = fs.readFileSync(path); + const reader = new KmxFileReader(); + const kmx = reader.read(input); + + let store = kmx.stores.find(store => store.dwSystemID == KMXFile.TSS_VERSION); + assert.isObject(store); + assert.equal(store.dpString, '10.0'); + + store = kmx.stores.find(store => store.dwSystemID == KMXFile.TSS_NAME); + assert.isObject(store); + assert.equal(store.dpString, 'Khmer Angkor'); + + store = kmx.stores.find(store => store.dwSystemID == KMXFile.TSS_KEYBOARDVERSION); + assert.isObject(store); + assert.equal(store.dpString, '1.3'); + + // TODO: add header, group, key tests once we have added support in KmxFileReader + }); +}); diff --git a/common/web/types/test/kpj/test-kpj-file-reader.ts b/common/web/types/test/kpj/test-kpj-file-reader.ts index 988d2977d3462fafa7afdeeca7305b3885f28772..b7c2f710131db0d9bdf8be3f9190a267ee4a2ad5 100644 --- a/common/web/types/test/kpj/test-kpj-file-reader.ts +++ b/common/web/types/test/kpj/test-kpj-file-reader.ts @@ -1,20 +1,23 @@ import * as fs from 'fs'; import 'mocha'; import {assert} from 'chai'; -import { loadKpjJsonSchema, makePathToFixture } from '../helpers/index.js'; +import { loadSchema, makePathToFixture } from '../helpers/index.js'; import { KPJFileReader } from "../../src/kpj/kpj-file-reader.js"; import { KeymanDeveloperProjectFile10, KeymanDeveloperProjectType } from '../../src/kpj/keyman-developer-project.js'; +import { TestCompilerCallbacks } from '../helpers/TestCompilerCallbacks.js'; + +const callbacks = new TestCompilerCallbacks(); describe('kpj-file-reader', function () { it('kpj-file-reader should read a valid file', function() { const kpjPath = 'khmer_angkor.kpj'; const path = makePathToFixture('kpj', kpjPath); const input = fs.readFileSync(path); - const reader = new KPJFileReader(); + const reader = new KPJFileReader(callbacks); const kpj = reader.read(input); console.dir(kpj); assert.doesNotThrow(() => { - reader.validate(kpj, loadKpjJsonSchema()); + reader.validate(kpj, loadSchema('kpj')); }); assert.equal(kpj.KeymanDeveloperProject.Options.BuildPath, '$PROJECTPATH\\build'); assert.equal(kpj.KeymanDeveloperProject.Options.CheckFilenameConventions, 'False'); diff --git a/common/web/types/test/kvk/test-kvk-round-trip.ts b/common/web/types/test/kvk/test-kvk-round-trip.ts index 487be68c1d6f78dd1df43d1ffa4aa23fbc820138..41dbe2dbc0cb25b553c790172998f006269c416b 100644 --- a/common/web/types/test/kvk/test-kvk-round-trip.ts +++ b/common/web/types/test/kvk/test-kvk-round-trip.ts @@ -4,7 +4,7 @@ import {assert} from 'chai'; import Hexy from 'hexy'; import gitDiff from 'git-diff'; const { hexy } = Hexy; -import { loadKvksJsonSchema, makePathToFixture } from '../helpers/index.js'; +import { loadSchema, makePathToFixture } from '../helpers/index.js'; import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js"; import KvkFileReader from "../../src/kvk/kvk-file-reader.js"; import KvkFileWriter from "../../src/kvk/kvk-file-writer.js"; @@ -51,7 +51,7 @@ describe('kvks-file-reader', function () { const reader = new KvksFileReader(); const kvks = reader.read(input); assert.doesNotThrow(() => { - reader.validate(kvks, loadKvksJsonSchema()); + reader.validate(kvks, loadSchema('kvks')); }); const errors: KVKSParseError[] = []; const vk = reader.transform(kvks, errors); @@ -74,7 +74,7 @@ describe('kvks-file-reader', function () { // Now, re-read kvk from the kvks const kvks = kvksReader.read(Buffer.from(kvksOut)); assert.doesNotThrow(() => { - kvksReader.validate(kvks, loadKvksJsonSchema()); + kvksReader.validate(kvks, loadSchema('kvks')); }); const errors: KVKSParseError[] = []; const vk2 = kvksReader.transform(kvks, errors); diff --git a/common/web/types/test/kvk/test-kvks-file.ts b/common/web/types/test/kvk/test-kvks-file.ts index e776a0a3613e741b03c8f22447cbc4c2b0214280..523bfaf54d618ce21da9f9137c67d9b04c39bd6a 100644 --- a/common/web/types/test/kvk/test-kvks-file.ts +++ b/common/web/types/test/kvk/test-kvks-file.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import 'mocha'; -import { loadKvksJsonSchema, makePathToFixture } from '../helpers/index.js'; +import { loadSchema, makePathToFixture } from '../helpers/index.js'; import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js"; import KvksFileWriter from "../../src/kvk/kvks-file-writer.js"; import { verify_khmer_angkor } from './test-kvk-utils.js'; @@ -14,7 +14,7 @@ describe('kvks-file-reader', function() { const reader = new KvksFileReader(); const kvks = reader.read(input); assert.doesNotThrow(() => { - reader.validate(kvks, loadKvksJsonSchema()); + reader.validate(kvks, loadSchema('kvks')); }); const errors: KVKSParseError[] = []; const vk = reader.transform(kvks, errors); diff --git a/common/windows/build.sh b/common/windows/build.sh index 480fc953774d20a75d13b89573f399f4aff964f3..65db064c5b202a395dfd31775d30151bbf2f3866 100755 --- a/common/windows/build.sh +++ b/common/windows/build.sh @@ -1,17 +1,15 @@ #!/usr/bin/env bash -# -# Compiles and tests the common Windows modules -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE +if [[ $BUILDER_OS != windows ]]; then + builder_echo grey "Platform is not Windows; skipping common/windows" + exit 0 +fi + # # TODO: when we have windows-specific tests, add them here # as child modules diff --git a/common/windows/cpp/include/.gitignore b/common/windows/cpp/include/.gitignore deleted file mode 100644 index 69ffc835cd58b37bb2f58058d770d2cf77c5ca15..0000000000000000000000000000000000000000 --- a/common/windows/cpp/include/.gitignore +++ /dev/null @@ -1 +0,0 @@ -keymanversion_build.h diff --git a/common/windows/cpp/include/keymansentry.h b/common/windows/cpp/include/keymansentry.h index bb65c37b5eb991439f8a12e7c4fadd78f93e40ac..4bc3c9e4cf5952239130af244453865897e457e5 100644 --- a/common/windows/cpp/include/keymansentry.h +++ b/common/windows/cpp/include/keymansentry.h @@ -1,5 +1,7 @@ #pragma once +#include + /* * Map of Sentry levels for events and breadcrumbs. */ @@ -26,7 +28,7 @@ int keyman_sentry_main(bool is_keyman_developer, const char *logger, int argc, c int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc, wchar_t *argv[], int(*run)(int, wchar_t**)); void keyman_sentry_setexceptionfilter(); -void keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack = false); +sentry_uuid_t keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack = false); void keyman_sentry_report_start(); // @@ -37,3 +39,4 @@ void keyman_sentry_report_start(); // d) privacy options are correctly checked // void keyman_sentry_test_crash(); +void keyman_sentry_test_message(); diff --git a/common/windows/cpp/include/keymanversion.h b/common/windows/cpp/include/keymanversion.h index a35421a2de09f5b7a3b2482dcc21b8f1524ee42b..e9d39defba2bbecd564c89f38ee0b980eb87f67c 100644 --- a/common/windows/cpp/include/keymanversion.h +++ b/common/windows/cpp/include/keymanversion.h @@ -1,7 +1,7 @@ #ifndef _KEYMANVERSION_H #define _KEYMANVERSION_H -#include "keymanversion_build.h" +#include "../../../include/keymanversion_build.h" // We should not need to add to these hard-coded version numbers for future versions, unless // we have a back-compat need to refer to a specific version. diff --git a/common/windows/cpp/include/legacy_kmx_file.h b/common/windows/cpp/include/legacy_kmx_file.h index eddbf233411cc068555dfa469651f1efbf619754..ae18543312f2303575ac276e1521c7bff9c20eb7 100644 --- a/common/windows/cpp/include/legacy_kmx_file.h +++ b/common/windows/cpp/include/legacy_kmx_file.h @@ -346,6 +346,7 @@ static_assert(sizeof(COMP_STORE) == KEYBOARDFILESTORE_SIZE, "COMP_STORE must be struct COMP_KEY { WORD Key; + WORD _reserved; DWORD Line; DWORD ShiftFlags; DWORD dpOutput; @@ -404,9 +405,18 @@ typedef COMP_STORE *PCOMP_STORE; typedef COMP_KEY *PCOMP_KEY; typedef COMP_GROUP *PCOMP_GROUP; + +typedef struct _COMPILER_OPTIONS { + DWORD dwSize; + BOOL ShouldAddCompilerVersion; + BOOL UseKmcmpLib; +} COMPILER_OPTIONS; + +typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS; + typedef int (CALLBACK *CompilerMessageProc)(int line, DWORD dwMsgCode, LPSTR szText); -extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL FSaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc); // I4865 // I4866 +//extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL FSaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc); // I4865 // I4866 #endif // _COMPILER_H diff --git a/common/windows/cpp/include/registry.h b/common/windows/cpp/include/registry.h index 4e2fe9ae35ba8bfe0e22e3597e83841e0ad32d6f..895f9e83becbbd3297c3375c5dcec036fd41d4b7 100644 --- a/common/windows/cpp/include/registry.h +++ b/common/windows/cpp/include/registry.h @@ -129,9 +129,6 @@ #define REGSZ_Flag_UseCachedHotkeyModifierState "Flag_UseCachedHotkeyModifierState" -/* REGSZ_Flag_UseKeymanCore DWORD: Turns on the common core - instead of windows core */ -#define REGSZ_Flag_UseKeymanCore "Flag_UseKeymanCore" - /* DWORD: Enable/disable deep TSF integration, default enabled; 0 = disabled, 1 = enabled, 2 = default */ #define REGSZ_DeepTSFIntegration "deep tsf integration" diff --git a/common/windows/cpp/include/vkeys.h b/common/windows/cpp/include/vkeys.h index 47a1daf3011df1e9d23adf6b1cc894d63494f6f0..6aa71cc497c0ce9eb0aaedf00737f632864509ed 100644 --- a/common/windows/cpp/include/vkeys.h +++ b/common/windows/cpp/include/vkeys.h @@ -1,4 +1,12 @@ #pragma once -extern PCWSTR VKeyNames[256]; -extern PCWSTR VKeyISO9995Names[256]; +#ifdef USE_CHAR16_T +#include +#define VKEY_CHAR KMX_UCHAR +#else +#define VKEY_CHAR WCHAR +#define lpuch(x) L ## x +#endif + +extern const VKEY_CHAR* VKeyNames[256]; +extern const VKEY_CHAR* VKeyISO9995Names[256]; diff --git a/common/windows/cpp/src/keymansentry.cpp b/common/windows/cpp/src/keymansentry.cpp index ce287723e1cfcfe4e77d0faeeb8cd2aee431a8cc..71f640a4a5b5b3294ee4fd3d5484d5a293a0bd2d 100644 --- a/common/windows/cpp/src/keymansentry.cpp +++ b/common/windows/cpp/src/keymansentry.cpp @@ -32,7 +32,7 @@ int keyman_sentry_init(bool is_keyman_developer, const char *logger) { // Set the sentry-db-directory to a writeable location - char szPath[MAX_PATH + 64]; // sufficient length for sentry-0.4.9-db etc + char szPath[MAX_PATH + 64]; // sufficient length for sentry-0.6.0-db etc LPITEMIDLIST pidl; if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_LOCAL_APPDATA, NULL, 0, &pidl))) { @@ -43,7 +43,7 @@ int keyman_sentry_init(bool is_keyman_developer, const char *logger) { *p = 0; } - strcat_s(szPath, "sentry-0.4.9-db"); + strcat_s(szPath, "sentry-" SENTRY_SDK_VERSION "-db"); sentry_options_set_database_path(options, szPath); } ILFree(pidl); @@ -132,7 +132,7 @@ sentry_value_t CaptureStackTrace(PVOID TopAddr, DWORD FramesToSkip) { void keyman_sentry_report_exception(DWORD ExceptionCode, PVOID ExceptionAddress) { sentry_value_t event; const int FRAMES_TO_SKIP = 0; - + sentry_uuid_t uuid = { 0 }; char message[64]; wsprintfA(message, "Exception %x at %p", (unsigned int) ExceptionCode, ExceptionAddress); @@ -159,18 +159,22 @@ void keyman_sentry_report_exception(DWORD ExceptionCode, PVOID ExceptionAddress) sentry_value_set_by_key(event, "threads", threads); } - sentry_capture_event(event); + uuid = sentry_capture_event(event); } fputs(message, stderr); fputs("\n", stderr); if(g_report_exceptions) { fputs("This error has been automatically reported to the Keyman team.\n", stderr); + char uuid_string[37]; + sentry_uuid_as_string(&uuid, uuid_string); + fprintf(stderr, "Sentry uuid: %s\n", uuid_string); } } -void keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack) { +sentry_uuid_t keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack) { const int FRAMES_TO_SKIP = 0; + sentry_uuid_t uuid = { 0 }; if ((g_report_exceptions && (level == SENTRY_LEVEL_ERROR || level == SENTRY_LEVEL_DEBUG)) || g_report_messages) { sentry_value_t event; @@ -190,12 +194,15 @@ void keyman_sentry_report_message(keyman_sentry_level_t level, const char *messa } } - sentry_capture_event(event); + uuid = sentry_capture_event(event); } + + return uuid; } /* Wrappers for main, wmain */ +#define SENTRY_USE_LOCAL_FILTER #ifdef SENTRY_USE_LOCAL_FILTER LPTOP_LEVEL_EXCEPTION_FILTER LastFilter; @@ -236,6 +243,11 @@ int keyman_sentry_main(bool is_keyman_developer, const char *logger, int argc, c keyman_sentry_test_crash(); } + if (argc > 1 && !strcmp(argv[1], "-sentry-client-test-message")) { + // Undocumented test parameter + keyman_sentry_test_message(); + } + int res = run(argc, argv); keyman_sentry_shutdown(); @@ -253,6 +265,11 @@ int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc, keyman_sentry_test_crash(); } + if (argc > 1 && !wcscmp(argv[1], L"-sentry-client-test-message")) { + // Undocumented test parameter + keyman_sentry_test_message(); + } + int res = run(argc, argv); keyman_sentry_shutdown(); @@ -260,9 +277,19 @@ int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc, return res; } +void keyman_sentry_test_message() { + fputs("Testing Sentry reporting:\n", stderr); + auto uuid = keyman_sentry_report_message(KEYMAN_SENTRY_LEVEL_INFO, "Testing Sentry message reporting", true); + char uuid_string[37]; + sentry_uuid_as_string(&uuid, uuid_string); + fprintf(stderr, "Sentry uuid: %s\n", uuid_string); + exit(0); +} void keyman_sentry_test_crash() { - fputs("Testing exception reporting:\n", stderr); + fputs("Testing Sentry exception reporting:\n", stderr); RaiseException(0x0EA0BEEF, EXCEPTION_NONCONTINUABLE, 0, NULL); + fputs("Should not have gotten here\n", stderr); + exit(1); } /* Delay load sentry.dll from our subdirectory: #5166 */ @@ -274,12 +301,12 @@ void keyman_sentry_test_crash() { #else #define SENTRY_DLL SENTRY_BASE_DLL #endif -#define SENTRY_INSTALL_PATH "sentry-0.4.9\\" SENTRY_DLL +#define SENTRY_INSTALL_PATH "sentry-" SENTRY_SDK_VERSION "\\" SENTRY_DLL #define SENTRY_DEV_PATH "common\\windows\\delphi\\ext\\sentry\\" SENTRY_DLL #define ENV_KEYMAN_ROOT "KEYMAN_ROOT" HMODULE LoadSentryLibrary() { - //MAX_PATH + 64 chars leaves space for "\sentry-0.4.9\sentry.x64.dll" + //MAX_PATH + 64 chars leaves space for "\sentry-0.6.0\sentry.x64.dll" char buf[MAX_PATH + 64], keyman_root[MAX_PATH + 64]; int nsize = GetModuleFileNameA(0, buf, MAX_PATH); diff --git a/common/windows/cpp/src/vkeys.cpp b/common/windows/cpp/src/vkeys.cpp index 4b4710baa9f54db208a6680776bb406496141d15..5bf249b3dd7cf7e8c8ac83e8c20e55a0221246fc 100644 --- a/common/windows/cpp/src/vkeys.cpp +++ b/common/windows/cpp/src/vkeys.cpp @@ -17,10 +17,9 @@ */ #include "pch.h" +#include "../include/vkeys.h" -#define lpuch(x) L ## x - -PCWSTR VKeyNames[256] = { +const VKEY_CHAR* VKeyNames[256] = { // Key Codes lpuch("K_?00"), // &H0 lpuch("K_LBUTTON"), // &H1 @@ -292,7 +291,7 @@ PCWSTR VKeyNames[256] = { lpuch("K_?FF") // &HFF }; -PCWSTR VKeyISO9995Names[256] = { +const VKEY_CHAR* VKeyISO9995Names[256] = { // Key Codes lpuch(""), // lpuch("K_?00"), // &H0 lpuch(""), // lpuch("K_LBUTTON"), // &H1 diff --git a/common/windows/delphi/Makefile b/common/windows/delphi/Makefile index 6241b54379e710ee7338916e99622495a52b8f58..2a8258f07bb3a8fe900c5a7def02a10b29245757 100644 --- a/common/windows/delphi/Makefile +++ b/common/windows/delphi/Makefile @@ -1,10 +1,9 @@ !include Defines.mak global-versions: -# Build our root version.txt +# Build our root version.txt; TODO: replace with build.sh deps cd $(KEYMAN_ROOT)\common\windows - - $(MKVER_U) $(KEYMAN_ROOT)\common\windows\cpp\include\keymanversion_build.in $(KEYMAN_ROOT)\common\windows\cpp\include\keymanversion_build.h $(MKVER_U) $(KEYMAN_ROOT)\common\windows\delphi\general\keymanversion_build.in $(KEYMAN_ROOT)\common\windows\delphi\general\keymanversion_build.inc + $(GIT_BASH_FOR_KEYMAN) $(KEYMAN_ROOT)/common/include/build.sh clean configure build !include Target.mak \ No newline at end of file diff --git a/common/windows/delphi/components/common_components.identcache b/common/windows/delphi/components/common_components.identcache deleted file mode 100644 index e216a30819a0f9d8ffa0e9eec5cc52bdbb1d0041..0000000000000000000000000000000000000000 Binary files a/common/windows/delphi/components/common_components.identcache and /dev/null differ diff --git a/common/windows/delphi/ext/sentry.txt b/common/windows/delphi/ext/sentry.txt index 2463cd98f37cc1b926e8a374d8227ca94b18780a..29c5273516381da69b8f97f6e9520846a87d4b59 100644 --- a/common/windows/delphi/ext/sentry.txt +++ b/common/windows/delphi/ext/sentry.txt @@ -4,7 +4,7 @@ Keyman Third Party Package Readme: Sentry Name: Sentry Purpose: Sentry provides error reporting to a Sentry server Source: https://github.com/getsentry/sentry-native -Version: 0.4.9 +Version: 0.6.0 License: MIT Updated for: N/A Documentation: https://docs.sentry.io/platforms/native/ @@ -15,8 +15,8 @@ Include Paths: N/A Installation notes ------------------ -The original C++ version can be downloaded from site above. Source is -not included +The original C++ version can be downloaded from site above, and must be +built following the instructions below. Build notes ----------- @@ -27,6 +27,9 @@ team. This needs to be updated whenever Sentry is updated. Sentry.Client.pas is a wrapper of sentry.pas which provides a Delphi-style interface to Sentry. +Note: as of 0.6.0, performance SDKs are not included in Sentry.Client.pas, +because we are not currently using them. + build_sentry_for_keyman.bat should be copied into the sentry-native repo, and tweaked as required for updated versions of Sentry. Run: @@ -37,6 +40,11 @@ and tweaked as required for updated versions of Sentry. Run: install: prepare install folder in sentry-native deploy: copy the sentry.dll and related files into this repo +**Important:** ensure that the sentry folder name in all .wxs files is +updated to match the SENTRY_SDK_VERSION from sentry.h. Source C++ and .pas +files use the constant to refer to the location where the files may be +found. + License - MIT ------------- diff --git a/common/windows/delphi/ext/sentry/crashpad_handler.exe b/common/windows/delphi/ext/sentry/crashpad_handler.exe index 41e10b88e1ff4b00ecf92bf4df311ba4ccdc0583..76276e6bec9e55686b779c34801c42b0f05ecdf7 100644 Binary files a/common/windows/delphi/ext/sentry/crashpad_handler.exe and b/common/windows/delphi/ext/sentry/crashpad_handler.exe differ diff --git a/common/windows/delphi/ext/sentry/sentry.dll b/common/windows/delphi/ext/sentry/sentry.dll index 96d753e4cc128b04dd28fbdc563e3b7c340da6ea..86fbd12db6fcbc322631ee74979706ecf526fbea 100644 Binary files a/common/windows/delphi/ext/sentry/sentry.dll and b/common/windows/delphi/ext/sentry/sentry.dll differ diff --git a/common/windows/delphi/ext/sentry/sentry.h b/common/windows/delphi/ext/sentry/sentry.h index 91146520a959751f20f773dfb5ed3ed01374136f..b5381642dae2923dfd5737a8519060f2b4906ed3 100644 --- a/common/windows/delphi/ext/sentry/sentry.h +++ b/common/windows/delphi/ext/sentry/sentry.h @@ -23,8 +23,14 @@ extern "C" { #endif /* SDK Version */ -#define SENTRY_SDK_NAME "sentry.native" -#define SENTRY_SDK_VERSION "0.4.9" +#ifndef SENTRY_SDK_NAME +# ifdef __ANDROID__ +# define SENTRY_SDK_NAME "sentry.native.android" +# else +# define SENTRY_SDK_NAME "sentry.native" +# endif +#endif +#define SENTRY_SDK_VERSION "0.6.0" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ @@ -46,6 +52,10 @@ extern "C" { #elif defined(__linux) || defined(__linux__) # define SENTRY_PLATFORM_LINUX # define SENTRY_PLATFORM_UNIX +#elif defined(_AIX) +/* IBM i PASE is also counted as AIX */ +# define SENTRY_PLATFORM_AIX +# define SENTRY_PLATFORM_UNIX #else # error unsupported platform #endif @@ -189,7 +199,7 @@ SENTRY_API sentry_value_t sentry_value_new_int32(int32_t value); SENTRY_API sentry_value_t sentry_value_new_double(double value); /** - * Creates a new boolen value. + * Creates a new boolean value. */ SENTRY_API sentry_value_t sentry_value_new_bool(int value); @@ -399,8 +409,8 @@ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread( * * See https://develop.sentry.dev/sdk/event-payloads/stacktrace/ * - * The returned object needs to be attached to either an exception - * event, or a thread object. + * The returned object must be attached to either an exception or thread + * object. * * If `ips` is NULL the current stack trace is captured, otherwise `len` * stack trace instruction pointers are attached to the event. @@ -408,6 +418,17 @@ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread( SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_stacktrace( void **ips, size_t len); +/** + * Sets the Stack Trace conforming to the Stack Trace Interface in a value. + * + * The value argument must be either an exception or thread object. + * + * If `ips` is NULL the current stack trace is captured, otherwise `len` stack + * trace instruction pointers are attached to the event. + */ +SENTRY_EXPERIMENTAL_API void sentry_value_set_stacktrace( + sentry_value_t value, void **ips, size_t len); + /** * Adds an Exception to an Event value. * @@ -469,9 +490,12 @@ typedef struct sentry_ucontext_s { * * If the address is given in `addr` the stack is unwound form there. * Otherwise (NULL is passed) the current instruction pointer is used as - * start address. The stack trace is written to `stacktrace_out` with up to - * `max_len` frames being written. The actual number of unwound stackframes - * is returned. + * start address. + * Unwinding with a given `addr` is not supported on all platforms. + * + * The stack trace in the form of instruction-addresses, is written to the + * caller allocated `stacktrace_out`, with up to `max_len` frames being written. + * The actual number of unwound stackframes is returned. */ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack( void *addr, void **stacktrace_out, size_t max_len); @@ -479,8 +503,12 @@ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack( /** * Unwinds the stack from the given context. * - * The stack trace is written to `stacktrace_out` with up to `max_len` frames - * being written. The actual number of unwound stackframes is returned. + * The caller is responsible to construct an appropriate `sentry_ucontext_t`. + * Unwinding from a user context is not supported on all platforms. + * + * The stack trace in the form of instruction-addresses, is written to the + * caller allocated `stacktrace_out`, with up to `max_len` frames being written. + * The actual number of unwound stackframes is returned. */ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack_from_ucontext( const sentry_ucontext_t *uctx, void **stacktrace_out, size_t max_len); @@ -543,13 +571,21 @@ typedef struct sentry_envelope_s sentry_envelope_t; SENTRY_API void sentry_envelope_free(sentry_envelope_t *envelope); /** - * Given an envelope returns the embedded event if there is one. + * Given an Envelope, returns the embedded Event if there is one. * - * This returns a borrowed value to the event in the envelope. + * This returns a borrowed value to the Event in the Envelope. */ SENTRY_API sentry_value_t sentry_envelope_get_event( const sentry_envelope_t *envelope); +/** + * Given an Envelope, returns the embedded Transaction if there is one. + * + * This returns a borrowed value to the Transaction in the Envelope. + */ +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_envelope_get_transaction( + const sentry_envelope_t *envelope); + /** * Serializes the envelope. * @@ -571,7 +607,7 @@ SENTRY_API int sentry_envelope_write_to_file( /** * The Sentry Client Options. * - * See https://docs.sentry.io/error-reporting/configuration/ + * See https://docs.sentry.io/platforms/native/configuration/ */ struct sentry_options_s; typedef struct sentry_options_s sentry_options_t; @@ -595,13 +631,16 @@ typedef struct sentry_options_s sentry_options_t; * * `startup_func`: This hook will be called by sentry inside of `sentry_init` * and instructs the transport to initialize itself. Failures will bubble up * to `sentry_init`. + * * `flush_func`: Instructs the transport to flush its queue. + * This hook receives a millisecond-resolution `timeout` parameter and should + * return `0` if the transport queue is flushed within the timeout. * * `shutdown_func`: Instructs the transport to flush its queue and shut down. * This hook receives a millisecond-resolution `timeout` parameter and should - * return `true` when the transport was flushed and shut down successfully. - * In case of `false`, sentry will log an error, but continue with freeing the - * transport. + * return `0` if the transport is flushed and shut down successfully. + * In case of a non-zero return value, sentry will log an error, but continue + * with freeing the transport. * * `free_func`: Frees the transports `state`. This hook might be called even - * though `shutdown_func` returned `false` previously. + * though `shutdown_func` returned a failure code previously. * * The transport interface might be extended in the future with hooks to flush * its internal queue without shutting down, and to dump its internal queue to @@ -641,6 +680,16 @@ SENTRY_API void sentry_transport_set_free_func( SENTRY_API void sentry_transport_set_startup_func(sentry_transport_t *transport, int (*startup_func)(const sentry_options_t *options, void *state)); +/** + * Sets the transport flush hook. + * + * This hook will receive a millisecond-resolution timeout. + * It should return `0` if all the pending envelopes are + * sent within the timeout, or `1` if the timeout is hit. + */ +SENTRY_API void sentry_transport_set_flush_func(sentry_transport_t *transport, + int (*flush_func)(uint64_t timeout, void *state)); + /** * Sets the transport shutdown hook. * @@ -671,6 +720,20 @@ SENTRY_API void sentry_transport_free(sentry_transport_t *transport); SENTRY_API sentry_transport_t *sentry_new_function_transport( void (*func)(const sentry_envelope_t *envelope, void *data), void *data); +/** + * This represents an interface for user-defined backends. + * + * Backends are responsible to handle crashes. They are maintained at runtime + * via various life-cycle hooks from the sentry-core. + * + * At this point none of those interfaces are exposed in the API including + * creation and destruction. The main use-case of the backend in the API at this + * point is to disable it via `sentry_options_set_backend` at runtime before it + * is initialized. + */ +struct sentry_backend_s; +typedef struct sentry_backend_s sentry_backend_t; + /* -- Options APIs -- */ /** @@ -707,11 +770,28 @@ SENTRY_API void sentry_options_set_transport( * call `sentry_value_decref` on the provided event, and return a * `sentry_value_new_null()` instead. * + * If you have set an `on_crash` callback (independent of whether it discards or + * retains the event), `before_send` will no longer be invoked for crash-events, + * which allows you to better distinguish between crashes and all other events + * in client-side pre-processing. + * * This function may be invoked inside of a signal handler and must be safe for * that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. * On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see * the documentation on SEH (structured exception handling) for more information * https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling + * + * Up to version 0.4.18 the `before_send` callback wasn't invoked in case the + * event sampling discarded an event. In the current implementation the + * `before_send` callback is invoked even if the event sampling discards the + * event, following the cross-SDK session filter order: + * + * https://develop.sentry.dev/sdk/sessions/#filter-order + * + * On Windows the crashpad backend can capture fast-fail crashes which by-pass + * SEH. Since the `before_send` is called by a local exception-handler, it will + * not be invoked when such a crash happened, even though a minidump will be + * sent. */ typedef sentry_value_t (*sentry_event_function_t)( sentry_value_t event, void *hint, void *closure); @@ -724,6 +804,64 @@ typedef sentry_value_t (*sentry_event_function_t)( SENTRY_API void sentry_options_set_before_send( sentry_options_t *opts, sentry_event_function_t func, void *data); +/** + * Type of the `on_crash` callback. + * + * The `on_crash` callback replaces the `before_send` callback for crash events. + * The interface is analogous to `before_send` in that the callback takes + * ownership of the `event`, and should usually return that same event. In case + * the event should be discarded, the callback needs to call + * `sentry_value_decref` on the provided event, and return a + * `sentry_value_new_null()` instead. + * + * Only the `inproc` backend currently fills the passed-in event with useful + * data and processes any modifications to the return value. Since both + * `breakpad` and `crashpad` use minidumps to capture the crash state, the + * passed-in event is empty when using these backends, and they ignore any + * changes to the return value. + * + * If you set this callback in the options, it prevents a concurrently enabled + * `before_send` callback from being invoked in the crash case. This allows for + * better differentiation between crashes and other events and gradual migration + * from existing `before_send` implementations: + * + * - if you have a `before_send` implementation and do not define an `on_crash` + * callback your application will receive both normal and crash events as + * before + * - if you have a `before_send` implementation but only want to handle normal + * events with it, then you can define an `on_crash` callback that returns + * the passed-in event and does nothing else + * - if you are not interested in normal events, but only want to act on + * crashes (within the limits mentioned below), then only define an + * `on_crash` callback with the option to filter (on all backends) or enrich + * (only inproc) the crash event + * + * This function may be invoked inside of a signal handler and must be safe for + * that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. + * On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see + * the documentation on SEH (structured exception handling) for more information + * https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling + * + * Platform-specific behavior: + * + * - does not work with crashpad on macOS. + * - for breakpad on Linux the `uctx` parameter is always NULL. + * - on Windows the crashpad backend can capture fast-fail crashes which + * by-pass SEH. Since `on_crash` is called by a local exception-handler, it will + * not be invoked when such a crash happened, even though a minidump will be + * sent. + */ +typedef sentry_value_t (*sentry_crash_function_t)( + const sentry_ucontext_t *uctx, sentry_value_t event, void *closure); + +/** + * Sets the `on_crash` callback. + * + * See the `sentry_crash_function_t` typedef above for more information. + */ +SENTRY_API void sentry_options_set_on_crash( + sentry_options_t *opts, sentry_crash_function_t func, void *data); + /** * Sets the DSN. */ @@ -738,6 +876,19 @@ SENTRY_API const char *sentry_options_get_dsn(const sentry_options_t *opts); * Sets the sample rate, which should be a double between `0.0` and `1.0`. * Sentry will randomly discard any event that is captured using * `sentry_capture_event` when a sample rate < 1 is set. + * + * The sampling happens at the end of the event processing according to the + * following order: + * + * https://develop.sentry.dev/sdk/sessions/#filter-order + * + * Only items 3. to 6. are currently applicable to sentry-native. This means + * each processing step is executed even if the sampling discards the event + * before sending it to the backend. This is particularly relevant to users of + * the `before_send` callback. + * + * The above is in contrast to versions up to 0.4.18 where the sampling happened + * at the beginning of the processing/filter sequence. */ SENTRY_API void sentry_options_set_sample_rate( sentry_options_t *opts, double sample_rate); @@ -999,6 +1150,29 @@ SENTRY_API void sentry_options_set_database_pathw( SENTRY_API void sentry_options_set_system_crash_reporter_enabled( sentry_options_t *opts, int enabled); +/** + * Sets the maximum time (in milliseconds) to wait for the asynchronous tasks to + * end on shutdown, before attempting a forced termination. + */ +SENTRY_API void sentry_options_set_shutdown_timeout( + sentry_options_t *opts, uint64_t shutdown_timeout); + +/** + * Gets the maximum time (in milliseconds) to wait for the asynchronous tasks to + * end on shutdown, before attempting a forced termination. + */ +SENTRY_API uint64_t sentry_options_get_shutdown_timeout(sentry_options_t *opts); + +/** + * Sets a user-defined backend. + * + * Since creation and destruction of backends is not exposed in the API, this + * can only be used to set the backend to `NULL`, which disables the backend in + * the initialization. + */ +SENTRY_API void sentry_options_set_backend( + sentry_options_t *opts, sentry_backend_t *backend); + /* -- Global APIs -- */ /** @@ -1012,6 +1186,15 @@ SENTRY_API void sentry_options_set_system_crash_reporter_enabled( */ SENTRY_API int sentry_init(sentry_options_t *options); +/** + * Instructs the transport to flush its send queue. + * + * The `timeout` parameter is in milliseconds. + * + * Returns 0 on success, or a non-zero return value in case the timeout is hit. + */ +SENTRY_API int sentry_flush(uint64_t timeout); + /** * Shuts down the sentry client and forces transports to flush out. * @@ -1080,6 +1263,10 @@ SENTRY_API sentry_user_consent_t sentry_user_consent_get(void); /** * Sends a sentry event. + * + * If returns a nil UUID if the event being passed in is a transaction, and the + * transaction will not be sent nor consumed. `sentry_transaction_finish` should + * be used to send transactions. */ SENTRY_API sentry_uuid_t sentry_capture_event(sentry_value_t event); @@ -1155,14 +1342,46 @@ SENTRY_API void sentry_remove_fingerprint(void); SENTRY_API void sentry_set_transaction(const char *transaction); /** - * Removes the transaction. + * Sets the event level. */ -SENTRY_API void sentry_remove_transaction(void); +SENTRY_API void sentry_set_level(sentry_level_t level); /** - * Sets the event level. + * Sets the maximum number of spans that can be attached to a + * transaction. */ -SENTRY_API void sentry_set_level(sentry_level_t level); +SENTRY_EXPERIMENTAL_API void sentry_options_set_max_spans( + sentry_options_t *opts, size_t max_spans); + +/** + * Gets the maximum number of spans that can be attached to a + * transaction. + */ +SENTRY_EXPERIMENTAL_API size_t sentry_options_get_max_spans( + sentry_options_t *opts); + +/** + * Sets the sample rate for transactions. Should be a double between + * `0.0` and `1.0`. Transactions will be randomly discarded during + * `sentry_transaction_finish` when the sample rate is < 1.0. + */ +SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sample_rate( + sentry_options_t *opts, double sample_rate); + +/** + * Returns the sample rate for transactions. + */ +SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate( + sentry_options_t *opts); + +/* -- Session APIs -- */ + +typedef enum { + SENTRY_SESSION_STATUS_OK, + SENTRY_SESSION_STATUS_CRASHED, + SENTRY_SESSION_STATUS_ABNORMAL, + SENTRY_SESSION_STATUS_EXITED, +} sentry_session_status_t; /** * Starts a new session. @@ -1174,6 +1393,505 @@ SENTRY_API void sentry_start_session(void); */ SENTRY_API void sentry_end_session(void); +/** + * Ends a session with an explicit `status` code. + */ +SENTRY_EXPERIMENTAL_API void sentry_end_session_with_status( + sentry_session_status_t status); + +/* -- Performance Monitoring/Tracing APIs -- */ + +/** + * A sentry Transaction Context. + * + * See Transaction Interface under + * https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes + */ +struct sentry_transaction_context_s; +typedef struct sentry_transaction_context_s sentry_transaction_context_t; + +/** + * A sentry Transaction. + * + * See https://develop.sentry.dev/sdk/event-payloads/transaction/ + */ +struct sentry_transaction_s; +typedef struct sentry_transaction_s sentry_transaction_t; + +/** + * A sentry Span. + * + * See https://develop.sentry.dev/sdk/event-payloads/span/ + */ +struct sentry_span_s; +typedef struct sentry_span_s sentry_span_t; + +/** + * Constructs a new Transaction Context. The returned value needs to be passed + * into `sentry_transaction_start` in order to be recorded and sent to sentry. + * + * See + * https://docs.sentry.io/platforms/native/enriching-events/transaction-name/ + * for an explanation of a Transaction's `name`, and + * https://develop.sentry.dev/sdk/performance/span-operations/ for conventions + * around an `operation`'s value. + * + * Also see https://develop.sentry.dev/sdk/event-payloads/transaction/#anatomy + * for an explanation of `operation`, in addition to other properties and + * actions that can be performed on a Transaction. + * + * The returned value is not thread-safe. Users are expected to ensure that + * appropriate locking mechanisms are implemented over the Transaction Context + * if it needs to be mutated across threads. Methods operating on the + * Transaction Context will mention what kind of expectations they carry if they + * need to mutate or access the object in a thread-safe way. + */ +SENTRY_EXPERIMENTAL_API sentry_transaction_context_t * +sentry_transaction_context_new(const char *name, const char *operation); + +/** + * Sets the `name` on a Transaction Context, which will be used in the + * Transaction constructed off of the context. + * + * The Transaction Context should not be mutated by other functions while + * setting a name on it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name( + sentry_transaction_context_t *tx_cxt, const char *name); + +/** + * Sets the `operation` on a Transaction Context, which will be used in the + * Transaction constructed off of the context + * + * See https://develop.sentry.dev/sdk/performance/span-operations/ for + * conventions on `operation`s. + * + * The Transaction Context should not be mutated by other functions while + * setting an operation on it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation( + sentry_transaction_context_t *tx_cxt, const char *operation); + +/** + * Sets the `sampled` field on a Transaction Context, which will be used in the + * Transaction constructed off of the context. + * + * When passed any value above 0, the Transaction will bypass all sampling + * options and always be sent to sentry. If passed 0, this Transaction and its + * child spans will never be sent to sentry. + * + * The Transaction Context should not be mutated by other functions while + * setting `sampled` on it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_sampled( + sentry_transaction_context_t *tx_cxt, int sampled); + +/** + * Removes the `sampled` field on a Transaction Context, which will be used in + * the Transaction constructed off of the context. + * + * The Transaction will use the sampling rate as defined in `sentry_options`. + * + * The Transaction Context should not be mutated by other functions while + * removing `sampled`. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled( + sentry_transaction_context_t *tx_cxt); + +/** + * Update the Transaction Context with the given HTTP header key/value pair. + * + * This is used to propagate distributed tracing metadata from upstream + * services. Therefore, the headers of incoming requests should be fed into this + * function so that sentry is able to continue a trace that was started by an + * upstream service. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header( + sentry_transaction_context_t *tx_cxt, const char *key, const char *value); + +/** + * Starts a new Transaction based on the provided context, restored from an + * external integration (i.e. a span from a different SDK) or manually + * constructed by a user. + * + * The second parameter is a custom Sampling Context to be used with a Traces + * Sampler to make a more informed sampling decision. The SDK does not currently + * support a custom Traces Sampler and this parameter is ignored for the time + * being but needs to be provided. + * + * Returns a Transaction, which is expected to be manually managed by the + * caller. Manual management involves ensuring that `sentry_transaction_finish` + * is invoked for the Transaction, and that the caller manually starts and + * finishes any child Spans as needed on the Transaction. + * + * Not invoking `sentry_transaction_finish` with the returned Transaction means + * it will be discarded, and will not be sent to sentry. + * + * To ensure that any Events or Message Events are associated with this + * Transaction while it is active, invoke and pass in the Transaction returned + * by this function to `sentry_set_transaction_object`. Further documentation on + * this can be found in `sentry_set_transaction_object`'s docstring. + * + * Takes ownership of `transaction_context`. A Transaction Context cannot be + * modified or re-used after it is used to start a Transaction. + * + * The returned value is not thread-safe. Users are expected to ensure that + * appropriate locking mechanisms are implemented over the Transaction if it + * needs to be mutated across threads. Methods operating on the Transaction will + * mention what kind of expectations they carry if they need to mutate or access + * the object in a thread-safe way. + */ +SENTRY_EXPERIMENTAL_API sentry_transaction_t *sentry_transaction_start( + sentry_transaction_context_t *tx_cxt, sentry_value_t sampling_ctx); + +/** + * Finishes and sends a Transaction to sentry. The event ID of the Transaction + * will be returned if this was successful; A nil UUID will be returned + * otherwise. + * + * Always takes ownership of `transaction`, regardless of whether the operation + * was successful or not. A Transaction cannot be modified or re-used after it + * is finished. + */ +SENTRY_EXPERIMENTAL_API sentry_uuid_t sentry_transaction_finish( + sentry_transaction_t *tx); + +/** + * Sets the Transaction so any Events sent while the Transaction + * is active will be associated with the Transaction. + * + * If the Transaction being passed in is unsampled, it will still be associated + * with any new Events. This will lead to some Events pointing to orphan or + * missing traces in sentry, see + * https://docs.sentry.io/product/sentry-basics/tracing/trace-view/#orphan-traces-and-broken-subtraces + * + * This increases the number of references pointing to the Transaction. Invoke + * `sentry_transaction_finish` to remove the Transaction set by this function as + * well as its reference by passing in the same Transaction as the one passed + * into this function. + */ +SENTRY_EXPERIMENTAL_API void sentry_set_transaction_object( + sentry_transaction_t *tx); + +/** + * Sets the Span so any Events sent while the Span + * is active will be associated with the Span. + * + * This increases the number of references pointing to the Span. Invoke + * `sentry_span_finish` to remove the Span set by this function as well + * as its reference by passing in the same Span as the one passed into + * this function. + */ +SENTRY_EXPERIMENTAL_API void sentry_set_span(sentry_span_t *span); + +/** + * Starts a new Span. + * + * The return value of `sentry_transaction_start` should be passed in as + * `parent`. + * + * Both `operation` and `description` can be null, but it is recommended to + * supply the former. See + * https://develop.sentry.dev/sdk/performance/span-operations/ for conventions + * around operations. + * + * See https://develop.sentry.dev/sdk/event-payloads/span/ for a description of + * the created Span's properties and expectations for `operation` and + * `description`. + * + * Returns a value that should be passed into `sentry_span_finish`. Not + * finishing the Span means it will be discarded, and will not be sent to + * sentry. `sentry_value_null` will be returned if the child Span could not be + * created. + * + * To ensure that any Events or Message Events are associated with this + * Span while it is active, invoke and pass in the Span returned + * by this function to `sentry_set_span`. Further documentation on this can be + * found in `sentry_set_span`'s docstring. + * + * This increases the number of references pointing to the Transaction. + * + * The returned value is not thread-safe. Users are expected to ensure that + * appropriate locking mechanisms are implemented over the Span if it needs + * to be mutated across threads. Methods operating on the Span will mention what + * kind of expectations they carry if they need to mutate or access the object + * in a thread-safe way. + */ +SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_transaction_start_child( + sentry_transaction_t *parent, char *operation, char *description); + +/** + * Starts a new Span. + * + * The return value of `sentry_span_start_child` may be passed in as `parent`. + * + * Both `operation` and `description` can be null, but it is recommended to + * supply the former. See + * https://develop.sentry.dev/sdk/performance/span-operations/ for conventions + * around operations. + * + * See https://develop.sentry.dev/sdk/event-payloads/span/ for a description of + * the created Span's properties and expectations for `operation` and + * `description`. + * + * Returns a value that should be passed into `sentry_span_finish`. Not + * finishing the Span means it will be discarded, and will not be sent to + * sentry. `sentry_value_null` will be returned if the child Span could not be + * created. + * + * To ensure that any Events or Message Events are associated with this + * Span while it is active, invoke and pass in the Span returned + * by this function to `sentry_set_span`. Further documentation on this can be + * found in `sentry_set_span`'s docstring. + * + * The returned value is not thread-safe. Users are expected to ensure that + * appropriate locking mechanisms are implemented over the Span if it needs + * to be mutated across threads. Methods operating on the Span will mention what + * kind of expectations they carry if they need to mutate or access the object + * in a thread-safe way. + */ +SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_span_start_child( + sentry_span_t *parent, char *operation, char *description); + +/** + * Finishes a Span. + * + * This takes ownership of `span`. A Span cannot be modified or re-used after it + * is finished. + * + * This will mutate the `span`'s containing Transaction, so the containing + * Transaction should also not be mutated by other functions when finishing a + * span. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_finish(sentry_span_t *span); + +/** + * Sets a tag on a Transaction to the given string value. + * + * Tags longer than 200 bytes will be truncated. + * + * The Transaction should not be mutated by other functions while a tag is being + * set on it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_tag( + sentry_transaction_t *transaction, const char *tag, const char *value); + +/** + * Removes a tag from a Transaction. + * + * The Transaction should not be mutated by other functions while a tag is being + * removed from it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_tag( + sentry_transaction_t *transaction, const char *tag); + +/** + * Sets the given key in a Transaction's "data" section to the given value. + * + * The Transaction should not be mutated by other functions while data is being + * set on it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_data( + sentry_transaction_t *transaction, const char *key, sentry_value_t value); + +/** + * Removes a key from a Transaction's "data" section. + * + * The Transaction should not be mutated by other functions while data is being + * removed from it. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_data( + sentry_transaction_t *transaction, const char *key); + +/** + * Sets a tag on a Span to the given string value. + * + * Tags longer than 200 bytes will be truncated. + * + * The Span should not be mutated by other functions while a tag is being set on + * it. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_set_tag( + sentry_span_t *span, const char *tag, const char *value); + +/** + * Removes a tag from a Span. + * + * The Span should not be mutated by other functions while a tag is being + * removed from it. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_remove_tag( + sentry_span_t *span, const char *tag); + +/** + * Sets the given key in a Span's "data" section to the given value. + * + * The Span should not be mutated by other functions while data is being set on + * it. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_set_data( + sentry_span_t *span, const char *key, sentry_value_t value); + +/** + * Removes a key from a Span's "data" section. + * + * The Span should not be mutated by other functions while data is being removed + * from it. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_remove_data( + sentry_span_t *span, const char *key); + +/** + * Sets a Transaction's name. + * + * The Transaction should not be mutated by other functions while setting its + * name. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name( + sentry_transaction_t *transaction, const char *name); + +/** + * The status of a Span or Transaction. + * + * See https://develop.sentry.dev/sdk/event-payloads/span/ for documentation. + */ +typedef enum { + // The operation completed successfully. + // HTTP status 100..299 + successful redirects from the 3xx range. + SENTRY_SPAN_STATUS_OK, + // The operation was cancelled (typically by the user). + SENTRY_SPAN_STATUS_CANCELLED, + // Unknown. Any non-standard HTTP status code. + // "We do not know whether the transaction failed or succeeded." + SENTRY_SPAN_STATUS_UNKNOWN, + // Client specified an invalid argument. 4xx. + // Note that this differs from FailedPrecondition. InvalidArgument + // indicates arguments that are problematic regardless of the + // state of the system. + SENTRY_SPAN_STATUS_INVALID_ARGUMENT, + // Deadline expired before operation could complete. + // For operations that change the state of the system, this error may be + // returned even if the operation has been completed successfully. + // HTTP redirect loops and 504 Gateway Timeout. + SENTRY_SPAN_STATUS_DEADLINE_EXCEEDED, + // 404 Not Found. Some requested entity (file or directory) was not found. + SENTRY_SPAN_STATUS_NOT_FOUND, + // Already exists (409) + // Some entity that we attempted to create already exists. + SENTRY_SPAN_STATUS_ALREADY_EXISTS, + // 403 Forbidden + // The caller does not have permission to execute the specified operation. + SENTRY_SPAN_STATUS_PERMISSION_DENIED, + // 429 Too Many Requests + // Some resource has been exhausted, perhaps a per-user quota or perhaps + // the entire file system is out of space. + SENTRY_SPAN_STATUS_RESOURCE_EXHAUSTED, + // Operation was rejected because the system is not in a state required for + // the operation's execution. + SENTRY_SPAN_STATUS_FAILED_PRECONDITION, + // The operation was aborted, typically due to a concurrency issue. + SENTRY_SPAN_STATUS_ABORTED, + // Operation was attempted past the valid range. + SENTRY_SPAN_STATUS_OUT_OF_RANGE, + // 501 Not Implemented + // Operation is not implemented or not enabled. + SENTRY_SPAN_STATUS_UNIMPLEMENTED, + // Other/generic 5xx + SENTRY_SPAN_STATUS_INTERNAL_ERROR, + // 503 Service Unavailable + SENTRY_SPAN_STATUS_UNAVAILABLE, + // Unrecoverable data loss or corruption + SENTRY_SPAN_STATUS_DATA_LOSS, + // 401 Unauthorized (actually does mean unauthenticated according to RFC + // 7235) + // Prefer PermissionDenied if a user is logged in. + SENTRY_SPAN_STATUS_UNAUTHENTICATED, +} sentry_span_status_t; + +/** + * Sets a Span's status. + * + * The Span should not be mutated by other functions while setting its status. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_set_status( + sentry_span_t *span, sentry_span_status_t status); + +/** + * Sets a Transaction's status. + * + * The Transaction should not be mutated by other functions while setting its + * status. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_status( + sentry_transaction_t *tx, sentry_span_status_t status); + +/** + * Type of the `iter_headers` callback. + * + * The callback is being called with HTTP header key/value pairs. + * These headers can be attached to outgoing HTTP requests to propagate + * distributed tracing metadata to downstream services. + * + */ +typedef void (*sentry_iter_headers_function_t)( + const char *key, const char *value, void *userdata); + +/** + * Iterates the distributed tracing HTTP headers for the given span. + */ +SENTRY_EXPERIMENTAL_API void sentry_span_iter_headers(sentry_span_t *span, + sentry_iter_headers_function_t callback, void *userdata); + +/** + * Iterates the distributed tracing HTTP headers for the given transaction. + */ +SENTRY_EXPERIMENTAL_API void sentry_transaction_iter_headers( + sentry_transaction_t *tx, sentry_iter_headers_function_t callback, + void *userdata); + +/** + * Returns whether the application has crashed on the last run. + * + * Notes: + * * The underlying value is set by sentry_init() - it must be called first. + * * Call sentry_clear_crashed_last_run() to reset for the next app run. + * + * Possible return values: + * 1 = the last run was a crash + * 0 = no crash recognized + * -1 = sentry_init() hasn't been called yet + */ +SENTRY_EXPERIMENTAL_API int sentry_get_crashed_last_run(void); + +/** + * Clear the status of the "crashed-last-run". You should explicitly call + * this after sentry_init() if you're using sentry_get_crashed_last_run(). + * Otherwise, the same information is reported on any subsequent runs. + * + * Notes: + * * This doesn't change the value of sentry_get_crashed_last_run() yet. + * However, if sentry_init() is called again, the value will change. + * * This may only be called after sentry_init() and before sentry_close(). + * + * Returns 0 on success, 1 on error. + */ +SENTRY_EXPERIMENTAL_API int sentry_clear_crashed_last_run(void); + +/** + * Sentry SDK version. + */ +SENTRY_EXPERIMENTAL_API const char *sentry_sdk_version(void); + +/** + * Sentry SDK name. + */ +SENTRY_EXPERIMENTAL_API const char *sentry_sdk_name(void); + +/** + * Sentry SDK User-Agent. + */ +SENTRY_EXPERIMENTAL_API const char *sentry_sdk_user_agent(void); + #ifdef __cplusplus } #endif diff --git a/common/windows/delphi/ext/sentry/sentry.lib b/common/windows/delphi/ext/sentry/sentry.lib index f9c6b6671d38fcca76ed1d1a816452a5a11f2fd3..9858e8c0396ac850c46a7548266f17cf01c02c41 100644 Binary files a/common/windows/delphi/ext/sentry/sentry.lib and b/common/windows/delphi/ext/sentry/sentry.lib differ diff --git a/common/windows/delphi/ext/sentry/sentry.pas b/common/windows/delphi/ext/sentry/sentry.pas index 24e85c9b9abcd2617640b084646837f073e55a5c..af47eb383fb897b9300d3069631a06aa47a40752 100644 --- a/common/windows/delphi/ext/sentry/sentry.pas +++ b/common/windows/delphi/ext/sentry/sentry.pas @@ -1,35 +1,35 @@ -{$D-} // Don't include debug information -// Delphi translation of sentry.h -// Sentry Native API 0.4.9 -// https://github.com/getsentry/sentry-native -unit sentry; - -{ - * NOTE on encodings: - * - * Sentry will assume an encoding of UTF-8 for all string data that is captured - * and being sent to sentry as an Event. - * All the functions that are dealing with *paths* will assume an OS-specific - * encoding, typically ANSI on Windows, UTF-8 macOS, and the locale encoding on - * Linux; and they provide wchar-compatible alternatives on Windows which are - * preferred. -} - -interface - -{$DEFINE SENTRY_API} - -//{$ALIGN ON} -{$MINENUMSIZE 4} - -{$WARN SYMBOL_PLATFORM OFF} - -{$IF DEFINED(MSWINDOWS)} - -uses - Winapi.Windows; - -{$DEFINE SENTRY_UUID_WINDOWS} +{$D-} // Don't include debug information +// Delphi translation of sentry.h +// Sentry Native API 0.6.0 - ***excluding performance APIs*** +// https://github.com/getsentry/sentry-native +unit sentry; + +{ + * NOTE on encodings: + * + * Sentry will assume an encoding of UTF-8 for all string data that is captured + * and being sent to sentry as an Event. + * All the functions that are dealing with *paths* will assume an OS-specific + * encoding, typically ANSI on Windows, UTF-8 macOS, and the locale encoding on + * Linux; and they provide wchar-compatible alternatives on Windows which are + * preferred. +} + +interface + +{$DEFINE SENTRY_API} + +//{$ALIGN ON} +{$MINENUMSIZE 4} + +{$WARN SYMBOL_PLATFORM OFF} + +{$IF DEFINED(MSWINDOWS)} + +uses + Winapi.Windows; + +{$DEFINE SENTRY_UUID_WINDOWS} {$ELSEIF DEFINED(ANDROID)} @@ -45,15 +45,15 @@ uses {$ENDIF} const -{$IFDEF WIN64} +{$IFDEF WIN64} sentry_dll = 'sentry.x64.dll'; {$ELSE} sentry_dll = 'sentry.dll'; {$ENDIF} - + const SENTRY_SDK_NAME = 'sentry.native'; - SENTRY_SDK_VERSION = '0.4.9'; + SENTRY_SDK_VERSION = '0.6.0'; SENTRY_SDK_USER_AGENT = SENTRY_SDK_NAME + '/' + SENTRY_SDK_VERSION; {$IF DEFINED(MSWINDOWS)} @@ -62,32 +62,32 @@ const {$MESSAGE ERROR 'Unsupported platform'} {$ENDIF} -type - size_t = NativeUInt; - -// The library internally uses the system malloc and free functions to manage -// memory. It does not use realloc. The reason for this is that on unix -// platforms we fall back to a simplistic page allocator once we have -// encountered a SIGSEGV or other terminating signal as malloc is no longer -// safe to use. Since we cannot portably reallocate allocations made on the -// pre-existing allocator we're instead not using realloc. -// -// Note also that after SIGSEGV sentry_free() becomes a noop. - -// -// allocates memory with the underlying allocator -// -function sentry_malloc(size: size_t): Pointer; cdecl; external sentry_dll delayed; - -// -// releases memory allocated from the underlying allocator -procedure sentry_free(ptr: Pointer); cdecl; external sentry_dll delayed; - +type + size_t = NativeUInt; + +// The library internally uses the system malloc and free functions to manage +// memory. It does not use realloc. The reason for this is that on unix +// platforms we fall back to a simplistic page allocator once we have +// encountered a SIGSEGV or other terminating signal as malloc is no longer +// safe to use. Since we cannot portably reallocate allocations made on the +// pre-existing allocator we're instead not using realloc. +// +// Note also that after SIGSEGV sentry_free() becomes a noop. + +// +// allocates memory with the underlying allocator +// +function sentry_malloc(size: size_t): Pointer; cdecl; external sentry_dll delayed; + +// +// releases memory allocated from the underlying allocator +procedure sentry_free(ptr: Pointer); cdecl; external sentry_dll delayed; + // legacy function. Alias for `sentry_free`. procedure sentry_string_free(str: PAnsiChar); cdecl; - + // -- Protocol Value API -- - + // // Type of a sentry value. // @@ -102,7 +102,7 @@ type SENTRY_VALUE_TYPE_LIST, SENTRY_VALUE_TYPE_OBJECT ); - + // // Represents a sentry protocol value. // @@ -119,20 +119,20 @@ type // automatically happens for some shared values in the event payload like // the module list. // - -type - {sentry_value_u = record - case Integer of - 0: (_bits: UInt64); - 1: (_double: Double); - end; - + +type + {sentry_value_u = record + case Integer of + 0: (_bits: UInt64); + 1: (_double: Double); + end; + sentry_value_t = sentry_value_u;} sentry_value_t = UInt64; - + +// +// Increments the reference count on the value.\ // -// Increments the reference count on the value.\ -// procedure sentry_value_incref( value: sentry_value_t ); cdecl; external sentry_dll delayed; @@ -214,7 +214,7 @@ function sentry_value_new_object: sentry_value_t; cdecl; external sentry_dll de function sentry_value_get_type( value: sentry_value_t ): sentry_value_type_t; cdecl; external sentry_dll delayed; - + // // Sets a key to a value in the map. // @@ -234,8 +234,8 @@ function sentry_value_remove_by_key( value: sentry_value_t; const k: PAnsiChar ): Integer; cdecl; external sentry_dll delayed; - -// + +// // Appends a value to a list. // // This moves the ownership of the value into the list. The caller does not @@ -278,9 +278,12 @@ function sentry_value_get_by_key( const k: PAnsiChar ): sentry_value_t; cdecl; external sentry_dll delayed; - // - // Looks up a value in a map by key. If missing a null value is returned. - // The returned value is owned. + +// + +// Looks up a value in a map by key. If missing a null value is returned. + +// The returned value is owned. // // If the caller no longer needs the value it must be released with // `sentry_value_decref`. @@ -376,20 +379,20 @@ type sentry_level_e = ( sentry_level_t = sentry_level_e; // -// Creates a new empty Event value. -// -// See https://docs.sentry.io/platforms/native/enriching-events/ for how to -// further work with events, and https://develop.sentry.dev/sdk/event-payloads/ -// for a detailed overview of the possible properties of an Event. -// +// Creates a new empty Event value. +// +// See https://docs.sentry.io/platforms/native/enriching-events/ for how to +// further work with events, and https://develop.sentry.dev/sdk/event-payloads/ +// for a detailed overview of the possible properties of an Event. +// function sentry_value_new_event: sentry_value_t; cdecl; external sentry_dll delayed; // // Creates a new Message Event value. -// -// See https://develop.sentry.dev/sdk/event-payloads/message/ -// -// `logger` can be NULL to omit the logger value. +// +// See https://develop.sentry.dev/sdk/event-payloads/message/ +// +// `logger` can be NULL to omit the logger value. // function sentry_value_new_message_event( level: sentry_level_t; @@ -399,8 +402,8 @@ function sentry_value_new_message_event( // // Creates a new Breadcrumb with a specific type and message. -// -// See https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/ +// +// See https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/ // function sentry_value_new_breadcrumb( const _type: PAnsiChar; @@ -408,73 +411,87 @@ function sentry_value_new_breadcrumb( ): sentry_value_t; cdecl; external sentry_dll delayed; // -// Creates a new Exception value. -// -// This is intended for capturing language-level exception, such as from a -// try-catch block. `type` and `value` here refer to the exception class and -// a possible description. -// -// See https://develop.sentry.dev/sdk/event-payloads/exception/ -// -// The returned value needs to be attached to an event via -// `sentry_event_add_exception`. -// -function {SENTRY_EXPERIMENTAL_API} sentry_value_new_exception( - const _type: PAnsiChar; - const value: PAnsiChar -): sentry_value_t; cdecl; external sentry_dll delayed; - -// -// Creates a new Thread value. -// -// See https://develop.sentry.dev/sdk/event-payloads/threads/ -// -// The returned value needs to be attached to an event via -// `sentry_event_add_thread`. -// -// `name` can be NULL. -// -function {SENTRY_EXPERIMENTAL_API} sentry_value_new_thread( - id: UInt64; - const name: PAnsiChar -): sentry_value_t; cdecl; external sentry_dll delayed; - -// -// Creates a new Stack Trace conforming to the Stack Trace Interface. -// -// See https://develop.sentry.dev/sdk/event-payloads/stacktrace/ -// -// The returned object needs to be attached to either an exception -// event, or a thread object. -// -// If `ips` is NULL the current stack trace is captured, otherwise `len` -// stack trace instruction pointers are attached to the event. -// -function {SENTRY_EXPERIMENTAL_API} sentry_value_new_stacktrace( - ips: PPVoid; - len: NativeUInt -): sentry_value_t; cdecl; external sentry_dll delayed; - -// -// Adds an Exception to an Event value. -// -// This takes ownership of the `exception`. -// -procedure {SENTRY_EXPERIMENTAL_API} sentry_event_add_exception( - event: sentry_value_t; - exception: sentry_value_t -); cdecl; external sentry_dll delayed; - -// -// Adds a Thread to an Event value. -// -// This takes ownership of the `thread`. -// -procedure {SENTRY_EXPERIMENTAL_API} sentry_event_add_thread( - event: sentry_value_t; - thread: sentry_value_t -); cdecl; external sentry_dll delayed; - +// Creates a new Exception value. +// +// This is intended for capturing language-level exception, such as from a +// try-catch block. `type` and `value` here refer to the exception class and +// a possible description. +// +// See https://develop.sentry.dev/sdk/event-payloads/exception/ +// +// The returned value needs to be attached to an event via +// `sentry_event_add_exception`. +// +function {SENTRY_EXPERIMENTAL_API} sentry_value_new_exception( + const _type: PAnsiChar; + const value: PAnsiChar +): sentry_value_t; cdecl; external sentry_dll delayed; + +// +// Creates a new Thread value. +// +// See https://develop.sentry.dev/sdk/event-payloads/threads/ +// +// The returned value needs to be attached to an event via +// `sentry_event_add_thread`. +// +// `name` can be NULL. +// +function {SENTRY_EXPERIMENTAL_API} sentry_value_new_thread( + id: UInt64; + const name: PAnsiChar +): sentry_value_t; cdecl; external sentry_dll delayed; + +// +// Creates a new Stack Trace conforming to the Stack Trace Interface. +// +// See https://develop.sentry.dev/sdk/event-payloads/stacktrace/ +// +// The returned object needs to be attached to either an exception +// event, or a thread object. +// +// If `ips` is NULL the current stack trace is captured, otherwise `len` +// stack trace instruction pointers are attached to the event. +// +function {SENTRY_EXPERIMENTAL_API} sentry_value_new_stacktrace( + ips: PPVoid; + len: NativeUInt +): sentry_value_t; cdecl; external sentry_dll delayed; + +// +// Sets the Stack Trace conforming to the Stack Trace Interface in a value. +// +// The value argument must be either an exception or thread object. +// +// If `ips` is NULL the current stack trace is captured, otherwise `len` stack +// trace instruction pointers are attached to the event. +// +procedure {SENTRY_EXPERIMENTAL_API} sentry_value_set_stacktrace( + value: sentry_value_t; + ips: PPVoid; + len: NativeUInt +); cdecl; external sentry_dll delayed; + +// +// Adds an Exception to an Event value. +// +// This takes ownership of the `exception`. +// +procedure {SENTRY_EXPERIMENTAL_API} sentry_event_add_exception( + event: sentry_value_t; + exception: sentry_value_t +); cdecl; external sentry_dll delayed; + +// +// Adds a Thread to an Event value. +// +// This takes ownership of the `thread`. +// +procedure {SENTRY_EXPERIMENTAL_API} sentry_event_add_thread( + event: sentry_value_t; + thread: sentry_value_t +); cdecl; external sentry_dll delayed; + // -- Experimental APIs -- // @@ -493,33 +510,35 @@ function sentry_value_to_msgpack( // Adds a stack trace to an event. // // The stack trace is added as part of a new thread object. -// This function is **deprecated** in favor of using -// `sentry_value_new_stacktrace` in combination with `sentry_value_new_thread` -// and `sentry_event_add_thread`. -// -// If `ips` is NULL the current stack trace is captured, otherwise `len` -// stack trace instruction pointers are attached to the event. +// This function is **deprecated** in favor of using +// `sentry_value_new_stacktrace` in combination with `sentry_value_new_thread` +// and `sentry_event_add_thread`. +// +// If `ips` is NULL the current stack trace is captured, otherwise `len` +// stack trace instruction pointers are attached to the event. // procedure sentry_event_value_add_stacktrace( event: sentry_value_t; ips: Pointer; len: Integer - ); cdecl; external sentry_dll delayed; - - // -// This represents the OS dependent user context in the case of a crash, and can -// be used to manually capture a crash. +); cdecl; external sentry_dll delayed; + + + +// +// This represents the OS dependent user context in the case of a crash, and can +// be used to manually capture a crash. // type sentry_ucontext_s = record -{$IF DEFINED(MSWINDOWS)} - exception_ptrs: EXCEPTION_POINTERS; -{$ELSE} - signum: Integer; - siginfo: siginfo_t; +{$IF DEFINED(MSWINDOWS)} + exception_ptrs: EXCEPTION_POINTERS; +{$ELSE} + signum: Integer; + siginfo: siginfo_t; user_context: ucontext_t; -{$ENDIF} +{$ENDIF} end; sentry_ucontext_t = sentry_ucontext_s; @@ -551,9 +570,10 @@ function sentry_unwind_stack_from_ucontext( max_len: size_t ): size_t; cdecl; external sentry_dll delayed; - - - // + + + +// // A UUID // type @@ -613,12 +633,12 @@ procedure sentry_uuid_as_string( type // -// A Sentry Envelope. -// -// The Envelope is an abstract type which represents a payload being sent to -// sentry. It can contain one or more items, typically an Event. -// See https://develop.sentry.dev/sdk/envelopes/ -// +// A Sentry Envelope. +// +// The Envelope is an abstract type which represents a payload being sent to +// sentry. It can contain one or more items, typically an Event. +// See https://develop.sentry.dev/sdk/envelopes/ +// sentry_envelope_s = record end; sentry_envelope_t = sentry_envelope_s; psentry_envelope_t = Pointer; @@ -626,9 +646,9 @@ type // // Frees an envelope. // -procedure sentry_envelope_free( - envelope: sentry_envelope_t -); cdecl; external sentry_dll delayed; +procedure sentry_envelope_free( + envelope: sentry_envelope_t +); cdecl; external sentry_dll delayed; // // Given an envelope returns the embedded event if there is one. @@ -639,6 +659,15 @@ function sentry_envelope_get_event( const envelope: psentry_envelope_t ): sentry_value_t; cdecl; external sentry_dll delayed; +// +// Given an Envelope, returns the embedded Transaction if there is one. +// +// This returns a borrowed value to the Transaction in the Envelope. +// +function {SENTRY_EXPERIMENTAL_API} sentry_envelope_get_transaction( + const envelope: psentry_envelope_t +): sentry_value_t; cdecl; external sentry_dll delayed; + // // Serializes the envelope // @@ -651,10 +680,10 @@ function sentry_envelope_serialize( // // Serializes the envelope into a file. -// -// `path` is assumed to be in platform-specific filesystem path encoding. -// -// Returns 0 on success. +// +// `path` is assumed to be in platform-specific filesystem path encoding. +// +// Returns 0 on success. // function sentry_envelope_write_to_file( const envelope: psentry_envelope_t; @@ -663,8 +692,8 @@ function sentry_envelope_write_to_file( // // The Sentry Client Options. -// -// See https://docs.sentry.io/error-reporting/configuration/ +// +// See https://docs.sentry.io/error-reporting/configuration/ // type sentry_options_s = record end; @@ -672,130 +701,161 @@ type psentry_options_t = ^sentry_options_t; // -// This represents an interface for user-defined transports. -// -// Transports are responsible for sending envelopes to sentry and are the last -// step in the event pipeline. -// -// Envelopes will be submitted to the transport in a _fire and forget_ fashion, -// and the transport must send those envelopes _in order_. -// -// A transport has the following hooks, all of which -// take the user provided `state` as last parameter. The transport state needs -// to be set with `sentry_transport_set_state` and typically holds handles and -// other information that can be reused across requests. -// -// * `send_func`: This function will take ownership of an envelope, and is -// responsible for freeing it via `sentry_envelope_free`. -// * `startup_func`: This hook will be called by sentry inside of `sentry_init` -// and instructs the transport to initialize itself. Failures will bubble up -// to `sentry_init`. -// * `shutdown_func`: Instructs the transport to flush its queue and shut down. -// This hook receives a millisecond-resolution `timeout` parameter and should -// return `true` when the transport was flushed and shut down successfully. -// In case of `false`, sentry will log an error, but continue with freeing the -// transport. -// * `free_func`: Frees the transports `state`. This hook might be called even -// though `shutdown_func` returned `false` previously. -// -// The transport interface might be extended in the future with hooks to flush -// its internal queue without shutting down, and to dump its internal queue to -// disk in case of a hard crash. -// +// This represents an interface for user-defined transports. +// +// Transports are responsible for sending envelopes to sentry and are the last +// step in the event pipeline. +// +// Envelopes will be submitted to the transport in a _fire and forget_ fashion, +// and the transport must send those envelopes _in order_. +// +// A transport has the following hooks, all of which +// take the user provided `state` as last parameter. The transport state needs +// to be set with `sentry_transport_set_state` and typically holds handles and +// other information that can be reused across requests. +// +// * `send_func`: This function will take ownership of an envelope, and is +// responsible for freeing it via `sentry_envelope_free`. +// * `startup_func`: This hook will be called by sentry inside of `sentry_init` +// and instructs the transport to initialize itself. Failures will bubble up +// to `sentry_init`. +// * `shutdown_func`: Instructs the transport to flush its queue and shut down. +// This hook receives a millisecond-resolution `timeout` parameter and should +// return `true` when the transport was flushed and shut down successfully. +// In case of `false`, sentry will log an error, but continue with freeing the +// transport. +// * `free_func`: Frees the transports `state`. This hook might be called even +// though `shutdown_func` returned `false` previously. +// +// The transport interface might be extended in the future with hooks to flush +// its internal queue without shutting down, and to dump its internal queue to +// disk in case of a hard crash. +// type sentry_transport_s = record end; psentry_transport_s = ^sentry_transport_s; sentry_transport_t = sentry_transport_s; - psentry_transport_t = ^sentry_transport_t; - - _sentry_transport_new_func = procedure(e: psentry_envelope_t; state: Pointer); cdecl; - _sentry_transport_free_func = procedure(state: Pointer); cdecl; - _sentry_transport_startup_func = function(const options: psentry_options_t; state: Pointer): Integer; cdecl; - _sentry_transport_shutdown_func = function(timeout: UInt64; state: Pointer): Integer; cdecl; - -// -// Creates a new transport with an initial `send_func`. -// -function sentry_transport_new( - send_func: _sentry_transport_new_func -): psentry_transport_t; cdecl; external sentry_dll delayed; - -// -// Sets the transport `state`. -// -// If the state is owned by the transport and needs to be freed, use -// `sentry_transport_set_free_func` to set an appropriate hook. -// -procedure sentry_transport_set_state( - transport: psentry_transport_t; - state: Pointer -); cdecl; external sentry_dll delayed; - -// -// Sets the transport hook to free the transport `state`. -// -procedure sentry_transport_set_free_func( - transport: psentry_transport_t; - free_func: _sentry_transport_free_func -); cdecl; external sentry_dll delayed; - -// -// Sets the transport startup hook. -// -// This hook is called from within `sentry_init` and will get a reference to the -// options which can be used to initialize a transports internal state. -// It should return `0` on success. A failure will bubble up to `sentry_init`. -// -procedure sentry_transport_set_startup_func( - transport: psentry_transport_t; - startup_func: _sentry_transport_startup_func -); cdecl; external sentry_dll delayed; - -// -// Sets the transport shutdown hook. -// -// This hook will receive a millisecond-resolution timeout. -// It should return `0` on success in case all the pending envelopes have been -// sent within the timeout, or `1` if the timeout was hit. -// -procedure sentry_transport_set_shutdown_func( - transport: psentry_transport_t; - shutdown_func: _sentry_transport_shutdown_func -); cdecl; external sentry_dll delayed; - -// -// Generic way to free a transport. -// -procedure sentry_transport_free( - transport: psentry_transport_t -); cdecl; external sentry_dll delayed; - -// -// Create a new function transport. -// -// It is a convenience function which works with a borrowed `data`, and will -// automatically free the envelope, so the user provided function does not need -// to do that. -// -// This function is *deprecated* and will be removed in a future version. -// It is here for backwards compatibility. Users should migrate to the -// `sentry_transport_new` API. -// -function sentry_new_function_transport( - func: _sentry_transport_new_func; - data: Pointer -): psentry_transport_t; cdecl; external sentry_dll delayed; - + psentry_transport_t = ^sentry_transport_t; + + _sentry_transport_new_func = procedure(e: psentry_envelope_t; state: Pointer); cdecl; + _sentry_transport_free_func = procedure(state: Pointer); cdecl; + _sentry_transport_startup_func = function(const options: psentry_options_t; state: Pointer): Integer; cdecl; + _sentry_transport_shutdown_func = function(timeout: UInt64; state: Pointer): Integer; cdecl; + _sentry_transport_flush_func = function(timeout: UInt64; state: Pointer): Integer; cdecl; + +// +// Creates a new transport with an initial `send_func`. +// +function sentry_transport_new( + send_func: _sentry_transport_new_func +): psentry_transport_t; cdecl; external sentry_dll delayed; + +// +// Sets the transport `state`. +// +// If the state is owned by the transport and needs to be freed, use +// `sentry_transport_set_free_func` to set an appropriate hook. +// +procedure sentry_transport_set_state( + transport: psentry_transport_t; + state: Pointer +); cdecl; external sentry_dll delayed; + +// +// Sets the transport hook to free the transport `state`. +// +procedure sentry_transport_set_free_func( + transport: psentry_transport_t; + free_func: _sentry_transport_free_func +); cdecl; external sentry_dll delayed; + +// +// Sets the transport startup hook. +// +// This hook is called from within `sentry_init` and will get a reference to the +// options which can be used to initialize a transports internal state. +// It should return `0` on success. A failure will bubble up to `sentry_init`. +// +procedure sentry_transport_set_startup_func( + transport: psentry_transport_t; + startup_func: _sentry_transport_startup_func +); cdecl; external sentry_dll delayed; + +// +// Sets the transport flush hook. +// +// This hook will receive a millisecond-resolution timeout. +// It should return `0` if all the pending envelopes are +// sent within the timeout, or `1` if the timeout is hit. +// +procedure {SENTRY_API} sentry_transport_set_flush_func( + transport: psentry_transport_t; + flush_func: _sentry_transport_flush_func +); cdecl; external sentry_dll delayed; + + + +// +// Sets the transport shutdown hook. +// +// This hook will receive a millisecond-resolution timeout. +// It should return `0` on success in case all the pending envelopes have been +// sent within the timeout, or `1` if the timeout was hit. +// +procedure sentry_transport_set_shutdown_func( + transport: psentry_transport_t; + shutdown_func: _sentry_transport_shutdown_func +); cdecl; external sentry_dll delayed; + +// +// Generic way to free a transport. +// +procedure sentry_transport_free( + transport: psentry_transport_t +); cdecl; external sentry_dll delayed; + +// +// Create a new function transport. +// +// It is a convenience function which works with a borrowed `data`, and will +// automatically free the envelope, so the user provided function does not need +// to do that. +// +// This function is *deprecated* and will be removed in a future version. +// It is here for backwards compatibility. Users should migrate to the +// `sentry_transport_new` API. +// +function sentry_new_function_transport( + func: _sentry_transport_new_func; + data: Pointer +): psentry_transport_t; cdecl; external sentry_dll delayed; + +// +// This represents an interface for user-defined backends. +// +// Backends are responsible to handle crashes. They are maintained at runtime +// via various life-cycle hooks from the sentry-core. +// +// At this point none of those interfaces are exposed in the API including +// creation and destruction. The main use-case of the backend in the API at this +// point is to disable it via `sentry_options_set_backend` at runtime before it +// is initialized. +// +type + sentry_backend_s = record end; + sentry_backend_t = sentry_backend_s; + psentry_backend_t = Pointer; + // -- Options APIs -- // -// The state of user consent. -// -type sentry_user_consent_t = ( - SENTRY_USER_CONSENT_UNKNOWN = -1, - SENTRY_USER_CONSENT_GIVEN = 1, - SENTRY_USER_CONSENT_REVOKED = 0 -); +// The state of user consent. +// +type sentry_user_consent_t = ( + SENTRY_USER_CONSENT_UNKNOWN = -1, + SENTRY_USER_CONSENT_GIVEN = 1, + SENTRY_USER_CONSENT_REVOKED = 0 +); // // Creates a new options struct. @@ -819,20 +879,20 @@ procedure sentry_options_set_transport( ); cdecl; external sentry_dll delayed; // -// Type of the `before_send` callback. -// -// The callback takes ownership of the `event`, and should usually return that -// same event. In case the event should be discarded, the callback needs to -// call `sentry_value_decref` on the provided event, and return a -// `sentry_value_new_null()` instead. -// -// This function may be invoked inside of a signal handler and must be safe for -// that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. -// On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see -// the documentation on SEH (structured exception handling) for more information -// https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling -// -type +// Type of the `before_send` callback. +// +// The callback takes ownership of the `event`, and should usually return that +// same event. In case the event should be discarded, the callback needs to +// call `sentry_value_decref` on the provided event, and return a +// `sentry_value_new_null()` instead. +// +// This function may be invoked inside of a signal handler and must be safe for +// that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. +// On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see +// the documentation on SEH (structured exception handling) for more information +// https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling +// +type sentry_event_function_t = function( event: sentry_value_t; hint: Pointer; @@ -841,15 +901,80 @@ type // // Sets the `before_send` callback. -// -// See the `sentry_event_function_t` typedef above for more information. -// +// +// See the `sentry_event_function_t` typedef above for more information. +// procedure sentry_options_set_before_send( opts: psentry_options_t; func: sentry_event_function_t; data: Pointer ); cdecl; external sentry_dll delayed; +// +// Type of the `on_crash` callback. +// +// The `on_crash` callback replaces the `before_send` callback for crash events. +// The interface is analogous to `before_send` in that the callback takes +// ownership of the `event`, and should usually return that same event. In case +// the event should be discarded, the callback needs to call +// `sentry_value_decref` on the provided event, and return a +// `sentry_value_new_null()` instead. +// +// Only the `inproc` backend currently fills the passed-in event with useful +// data and processes any modifications to the return value. Since both +// `breakpad` and `crashpad` use minidumps to capture the crash state, the +// passed-in event is empty when using these backends, and they ignore any +// changes to the return value. +// +// If you set this callback in the options, it prevents a concurrently enabled +// `before_send` callback from being invoked in the crash case. This allows for +// better differentiation between crashes and other events and gradual migration +// from existing `before_send` implementations: +// +// - if you have a `before_send` implementation and do not define an `on_crash` +// callback your application will receive both normal and crash events as +// before +// - if you have a `before_send` implementation but only want to handle normal +// events with it, then you can define an `on_crash` callback that returns +// the passed-in event and does nothing else +// - if you are not interested in normal events, but only want to act on +// crashes (within the limits mentioned below), then only define an +// `on_crash` callback with the option to filter (on all backends) or enrich +// (only inproc) the crash event +// +// This function may be invoked inside of a signal handler and must be safe for +// that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. +// On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see +// the documentation on SEH (structured exception handling) for more information +// https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling +// +// Platform-specific behavior: +// +// - does not work with crashpad on macOS. +// - for breakpad on Linux the `uctx` parameter is always NULL. +// - on Windows the crashpad backend can capture fast-fail crashes which +// by-pass SEH. Since `on_crash` is called by a local exception-handler, it will +// not be invoked when such a crash happened, even though a minidump will be +// sent. +// +type + sentry_crash_function_t = function( + const uctx: psentry_ucontext_t; + event: sentry_value_t; + closure: Pointer + ): sentry_value_t; cdecl; + +// +// Sets the `on_crash` callback. +// +// See the `sentry_crash_function_t` typedef above for more information. +// +procedure {SENTRY_API} sentry_options_set_on_crash( + opts: psentry_options_t; + func: sentry_crash_function_t; + data: Pointer +); cdecl; external sentry_dll delayed; + // // Sets the DSN. // @@ -866,21 +991,21 @@ function sentry_options_get_dsn( ): PAnsiChar; cdecl; external sentry_dll delayed; // -// Sets the sample rate, which should be a double between `0.0` and `1.0`. -// Sentry will randomly discard any event that is captured using -// `sentry_capture_event` when a sample rate < 1 is set. -// -procedure sentry_options_set_sample_rate( - opts: psentry_options_t; - sample_rate: double -); cdecl; external sentry_dll delayed; - -// -// Gets the sample rate. -// -function sentry_options_get_sample_rate( - const opts: psentry_options_t -): double; cdecl; external sentry_dll delayed; +// Sets the sample rate, which should be a double between `0.0` and `1.0`. +// Sentry will randomly discard any event that is captured using +// `sentry_capture_event` when a sample rate < 1 is set. +// +procedure sentry_options_set_sample_rate( + opts: psentry_options_t; + sample_rate: double +); cdecl; external sentry_dll delayed; + +// +// Gets the sample rate. +// +function sentry_options_get_sample_rate( + const opts: psentry_options_t +): double; cdecl; external sentry_dll delayed; // // Sets the release. @@ -961,20 +1086,20 @@ function sentry_options_get_ca_certs( ): PAnsiChar; cdecl; external sentry_dll delayed; // -// Configures the name of the http transport thread. -// -procedure sentry_options_set_transport_thread_name( - opts: psentry_options_t; - const name: PAnsiChar -); cdecl; external sentry_dll delayed; - -// -// Returns the configured http transport thread name. -// -function sentry_options_get_transport_thread_name( - const opts: psentry_options_t -): PAnsiChar; cdecl; external sentry_dll delayed; - +// Configures the name of the http transport thread. +// +procedure sentry_options_set_transport_thread_name( + opts: psentry_options_t; + const name: PAnsiChar +); cdecl; external sentry_dll delayed; + +// +// Returns the configured http transport thread name. +// +function sentry_options_get_transport_thread_name( + const opts: psentry_options_t +): PAnsiChar; cdecl; external sentry_dll delayed; + // // Enables or disables debug printing mode. // @@ -991,111 +1116,111 @@ function sentry_options_get_debug( ): Integer; cdecl; external sentry_dll delayed; // -// Sets the number of breadcrumbs being tracked and attached to events. -// -// Defaults to 100. -// -procedure sentry_options_set_max_breadcrumbs( - opts: psentry_options_t; - max_breadcrumbs: NativeUInt -); cdecl; external sentry_dll delayed; - -// -// Gets the number of breadcrumbs being tracked and attached to events. -// -function sentry_options_get_max_breadcrumbs( - const opts: psentry_options_t -): NativeUInt; cdecl; external sentry_dll delayed; - -// -// Type of the callback for logger function. -// -type - sentry_logger_function_t = procedure( - level: sentry_level_t; - const message: PAnsiChar; - args: va_list; - userdata: Pointer - ); cdecl; - -// -// Sets the sentry-native logger function. -// -// Used for logging debug events when the `debug` option is set to true. -// -procedure sentry_options_set_logger( - opts: psentry_options_t; - func: sentry_logger_function_t; - userdata: Pointer -); cdecl; external sentry_dll delayed; - -// -// Enables or disables automatic session tracking. -// -// Automatic session tracking is enabled by default and is equivalent to calling -// `sentry_start_session` after startup. -// There can only be one running session, and the current session will always be -// closed implicitly by `sentry_close`, when starting a new session with -// `sentry_start_session`, or manually by calling `sentry_end_session`. -// -procedure sentry_options_set_auto_session_tracking( - opts: psentry_options_t; - val: Integer -); cdecl; external sentry_dll delayed; - -// -// Returns true if automatic session tracking is enabled. -// -function sentry_options_get_auto_session_tracking( - const opts: psentry_options_t +// Sets the number of breadcrumbs being tracked and attached to events. +// +// Defaults to 100. +// +procedure sentry_options_set_max_breadcrumbs( + opts: psentry_options_t; + max_breadcrumbs: NativeUInt +); cdecl; external sentry_dll delayed; + +// +// Gets the number of breadcrumbs being tracked and attached to events. +// +function sentry_options_get_max_breadcrumbs( + const opts: psentry_options_t +): NativeUInt; cdecl; external sentry_dll delayed; + +// +// Type of the callback for logger function. +// +type + sentry_logger_function_t = procedure( + level: sentry_level_t; + const message: PAnsiChar; + args: va_list; + userdata: Pointer + ); cdecl; + +// +// Sets the sentry-native logger function. +// +// Used for logging debug events when the `debug` option is set to true. +// +procedure sentry_options_set_logger( + opts: psentry_options_t; + func: sentry_logger_function_t; + userdata: Pointer +); cdecl; external sentry_dll delayed; + +// +// Enables or disables automatic session tracking. +// +// Automatic session tracking is enabled by default and is equivalent to calling +// `sentry_start_session` after startup. +// There can only be one running session, and the current session will always be +// closed implicitly by `sentry_close`, when starting a new session with +// `sentry_start_session`, or manually by calling `sentry_end_session`. +// +procedure sentry_options_set_auto_session_tracking( + opts: psentry_options_t; + val: Integer +); cdecl; external sentry_dll delayed; + +// +// Returns true if automatic session tracking is enabled. +// +function sentry_options_get_auto_session_tracking( + const opts: psentry_options_t +): Integer; cdecl; external sentry_dll delayed; + +// +// Enables or disabled user consent requirements for uploads. +// +// This disables uploads until the user has given the consent to the SDK. +// Consent itself is given with `sentry_user_consent_give` and +// `sentry_user_consent_revoke`. +// +procedure sentry_options_set_require_user_consent( + opts: psentry_options_t; + val: Integer); cdecl; external sentry_dll delayed; + +// +// Returns true if user consent is required. +// +function sentry_options_get_require_user_consent( + const opts: psentry_options_t ): Integer; cdecl; external sentry_dll delayed; // -// Enables or disabled user consent requirements for uploads. -// -// This disables uploads until the user has given the consent to the SDK. -// Consent itself is given with `sentry_user_consent_give` and -// `sentry_user_consent_revoke`. -// -procedure sentry_options_set_require_user_consent( - opts: psentry_options_t; - val: Integer); cdecl; external sentry_dll delayed; - -// -// Returns true if user consent is required. -// -function sentry_options_get_require_user_consent( - const opts: psentry_options_t -): Integer; cdecl; external sentry_dll delayed; - -// -// Enables or disables on-device symbolication of stack traces. -// -// This feature can have a performance impact, and is enabled by default on -// Android. It is usually only needed when it is not possible to provide debug -// information files for system libraries which are needed for serverside -// symbolication. -// -procedure sentry_options_set_symbolize_stacktraces( - opts: psentry_options_t; - val: Integer -); cdecl; external sentry_dll delayed; - -// -// Returns true if on-device symbolication of stack traces is enabled. -// -function sentry_options_get_symbolize_stacktraces( - const opts: psentry_options_t -): Integer; cdecl; external sentry_dll delayed; - +// Enables or disables on-device symbolication of stack traces. +// +// This feature can have a performance impact, and is enabled by default on +// Android. It is usually only needed when it is not possible to provide debug +// information files for system libraries which are needed for serverside +// symbolication. +// +procedure sentry_options_set_symbolize_stacktraces( + opts: psentry_options_t; + val: Integer +); cdecl; external sentry_dll delayed; + +// +// Returns true if on-device symbolication of stack traces is enabled. +// +function sentry_options_get_symbolize_stacktraces( + const opts: psentry_options_t +): Integer; cdecl; external sentry_dll delayed; + // // Adds a new attachment to be sent along. // -// `path` is assumed to be in platform-specific filesystem path encoding. -// API Users on windows are encouraged to use `sentry_options_add_attachmentw` -// instead. -// +// `path` is assumed to be in platform-specific filesystem path encoding. +// API Users on windows are encouraged to use `sentry_options_add_attachmentw` +// instead. +// procedure sentry_options_add_attachment( opts: psentry_options_t; const path: PAnsiChar @@ -1105,15 +1230,15 @@ procedure sentry_options_add_attachment( // Sets the path to the crashpad handler if the crashpad backend is used. // // The path defaults to the `crashpad_handler`/`crashpad_handler.exe` -// executable, depending on platform, which is expected to be present in the -// same directory as the app executable. -// -// It is recommended that library users set an explicit handler path, depending +// executable, depending on platform, which is expected to be present in the +// same directory as the app executable. +// +// It is recommended that library users set an explicit handler path, depending // on the directory/executable structure of their app. // -// `path` is assumed to be in platform-specific filesystem path encoding. -// API Users on windows are encouraged to use `sentry_options_set_handler_pathw` -// instead. +// `path` is assumed to be in platform-specific filesystem path encoding. +// API Users on windows are encouraged to use `sentry_options_set_handler_pathw` +// instead. // procedure sentry_options_set_handler_path( opts: psentry_options_t; @@ -1122,33 +1247,33 @@ procedure sentry_options_set_handler_path( // // Sets the path to the Sentry Database Directory. -// -// Sentry will use this path to persist user consent, sessions, and other -// artifacts in case of a crash. This will also be used by the crashpad backend -// if it is configured. -// -// The directory is used for "cached" data, which needs to persist across -// application restarts to ensure proper flagging of release-health sessions, -// but might otherwise be safely purged regularly. -// -// It is roughly equivalent to the type of `AppData/Local` on Windows and -// `XDG_CACHE_HOME` on Linux, and equivalent runtime directories on other -// platforms. -// -// It is recommended that users set an explicit absolute path, depending -// on their apps runtime directory. The path will be created if it does not -// exist, and will be resolved to an absolute path inside of `sentry_init`. The -// directory should not be shared with other application data/configuration, as -// sentry-native will enumerate and possibly delete files in that directory. An -// example might be `$XDG_CACHE_HOME/your-app/sentry` -// -// If no explicit path it set, sentry-native will default to `.sentry-native` in -// the current working directory, with no specific platform-specific handling. -// -// `path` is assumed to be in platform-specific filesystem path encoding. -// API Users on windows are encouraged to use -// `sentry_options_set_database_pathw` instead. -// +// +// Sentry will use this path to persist user consent, sessions, and other +// artifacts in case of a crash. This will also be used by the crashpad backend +// if it is configured. +// +// The directory is used for "cached" data, which needs to persist across +// application restarts to ensure proper flagging of release-health sessions, +// but might otherwise be safely purged regularly. +// +// It is roughly equivalent to the type of `AppData/Local` on Windows and +// `XDG_CACHE_HOME` on Linux, and equivalent runtime directories on other +// platforms. +// +// It is recommended that users set an explicit absolute path, depending +// on their apps runtime directory. The path will be created if it does not +// exist, and will be resolved to an absolute path inside of `sentry_init`. The +// directory should not be shared with other application data/configuration, as +// sentry-native will enumerate and possibly delete files in that directory. An +// example might be `$XDG_CACHE_HOME/your-app/sentry` +// +// If no explicit path it set, sentry-native will default to `.sentry-native` in +// the current working directory, with no specific platform-specific handling. +// +// `path` is assumed to be in platform-specific filesystem path encoding. +// API Users on windows are encouraged to use +// `sentry_options_set_database_pathw` instead. +// procedure sentry_options_set_database_path( opts: psentry_options_t; const path: PAnsiChar @@ -1193,6 +1318,37 @@ procedure sentry_options_set_system_crash_reporter_enabled( enabled: Integer ); cdecl; external sentry_dll delayed; +// +// Sets the maximum time (in milliseconds) to wait for the asynchronous tasks to +// end on shutdown, before attempting a forced termination. +// +procedure {SENTRY_API} sentry_options_set_shutdown_timeout( + opts: psentry_options_t; + shutdown_timeout: UInt64 +); cdecl; external sentry_dll delayed; + +// +// Gets the maximum time (in milliseconds) to wait for the asynchronous tasks to +// end on shutdown, before attempting a forced termination. +// +function {SENTRY_API} sentry_options_get_shutdown_timeout( + opts: psentry_options_t +): UInt64; cdecl; external sentry_dll delayed; + +// +// Sets a user-defined backend. +// +// Since creation and destruction of backends is not exposed in the API, this +// can only be used to set the backend to `NULL`, which disables the backend in +// the initialization. +// +procedure {SENTRY_API} sentry_options_set_backend( + opts: psentry_options_t; + backend: psentry_backend_t +); cdecl; external sentry_dll delayed; + + + // -- Global APIs -- // @@ -1201,33 +1357,45 @@ procedure sentry_options_set_system_crash_reporter_enabled( // This takes ownership of the options. After the options have been set they // cannot be modified any more. // Depending on the configured transport and backend, this function might not be -// fully thread-safe. -// Returns 0 on success. +// fully thread-safe. +// Returns 0 on success. // function sentry_init( options: psentry_options_t ): Integer; cdecl; external sentry_dll delayed; +// +// Instructs the transport to flush its send queue. +// +// The `timeout` parameter is in milliseconds. +// +// Returns 0 on success, or a non-zero return value in case the timeout is hit. +// +function {SENTRY_API} sentry_flush( + timeout: UInt64 +): Integer; cdecl; external sentry_dll delayed; + + +// +// Shuts down the sentry client and forces transports to flush out. +// +// Returns 0 on success. +// +function sentry_close: Integer; cdecl; external sentry_dll delayed; + // // Shuts down the sentry client and forces transports to flush out. // -// Returns 0 on success. -// -function sentry_close: Integer; cdecl; external sentry_dll delayed; - -// -// Shuts down the sentry client and forces transports to flush out. -// -// This is a **deprecated** alias for `sentry_close`. -// -// Returns 0 on success. -// -function sentry_shutdown: Integer; cdecl; external sentry_dll delayed; - -// -// This will lazily load and cache a list of all the loaded libraries. -// -// Returns a new reference to an immutable, frozen list. +// This is a **deprecated** alias for `sentry_close`. +// +// Returns 0 on success. +// +function sentry_shutdown: Integer; cdecl; external sentry_dll delayed; + +// +// This will lazily load and cache a list of all the loaded libraries. +// +// Returns a new reference to an immutable, frozen list. // The reference must be released with `sentry_value_decref`. // function sentry_get_modules_list: sentry_value_t; cdecl; external sentry_dll delayed; @@ -1236,43 +1404,43 @@ function sentry_get_modules_list: sentry_value_t; cdecl; external sentry_dll de // Clears the internal module cache. // // For performance reasons, sentry will cache the list of loaded libraries when -// capturing events. This cache can get out-of-date when loading or unloading -// libraries at runtime. It is therefore recommended to call -// `sentry_clear_modulecache` when doing so, to make sure that the next call to +// capturing events. This cache can get out-of-date when loading or unloading +// libraries at runtime. It is therefore recommended to call +// `sentry_clear_modulecache` when doing so, to make sure that the next call to // `sentry_capture_event` will have an up-to-date module list. // procedure sentry_clear_modulecache; cdecl; external sentry_dll delayed; // // Re-initializes the Sentry backend. -// -// This is needed if a third-party library overrides the previously installed -// signal handler. Calling this function can be potentially dangerous and should -// only be done when necessary. -// -// Returns 0 on success. -// +// +// This is needed if a third-party library overrides the previously installed +// signal handler. Calling this function can be potentially dangerous and should +// only be done when necessary. +// +// Returns 0 on success. +// function sentry_reinstall_backend: Integer; cdecl; external sentry_dll delayed; // -// Gives user consent. -// -procedure sentry_user_consent_give; external sentry_dll delayed; - -// -// Revokes user consent. -// -procedure sentry_user_consent_revoke; external sentry_dll delayed; - -// -// Resets the user consent (back to unknown). -// -procedure sentry_user_consent_reset; external sentry_dll delayed; - -// -// Checks the current state of user consent. -// -function sentry_user_consent_get: sentry_user_consent_t; external sentry_dll delayed; +// Gives user consent. +// +procedure sentry_user_consent_give; external sentry_dll delayed; + +// +// Revokes user consent. +// +procedure sentry_user_consent_revoke; external sentry_dll delayed; + +// +// Resets the user consent (back to unknown). +// +procedure sentry_user_consent_reset; external sentry_dll delayed; + +// +// Checks the current state of user consent. +// +function sentry_user_consent_get: sentry_user_consent_t; external sentry_dll delayed; // // Sends a sentry event. @@ -1282,14 +1450,14 @@ function sentry_capture_event( ): sentry_uuid_t; cdecl; external sentry_dll delayed; // -// Captures an exception to be handled by the backend. -// -// This is safe to be called from a crashing thread and may not return. -// -procedure sentry_handle_exception( - const uctx: psentry_ucontext_t -); cdecl; external sentry_dll delayed; - +// Captures an exception to be handled by the backend. +// +// This is safe to be called from a crashing thread and may not return. +// +procedure sentry_handle_exception( + const uctx: psentry_ucontext_t +); cdecl; external sentry_dll delayed; + // // Adds the breadcrumb to be sent in case of an event. // @@ -1357,8 +1525,8 @@ procedure sentry_remove_context( // // Sets the event fingerprint. // -// This accepts a variable number of arguments, and needs to be terminated by a -// trailing `NULL`. +// This accepts a variable number of arguments, and needs to be terminated by a +// trailing `NULL`. // procedure sentry_set_fingerprint( const fingerprint: PAnsiChar @@ -1389,36 +1557,43 @@ procedure sentry_set_level( level: sentry_level_t ); cdecl; external sentry_dll delayed; - // -// Starts a new session. -// -procedure sentry_start_session; cdecl; external sentry_dll delayed; - -// -// Ends a session. -// -procedure sentry_end_session; cdecl; external sentry_dll delayed; - - // - // Sets the path to sentry.dll; this must - // be called before any other sentry apis - // - procedure sentry_set_library_path(const path: string); - - implementation - -uses - System.SysUtils; - -procedure sentry_set_library_path(const path: string); -begin - if LoadLibrary(PChar(path)) = 0 then - RaiseLastOSError; -end; - -procedure sentry_string_free(str: PAnsiChar); cdecl; -begin - sentry_free(str); -end; - -end. + +// +// Starts a new session. +// +procedure sentry_start_session; cdecl; external sentry_dll delayed; + +// +// Ends a session. +// +procedure sentry_end_session; cdecl; external sentry_dll delayed; + + +// + +// Sets the path to sentry.dll; this must + +// be called before any other sentry apis + +// + +procedure sentry_set_library_path(const path: string); + + +implementation + +uses + System.SysUtils; + +procedure sentry_set_library_path(const path: string); +begin + if LoadLibrary(PChar(path)) = 0 then + RaiseLastOSError; +end; + +procedure sentry_string_free(str: PAnsiChar); cdecl; +begin + sentry_free(str); +end; + +end. diff --git a/common/windows/delphi/ext/sentry/sentry.x64.dll b/common/windows/delphi/ext/sentry/sentry.x64.dll index 9737a5acc0c142bb7f0878c61bd323c22ac667d8..5587ebaa0c60a6dc3370110a3fc7291a4d6af7b4 100644 Binary files a/common/windows/delphi/ext/sentry/sentry.x64.dll and b/common/windows/delphi/ext/sentry/sentry.x64.dll differ diff --git a/common/windows/delphi/ext/sentry/sentry.x64.lib b/common/windows/delphi/ext/sentry/sentry.x64.lib index 1a9f57d57c02ae950edb4656636f493aec7a3e0e..31fe0927543615010591537ab95d8a1f2528c683 100644 Binary files a/common/windows/delphi/ext/sentry/sentry.x64.lib and b/common/windows/delphi/ext/sentry/sentry.x64.lib differ diff --git a/common/windows/delphi/ext/sentry/test/Sentry.Client.Test.SentryClientTestMain.pas b/common/windows/delphi/ext/sentry/test/Sentry.Client.Test.SentryClientTestMain.pas index c9f33f6533008375d2b1e1d303bcb56db6a2e93d..49fe1bc91c1e52a48e0913e9ce7d7abda8eea5de 100644 --- a/common/windows/delphi/ext/sentry/test/Sentry.Client.Test.SentryClientTestMain.pas +++ b/common/windows/delphi/ext/sentry/test/Sentry.Client.Test.SentryClientTestMain.pas @@ -7,6 +7,7 @@ procedure main; implementation uses + sentry, Sentry.Client, System.SysUtils; @@ -29,7 +30,7 @@ begin //o.DSN := 'https://7b1ff1dae2c8495b84f90dadcf512b84@sentry.io/4853461'; o.Release := 'keyman-14.0.22-alpha-local'; o.HandlerPath := 'c:\Projects\keyman\app\windows\src\ext\sentry\test\Win32\Release\crashpad_handler.exe'; - o.DatabasePath := '.\sentry-0.4.9-db'; + o.DatabasePath := '.\sentry-'+SENTRY_SDK_VERSION+'-db'; Result := TSentryClient.Create(o); end; diff --git a/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas b/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas index 5504f66f9ec72384a9cb3292696fef5250f5abfb..8c195f059d80be5e4e42f65efb674e212b93571a 100644 --- a/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas +++ b/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas @@ -375,7 +375,7 @@ begin end; o.HandlerPath := ExtractFilePath(path) + 'crashpad_handler.exe'; - o.DatabasePath := TKeymanPaths.ErrorLogPath + 'sentry-0.4.9-db'; + o.DatabasePath := TKeymanPaths.ErrorLogPath + 'sentry-'+SENTRY_SDK_VERSION+'-db'; FClient := SentryClientClass.Create(o, ALogger, f); FClient.OnAfterEvent := ClientAfterEvent; diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas index fcb51ec7d0a92ef5c3302f0297a15485ee6e7498..57f865f9b29ab9ff6165f5037d951f9afb6caedb 100644 --- a/common/windows/delphi/general/RegistryKeys.pas +++ b/common/windows/delphi/general/RegistryKeys.pas @@ -393,6 +393,8 @@ const SRegValue_IDEOptAutoSaveBeforeCompiling = 'auto save before compiling'; // CU SRegValue_IDEOptOSKAutoSaveBeforeImporting = 'osk auto save before importing'; // CU + SRegValue_IDEOptUseLegacyCompiler = 'use legacy compiler'; // CU + // Note: keeping 'web host port' reg value name to ensure settings maintained // from version 14.0 and earlier of Keyman Developer. Other values are // new with Keyman Developer 15.0 diff --git a/common/windows/delphi/packages/PackageInfo.pas b/common/windows/delphi/packages/PackageInfo.pas index dd4dbcd1106322240c4e8a0d574c54e7dad2a621..e468a9ceb741971a2d873096f8c19959b8173bef 100644 --- a/common/windows/delphi/packages/PackageInfo.pas +++ b/common/windows/delphi/packages/PackageInfo.pas @@ -459,7 +459,6 @@ const SXML_PackageLexicalModel = 'LexicalModel'; SXML_PackageLexicalModel_Name = 'Name'; SXML_PackageLexicalModel_ID = 'ID'; - SXML_PackageLexicalModel_Version = 'Version'; SXML_PackageLexicalModel_RTL = 'RTL'; SXML_PackageLexicalModel_Languages = 'Languages'; @@ -2213,7 +2212,7 @@ begin lexicalModel := TPackageLexicalModel.Create(Package); lexicalModel.Name := XmlVarToStr(ALexicalModel.ChildValues[SXML_PackageLexicalModel_Name]); lexicalModel.ID := XmlVarToStr(ALexicalModel.ChildValues[SXML_PackageLexicalModel_ID]); - lexicalModel.RTL := ANode.ChildNodes.IndexOf(SXML_PackageLexicalModel_RTL) >= 0; + lexicalModel.RTL := ALexicalModel.ChildNodes.IndexOf(SXML_PackageLexicalModel_RTL) >= 0; lexicalModel.Languages.LoadXML(ALexicalModel); Add(lexicalModel); end; diff --git a/core/.gitignore b/core/.gitignore index e71a856da391c0a9fe861bc905e6d2aa7704f614..b02aacc47369170f728d0bb46c2068c69d2d4314 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,6 +1,7 @@ .vs/ build/ configure/ +resources/ # cross-compile for meson wasm, built from wasm.build..in wasm.build @@ -10,8 +11,6 @@ TIER.md VERSION.md shellHelperFunctions.sh -src/rust/keyman_keyboard_processor/target/ - # The following files/subdirs are generated by autotools when building ibus-keyman. # They can be removed after we convert ibus-keyman to use meson. .deps/ diff --git a/core/build.sh b/core/build.sh index caaeafb9292eac05de4d2beaf0a2e98f03eb5877..9779419202915c6031fe0ff0c1a039eac58344d3 100755 --- a/core/build.sh +++ b/core/build.sh @@ -93,12 +93,6 @@ if builder_is_dep_build || builder_has_option --no-tests; then builder_remove_dep /developer/src/kmc fi -if builder_is_debug_build; then - CONFIGURATION=debug -else - CONFIGURATION=release -fi - # 'mac' target builds both x86_64 and arm architectures and # generates a 'fat' library from them. builder_describe_internal_dependency \ @@ -106,20 +100,28 @@ builder_describe_internal_dependency \ build:mac build:mac-arm64 builder_describe_outputs \ - configure:x86 /core/build/x86/$CONFIGURATION/build.ninja \ - configure:x64 /core/build/x64/$CONFIGURATION/build.ninja \ - configure:mac /core/build/mac/$CONFIGURATION/ \ - configure:mac-x86_64 /core/build/mac-x86_64/$CONFIGURATION/build.ninja \ - configure:mac-arm64 /core/build/mac-arm64/$CONFIGURATION/build.ninja \ - configure:arch /core/build/arch/$CONFIGURATION/build.ninja \ - configure:wasm /core/build/wasm/$CONFIGURATION/build.ninja \ - build:x86 /core/build/x86/$CONFIGURATION/src/libkmnkbp0.a \ - build:x64 /core/build/x64/$CONFIGURATION/src/libkmnkbp0.a \ - build:mac /core/build/mac/$CONFIGURATION/libkmnkbp0.a \ - build:mac-x86_64 /core/build/mac-x86_64/$CONFIGURATION/src/libkmnkbp0.a \ - build:mac-arm64 /core/build/mac-arm64/$CONFIGURATION/src/libkmnkbp0.a \ - build:arch /core/build/arch/$CONFIGURATION/src/libkmnkbp0.a \ - build:wasm /core/build/wasm/$CONFIGURATION/src/libkmnkbp0.a + configure:x86 /core/build/x86/$BUILDER_CONFIGURATION/build.ninja \ + configure:x64 /core/build/x64/$BUILDER_CONFIGURATION/build.ninja \ + configure:mac /core/build/mac/$BUILDER_CONFIGURATION/ \ + configure:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/build.ninja \ + configure:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/build.ninja \ + configure:arch /core/build/arch/$BUILDER_CONFIGURATION/build.ninja \ + configure:wasm /core/build/wasm/$BUILDER_CONFIGURATION/build.ninja \ + build:x86 /core/build/x86/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \ + build:x64 /core/build/x64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \ + build:mac /core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a \ + build:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \ + build:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \ + build:arch /core/build/arch/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \ + build:wasm /core/build/wasm/$BUILDER_CONFIGURATION/src/libkmnkbp0.a + +# Import our standard compiler defines; this is copied from +# /resources/build/meson/standard.meson.build by build.sh, because meson doesn't +# allow us to reference a file outside its root +if builder_has_action configure; then + mkdir -p "$THIS_SCRIPT_PATH/resources" + cp "$KEYMAN_ROOT/resources/build/meson/standard.meson.build" "$THIS_SCRIPT_PATH/resources/meson.build" +fi # Iterate through all possible targets; note that targets that cannot be built # on the current platform have already been excluded through the archtargets @@ -129,7 +131,7 @@ targets=(wasm x86 x64 mac-x86_64 mac-arm64 arch) do_action() { local action_function=do_$1 for target in "${targets[@]}"; do - MESON_PATH="$KEYMAN_ROOT/core/build/$target/$CONFIGURATION" + MESON_PATH="$KEYMAN_ROOT/core/build/$target/$BUILDER_CONFIGURATION" $action_function $target done } @@ -145,7 +147,7 @@ do_action configure # After we have built the necessary internal dependencies, then we can go # ahead and build a fat library for external consumption if builder_start_action configure:mac; then - mkdir -p "$KEYMAN_ROOT/core/build/mac/$CONFIGURATION" + mkdir -p "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION" builder_finish_action success configure:mac fi @@ -155,9 +157,9 @@ do_action build if builder_start_action build:mac; then lipo -create \ - "$KEYMAN_ROOT/core/build/mac-x86_64/$CONFIGURATION/src/libkmnkbp0.a" \ - "$KEYMAN_ROOT/core/build/mac-arm64/$CONFIGURATION/src/libkmnkbp0.a" \ - -output "$KEYMAN_ROOT/core/build/mac/$CONFIGURATION/libkmnkbp0.a" + "$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \ + "$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \ + -output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a" builder_finish_action success build:mac fi @@ -170,7 +172,7 @@ if builder_start_action test:mac; then # assume that build:mac has run so both architectures will be # available target=mac-`uname -m` - MESON_PATH="$KEYMAN_ROOT/core/build/$target/$CONFIGURATION" + MESON_PATH="$KEYMAN_ROOT/core/build/$target/$BUILDER_CONFIGURATION" meson test -C "$MESON_PATH" "${builder_extra_params[@]}" builder_finish_action success test:mac fi diff --git a/core/commands.inc.sh b/core/commands.inc.sh index f3eda70e6d6960adf34504d051090daf09811665..2e5fb9e7b9ffda235b6d665e250d27e69fc760f1 100644 --- a/core/commands.inc.sh +++ b/core/commands.inc.sh @@ -37,11 +37,11 @@ do_configure() { fi if [[ $target =~ ^(x86|x64)$ ]]; then - cmd //C build.bat $target $CONFIGURATION configure $BUILD_BAT_keyman_core_tests "${builder_extra_params[@]}" + cmd //C build.bat $target $BUILDER_CONFIGURATION configure $BUILD_BAT_keyman_core_tests "${builder_extra_params[@]}" else pushd "$THIS_SCRIPT_PATH" > /dev/null # Additional arguments are used by Linux build, e.g. -Dprefix=${INSTALLDIR} - meson setup "$MESON_PATH" $MESON_CROSS_FILE --werror --buildtype $CONFIGURATION $STANDARD_MESON_ARGS "${builder_extra_params[@]}" + meson setup "$MESON_PATH" $MESON_CROSS_FILE --werror --buildtype $BUILDER_CONFIGURATION $STANDARD_MESON_ARGS "${builder_extra_params[@]}" popd > /dev/null fi @@ -56,7 +56,7 @@ do_build() { local target=$1 builder_start_action build:$target || return 0 if [[ $target =~ ^(x86|x64)$ ]]; then - cmd //C build.bat $target $CONFIGURATION build "${builder_extra_params[@]}" + cmd //C build.bat $target $BUILDER_CONFIGURATION build "${builder_extra_params[@]}" elif $MESON_LOW_VERSION; then pushd "$MESON_PATH" > /dev/null ninja @@ -75,7 +75,7 @@ do_test() { local target=$1 builder_start_action test:$target || return 0 if [[ $target =~ ^(x86|x64)$ ]]; then - cmd //C build.bat $target $CONFIGURATION test "${builder_extra_params[@]}" + cmd //C build.bat $target $BUILDER_CONFIGURATION test "${builder_extra_params[@]}" else meson test -C "$MESON_PATH" "${builder_extra_params[@]}" fi diff --git a/core/cross-mac-arm64.build b/core/cross-mac-arm64.build index c2b64924dc4a1ea8875a0a0700717d2c11f6bc38..720e9f31007b1cd60414791c6a73dca9f42913fd 100644 --- a/core/cross-mac-arm64.build +++ b/core/cross-mac-arm64.build @@ -12,4 +12,5 @@ ar = 'ar' ld = 'ld' strip = 'strip' lipo = 'lipo' -ranlib = 'ranlib' \ No newline at end of file +ranlib = 'ranlib' +pkgconfig = 'pkg-config' diff --git a/core/cross-mac-x86_64.build b/core/cross-mac-x86_64.build index cff18e943c505aca98dbfab4e3d7feefb03a8dc1..2ab2c62a600f3f552f30a93d807dad22594e716d 100644 --- a/core/cross-mac-x86_64.build +++ b/core/cross-mac-x86_64.build @@ -12,4 +12,5 @@ ar = 'ar' ld = 'ld' strip = 'strip' lipo = 'lipo' -ranlib = 'ranlib' \ No newline at end of file +ranlib = 'ranlib' +pkgconfig = 'pkg-config' diff --git a/core/include/ldml/build.sh b/core/include/ldml/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..2f4b76ab8edd3591119689609dd8a65aa63b4c8a --- /dev/null +++ b/core/include/ldml/build.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# Packages the @keymanapp/ldml-keyboard-constants package. +# + +# Exit on command failure and when using unset variables: +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +cd "$THIS_SCRIPT_PATH" + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +builder_describe "Build Keyman ldml-keyboard-constants package" \ + "@/common/web/keyman-version" \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "pack build a local .tgz pack for testing" \ + "publish publish to npm" \ + "--dry-run,-n don't actually publish, just dry run" + +builder_describe_outputs \ + configure /node_modules \ + build /core/include/ldml/build/keyboardprocessor_ldml.js + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action clean; then + rm -rf ./build/ + builder_finish_action success clean +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action configure; then + verify_npm_setup + builder_finish_action success configure +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action build; then + tsc --build + builder_finish_action success build +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action test; then + # no tests at this time + builder_finish_action success test +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action publish; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm + builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack +fi diff --git a/core/include/ldml/package.json b/core/include/ldml/package.json index d74a720d1ea07e51ca41b12cf1f968704b445c53..76da479930bbbb520111e5f8076f50a4829ca59e 100644 --- a/core/include/ldml/package.json +++ b/core/include/ldml/package.json @@ -7,6 +7,9 @@ "ldml", "unicode" ], + "files": [ + "/build/keyboardprocessor_ldml.*" + ], "license": "MIT", "type": "module", "main": "build/keyboardprocessor_ldml.js", diff --git a/core/meson.build b/core/meson.build index 47b8860389e3c670a1053ac5aa2da96533dbf824..81d994124c466e7d7a1f932fae32b1ef62b30f4e 100644 --- a/core/meson.build +++ b/core/meson.build @@ -14,27 +14,19 @@ project('keyboardprocessor', 'cpp', 'c', 'warning_level=2'], meson_version: '>=0.53.0') -compiler = meson.get_compiler('cpp') +# Import our standard compiler defines; this is copied from +# /resources/build/standard.meson.build by build.sh, because +# meson doesn't allow us to reference a file outside its root +subdir('resources') lib_version = '0.0.0' py = import('python') python = py.find_installation() -# Once we can assume meson 0.60 we can delete this -# (https://mesonbuild.com/Release-notes-for-0-60-0.html#msvc-compiler-now-assumes-utf8-source-code-by-default) -if compiler.get_id() == 'msvc' - add_global_arguments('/source-charset:utf-8', language: ['c', 'cpp']) -endif - -message('host_machine.system(): ' + host_machine.system()) -message('compiler.get_id(): ' + compiler.get_id()) - -cc = meson.get_compiler('c') - # TODO: Shared includes may use namespaces, etc which need future tidyup. # For now, we use KMN_KBP to inject the km::kbp::kmx namespace -defns = ['-DKMN_KBP'] +defns += ['-DKMN_KBP'] # #define DEBUG when we are on a debug build if get_option('buildtype') == 'debug' diff --git a/core/src/debuglog.h b/core/src/debuglog.h index c68b88d1be1ac31450eadfeacd95b2358e50c206..16fba4642854a8359bf5530da2d50ff3c93dcd81 100644 --- a/core/src/debuglog.h +++ b/core/src/debuglog.h @@ -50,4 +50,4 @@ void write_console(KMX_BOOL error, const wchar_t *fmt, ...); } } -} \ No newline at end of file +} diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index d06bcc3731bd8b8c2558c7dd994dc69d47db9b8e..68dd5bacb0756b96248be9848b535b010611bd3e 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -670,7 +670,7 @@ COMP_KMXPLUS_KEYS_Helper::getKeys(KMX_DWORD i) const { const COMP_KMXPLUS_KEYS_KEY* COMP_KMXPLUS_KEYS_Helper::findKeyByStringId(KMX_DWORD strId, KMX_DWORD &i) const { - for (i = 0; i < key2->keyCount; i++) { + for (; i < key2->keyCount; i++) { if (keys[i].id == strId) { return &keys[i]; } @@ -678,6 +678,16 @@ COMP_KMXPLUS_KEYS_Helper::findKeyByStringId(KMX_DWORD strId, KMX_DWORD &i) const return nullptr; } +const COMP_KMXPLUS_KEYS_KEY* +COMP_KMXPLUS_KEYS_Helper::findKeyByStringTo(KMX_DWORD strId, KMX_DWORD &i) const { + for (; i < key2->keyCount; i++) { + if (keys[i].to == strId) { + return &keys[i]; + } + } + return nullptr; +} + const COMP_KMXPLUS_KEYS_FLICK_LIST * COMP_KMXPLUS_KEYS_Helper::getFlickLists(KMX_DWORD i) const { if (!valid() || i >= key2->flicksCount) { diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 74389ba5907a9568170bbc6e0c274f1784841de5..7944d7a712d2c8ed642a603c59307136f4fa414b 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -506,10 +506,17 @@ public: /** * Search for a key by string id. * @param strID id to search for - * @param index on exit, index of item if found. Undefined otherwise. + * @param index on entry, id to start with such as 0. On exit, index of item if found. Undefined otherwise. * @return pointer to key or nullptr */ const COMP_KMXPLUS_KEYS_KEY *findKeyByStringId(KMX_DWORD strId, KMX_DWORD &index) const; + /** + * Search for a key by 'to' string id + * @param strID id to search for + * @param index on entry, id to start with such as 0. On exit, index of item if found. Undefined otherwise. + * @return pointer to key or nullptr + */ + const COMP_KMXPLUS_KEYS_KEY *findKeyByStringTo(KMX_DWORD strId, KMX_DWORD &index) const; private: const COMP_KMXPLUS_KEYS *key2; diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index d5c75188b082eaf1c33fa1734e160b38358b821d..168d205cea3ff959ee5a8f36e3dbceb5182c1d79 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -41,15 +41,16 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - `common/web/types/src/kmx/kmx-plus.ts` - Also update class KMXPlusFile to include the actual binary format (coordinate with `kmx_plus.h`) - E/C `common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts` if there’s any changing to the boxing needed + - By the way, you might get errors such as “Error validating LDML XML file: /keyboard/transforms/0: additionalProperties: must NOT have additional properties additionalProperty="transform"” if the boxing functions don't match the schema. `boxArrays()` might be trying to add empty objects/arrays that aren't (anymore) legal per schema ## In-memory data: Phase 2 - first the compiler tests - - add a section in `developer/src/kmc-keyboard/test/fixtures/sections` if needed - - add a test case such as `developer/src/kmc-keyboard/test/test-key2.ts` + - add a section in `developer/src/kmc-ldml/test/fixtures/sections` if needed + - add a test case such as `developer/src/kmc-ldml/test/test-key2.ts` - add a compiler - - `developer/src/kmc-keyboard/compiler/key2.ts` - - link it in to `developer/src/kmc-keyboard/src/compiler/compiler.ts` + - `developer/src/kmc-ldml/compiler/key2.ts` + - link it in to `developer/src/kmc-ldml/src/compiler/compiler.ts` - add the import - add to `SECTION_COMPILERS` @@ -67,10 +68,10 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - Also note that restructure will happily ignore (write zeros for) any fields where the BUILDER_* fields don't match the COMP_PLUS_* fields. - update basic.xml and basic.txt - - Tweak `eveloper/src/kmc-keyboard/test/fixtures/basic.xml` as needed - - You can use `developer/src/kmc-keyboard/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml. + - Tweak `eveloper/src/kmc-ldml/test/fixtures/basic.xml` as needed + - You can use `developer/src/kmc-ldml/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs. + - `developer/src/kmc-ldml/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt. + - `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml. + - `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml. ## more to come diff --git a/core/src/meson.build b/core/src/meson.build index 4694a0b8d72c2650fb92f2cf3d0935b92c21aee0..97b929164647288845817f40f6824a8aa1b17caf 100644 --- a/core/src/meson.build +++ b/core/src/meson.build @@ -4,58 +4,21 @@ # Authors: Tim Eves (TSE) # -defns += ['-DKMN_KBP_EXPORTING'] version_res = [] -if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' - warns = [ - '-Wctor-dtor-privacy', - '-Wdouble-promotion', - '-Wendif-labels', - '-Wno-unknown-pragmas', - '-Wno-missing-field-initializers', - '-Wnon-virtual-dtor', - '-Wshadow' - ] - flags = [ - '-fvisibility=hidden', - '-fvisibility-inlines-hidden' - ] - links = [] - - if compiler.get_id() == 'clang' - warns += [ - '-Wimplicit-fallthrough', - '-Wno-double-promotion', - '-Wshorten-64-to-32' - ] - endif - - if host_machine.system() == 'darwin' - warns += ['-Wno-ctor-dtor-privacy', '-Wno-non-virtual-dtor'] - endif -endif - -if compiler.get_id() == 'msvc' - warns = [] - flags = [] - defns += [ - '-D_SCL_SECURE_NO_WARNINGS', - '-D_CRT_SECURE_NO_WARNINGS', - '-DUNICODE' - ] - links = [] +# TODO: shouldn't this be defined only for dylib and not static lib? +defns += ['-DKMN_KBP_EXPORTING'] +if cpp_compiler.get_id() == 'msvc' + defns += ['-DUNICODE'] # /n = append null # /c65001 = utf-8 file format version_res += import('windows').compile_resources('version.rc', args:['/n','/c65001']) endif -if compiler.get_id() == 'emscripten' - warns = [] - flags = [] - defns = ['-DKMN_KBP'] - links = [] +if cpp_compiler.get_id() == 'emscripten' + # TODO: why do we need this defn here? + defns += ['-DKMN_KBP'] endif kmx_files = files( diff --git a/core/subprojects/.gitignore b/core/subprojects/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..99479aa952cd339f9d98c7bab6dd21ad9700355e --- /dev/null +++ b/core/subprojects/.gitignore @@ -0,0 +1,4 @@ +/icu +/*.zip +/*.tgz +/packagecache diff --git a/core/subprojects/README.md b/core/subprojects/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4ff961beae35499e112566da1e855c2e9f8d7e62 --- /dev/null +++ b/core/subprojects/README.md @@ -0,0 +1,12 @@ +# core/subprojects + +## What's this? What _is_ this? + +- These are subprojects accessible to the `core` meson project. + +## icu + +- When building for Linux, [ICU](https://icu.unicode.org) must already be installed. The system ICU will be used instead of the subproject. +- Patches for icu (i.e. meson build files) are in [`packagefiles/icu`](./packagefiles/icu/) +- It would be preferable to use meson's [wrapdb](https://mesonbuild.com/Wrapdb-projects.html), and in fact the patchfiles here are sourced from that project. +- However, having our own patchfiles allows for slimming down ICU, at least until the wrapdb version is more configurable [mesonbuild/wrapdb#950](https://github.com/mesonbuild/wrapdb/issues/950) diff --git a/core/subprojects/icu-minimal.wrap b/core/subprojects/icu-minimal.wrap new file mode 100644 index 0000000000000000000000000000000000000000..fe509b48e03851337605ed10c56a8b5d137dd4bf --- /dev/null +++ b/core/subprojects/icu-minimal.wrap @@ -0,0 +1,14 @@ +[wrap-file] +directory = icu +source_url = https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.tgz +source_filename = icu4c-73_1-src.tgz +source_hash = a457431de164b4aa7eca00ed134d00dfbf88a77c6986a10ae7774fc076bb8c45 +## TODO-LDML: Note, need meson 0.55.0 to use patch_directory +patch_directory = icu + +# Based on: wrapdb_version = 72.1-3 + +[provide] +icu-uc = icuuc_dep +# TODO-LDML: not including i18n at present +#icu-i18n = icui18n_dep diff --git a/core/subprojects/packagefiles/icu/LICENSE.meson b/core/subprojects/packagefiles/icu/LICENSE.meson new file mode 100644 index 0000000000000000000000000000000000000000..b59833dedbe2e4a84304431f4acca5dc4599cec7 --- /dev/null +++ b/core/subprojects/packagefiles/icu/LICENSE.meson @@ -0,0 +1,19 @@ +Copyright (c) 2021 The Meson development team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/core/subprojects/packagefiles/icu/meson.build b/core/subprojects/packagefiles/icu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..32bc209139eaa12d2dceb70bd805754dc696a9c4 --- /dev/null +++ b/core/subprojects/packagefiles/icu/meson.build @@ -0,0 +1,98 @@ +project( + 'icu', + 'c', + 'cpp', + version: '73.1', + meson_version: '>=0.57.0', # TODO-LDML: Too high for debian + default_options: [ 'cpp_std=c++17' ], +) + +U_ICU_VERSION = meson.project_version() +PACKAGE_ICU_DESCRIPTION = 'International Components for Unicode' +PACKAGE_ICU_URL = 'http://icu-project.org' + +cpp = meson.get_compiler('cpp') +# cpp_native = meson.get_compiler('cpp', native: true) + +have_elf_h = cpp.has_header('elf.h') + +if host_machine.system() == 'windows' + add_project_arguments('-DU_PLATFORM_USES_ONLY_WIN32_API', language: 'cpp') + add_project_arguments('-DWIN32', '-DWIN64', '-D_MBCS', language: 'cpp') + add_project_arguments('-DU_PLATFORM_USES_ONLY_WIN32_API', language: 'c') + add_project_arguments('-DWIN32', '-DWIN64', '-D_MBCS', language: 'c') +endif + + +# per icudefs.mk.in: +# "U_ATTRIBUTE_DEPRECATED is defined to hide warnings about deprecated API warnings." +add_project_arguments('-DU_ATTRIBUTE_DEPRECATED=', '-DUCONFIG_USE_LOCAL=1', language: 'c') + +add_project_arguments('-DU_NOEXCEPT=', language: 'c') + +uconfig = configuration_data() + +uconfig.set('U_ENABLE_DYLOAD', 0) # no DLL +uconfig.set('U_CHECK_DYLOAD', 0) # no DLL +uconfig.set('UCONFIG_NO_FILE_IO', 1) +uconfig.set('UCONFIG_NO_LEGACY_CONVERSION', 1) # turn off file based codepage conversion +uconfig.set('UCONFIG_NO_NORMALIZATION', 1) # TODO-LDML: may want this +uconfig.set('UCONFIG_NO_BREAK_ITERATION', 1) # TODO-LDML: may want this +uconfig.set('UCONFIG_NO_IDNA', 1) +uconfig.set('UCONFIG_NO_COLLATION', 1) +uconfig.set('UCONFIG_NO_FORMATTING', 1) +uconfig.set('UCONFIG_NO_TRANSLITERATION', 1) +uconfig.set('UCONFIG_NO_REGULAR_EXPRESSIONS', 1) # TODO-LDML: Will probably want this for transforms #7375 +uconfig.set('UCONFIG_NO_SERVICE', 1) + + +uconfig_local = configure_file( + configuration : uconfig, + # dir : 'source/common/unicode', + output : 'uconfig_local.h', +) + + +if cpp.get_argument_syntax() == 'msvc' +# TODO-LDML: cl : Command line error D8016 : '/utf-8' and '/source-charset:utf-8' command-line options are incompatible +# see note in core/meson.build + # add_project_arguments('/utf-8', language: 'cpp') + # add_project_arguments('/utf-8', language: 'c') +endif + +# TODO-LDML: skip this check +# if meson.version().version_compare('>= 0.62') +# dl_dep = dependency('dl', required: false) +# dl_native_dep = dependency('dl', required: false, native: true) +# else +# dl_dep = cpp.find_library('dl', required: false) +# dl_native_dep = cpp_native.find_library('dl', required: false, native: true) +# endif + +windows = import('windows') +pkg = import('pkgconfig') + +# Compiler flags the users of this library must use. +usage_args = [] + +if get_option('default_library') == 'static' + add_project_arguments('-DU_STATIC_IMPLEMENTATION', language: 'c') + add_project_arguments('-DU_STATIC_IMPLEMENTATION', language: 'cpp') + usage_args = ['-DU_STATIC_IMPLEMENTATION'] + if cpp.get_argument_syntax() == 'msvc' + library_prefix = '' + library_suffix = '' + else + library_prefix = 's' + if get_option('buildtype') == 'debug' + library_suffix = 'd' + else + library_suffix = '' + endif + endif +else + library_prefix = '' + library_suffix = '' +endif + +subdir('source') diff --git a/core/subprojects/packagefiles/icu/source/common/meson.build b/core/subprojects/packagefiles/icu/source/common/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..4653d88383c163221bc5417dbd973673c41ccaec --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/common/meson.build @@ -0,0 +1,358 @@ +sources = files( + 'appendable.cpp', + 'bmpset.cpp', + 'brkeng.cpp', + 'brkiter.cpp', + 'bytesinkutil.cpp', + 'bytestream.cpp', + 'bytestrie.cpp', + 'bytestriebuilder.cpp', + 'bytestrieiterator.cpp', + 'caniter.cpp', + 'characterproperties.cpp', + 'chariter.cpp', + 'charstr.cpp', + 'cmemory.cpp', + 'cstr.cpp', + 'cstring.cpp', + 'cwchar.cpp', + 'dictbe.cpp', + 'dictionarydata.cpp', + 'dtintrv.cpp', + 'edits.cpp', + 'emojiprops.cpp', + 'errorcode.cpp', + 'filteredbrk.cpp', + 'filterednormalizer2.cpp', + 'icudataver.cpp', + 'icuplug.cpp', + 'loadednormalizer2impl.cpp', + 'localebuilder.cpp', + 'locavailable.cpp', + 'locbased.cpp', + 'locdispnames.cpp', + 'locdspnm.cpp', + 'locid.cpp', + 'loclikely.cpp', + 'locmap.cpp', + 'locresdata.cpp', + 'locutil.cpp', + 'lstmbe.cpp', + 'messagepattern.cpp', + 'normalizer2.cpp', + 'normalizer2impl.cpp', + 'normlzr.cpp', + 'parsepos.cpp', + 'patternprops.cpp', + 'propname.cpp', + 'propsvec.cpp', + 'punycode.cpp', + 'putil.cpp', + 'rbbi.cpp', + 'rbbi_cache.cpp', + 'rbbidata.cpp', + 'rbbinode.cpp', + 'rbbirb.cpp', + 'rbbiscan.cpp', + 'rbbisetb.cpp', + 'rbbistbl.cpp', + 'rbbitblb.cpp', + 'resbund.cpp', + 'resbund_cnv.cpp', + 'resource.cpp', + 'ruleiter.cpp', + 'schriter.cpp', + 'serv.cpp', + 'servlk.cpp', + 'servlkf.cpp', + 'servls.cpp', + 'servnotf.cpp', + 'servrbf.cpp', + 'servslkf.cpp', + 'sharedobject.cpp', + 'simpleformatter.cpp', + 'static_unicode_sets.cpp', + 'stringpiece.cpp', + 'stringtriebuilder.cpp', + 'uarrsort.cpp', + 'ubidi.cpp', + 'ubidi_props.cpp', + 'ubidiln.cpp', + 'ubidiwrt.cpp', + 'ubrk.cpp', + 'ucase.cpp', + 'ucasemap.cpp', + 'ucasemap_titlecase_brkiter.cpp', + 'ucat.cpp', + 'uchar.cpp', + 'ucharstrie.cpp', + 'ucharstriebuilder.cpp', + 'ucharstrieiterator.cpp', + 'uchriter.cpp', + 'ucln_cmn.cpp', + 'ucmndata.cpp', + 'ucnv.cpp', + 'ucnv2022.cpp', + 'ucnv_bld.cpp', + 'ucnv_cb.cpp', + 'ucnv_cnv.cpp', + 'ucnv_ct.cpp', + 'ucnv_err.cpp', + 'ucnv_ext.cpp', + 'ucnv_io.cpp', + 'ucnv_lmb.cpp', + 'ucnv_set.cpp', + 'ucnv_u16.cpp', + 'ucnv_u32.cpp', + 'ucnv_u7.cpp', + 'ucnv_u8.cpp', + 'ucnvbocu.cpp', + 'ucnvdisp.cpp', + 'ucnvhz.cpp', + 'ucnvisci.cpp', + 'ucnvlat1.cpp', + 'ucnvmbcs.cpp', + 'ucnvscsu.cpp', + 'ucnvsel.cpp', + 'ucol_swp.cpp', + 'ucptrie.cpp', + 'ucurr.cpp', + 'udata.cpp', + 'udatamem.cpp', + 'udataswp.cpp', + 'uenum.cpp', + 'uhash.cpp', + 'uhash_us.cpp', + 'uidna.cpp', + 'uinit.cpp', + 'uinvchar.cpp', + 'uiter.cpp', + 'ulist.cpp', + 'uloc.cpp', + 'uloc_keytype.cpp', + 'uloc_tag.cpp', + 'umapfile.cpp', + 'umath.cpp', + 'umutablecptrie.cpp', + 'umutex.cpp', + 'unames.cpp', + 'unifiedcache.cpp', + 'unifilt.cpp', + 'unifunct.cpp', + 'uniset.cpp', + 'uniset_closure.cpp', + 'uniset_props.cpp', + 'unisetspan.cpp', + 'unistr.cpp', + 'unistr_case.cpp', + 'unistr_case_locale.cpp', + 'unistr_cnv.cpp', + 'unistr_props.cpp', + 'unistr_titlecase_brkiter.cpp', + 'unorm.cpp', + 'unormcmp.cpp', + 'uobject.cpp', + 'uprops.cpp', + 'ures_cnv.cpp', + 'uresbund.cpp', + 'uresdata.cpp', + 'usc_impl.cpp', + 'uscript.cpp', + 'uscript_props.cpp', + 'uset.cpp', + 'uset_props.cpp', + 'usetiter.cpp', + 'ushape.cpp', + 'usprep.cpp', + 'ustack.cpp', + 'ustr_cnv.cpp', + 'ustr_titlecase_brkiter.cpp', + 'ustr_wcs.cpp', + 'ustrcase.cpp', + 'ustrcase_locale.cpp', + 'ustrenum.cpp', + 'ustrfmt.cpp', + 'ustring.cpp', + 'ustrtrns.cpp', + 'utext.cpp', + 'utf_impl.cpp', + 'util.cpp', + 'util_props.cpp', + 'utrace.cpp', + 'utrie.cpp', + 'utrie2.cpp', + 'utrie2_builder.cpp', + 'utrie_swap.cpp', + 'uts46.cpp', + 'utypes.cpp', + 'uvector.cpp', + 'uvectr32.cpp', + 'uvectr64.cpp', + 'wintz.cpp', +) + +if host_machine.system() == 'windows' + sources += windows.compile_resources('common.rc', include_directories: incdir) +endif + +icuuc_name = '@0@icuuc@1@'.format(library_prefix, library_suffix) + +common_lib = library( + icuuc_name, + sources, + include_directories: [incdir,'../..'], + c_args: '-DU_COMMON_IMPLEMENTATION', + cpp_args: '-DU_COMMON_IMPLEMENTATION', + link_with: stubdata_lib, + # dependencies: dl_dep, + version: U_ICU_VERSION, + install: true, +) + +# if meson.can_run_host_binaries() + common_native_lib = common_lib +# else +# common_native_lib = library( +# '@0@-native'.format(icuuc_name), +# sources, +# include_directories: incdir, +# c_args: '-DU_COMMON_IMPLEMENTATION', +# cpp_args: '-DU_COMMON_IMPLEMENTATION', +# link_with: stubdata_native_lib, +# # dependencies: dl_native_dep, +# version: U_ICU_VERSION, +# native: true, +# ) +# endif + +icuuc_dep = declare_dependency( + link_with: common_lib, + compile_args: usage_args, + include_directories: incdir, + # dependencies: dl_dep, +) + +# icuuc_native_dep = declare_dependency( +# link_with: common_native_lib, +# compile_args: usage_args, +# include_directories: incdir, +# dependencies: dl_native_dep, +# ) + +if meson.version().version_compare('>=0.54.0') + meson.override_dependency('icu-uc', icuuc_dep) +endif + +headers = files( + 'unicode/appendable.h', + 'unicode/brkiter.h', + 'unicode/bytestream.h', + 'unicode/bytestrie.h', + 'unicode/bytestriebuilder.h', + 'unicode/caniter.h', + 'unicode/casemap.h', + 'unicode/char16ptr.h', + 'unicode/chariter.h', + 'unicode/dbbi.h', + 'unicode/docmain.h', + 'unicode/dtintrv.h', + 'unicode/edits.h', + 'unicode/enumset.h', + 'unicode/errorcode.h', + 'unicode/filteredbrk.h', + 'unicode/icudataver.h', + 'unicode/icuplug.h', + 'unicode/idna.h', + 'unicode/localebuilder.h', + 'unicode/localematcher.h', + 'unicode/localpointer.h', + 'unicode/locdspnm.h', + 'unicode/locid.h', + 'unicode/messagepattern.h', + 'unicode/normalizer2.h', + 'unicode/normlzr.h', + 'unicode/parseerr.h', + 'unicode/parsepos.h', + 'unicode/platform.h', + 'unicode/ptypes.h', + 'unicode/putil.h', + 'unicode/rbbi.h', + 'unicode/rep.h', + 'unicode/resbund.h', + 'unicode/schriter.h', + 'unicode/simpleformatter.h', + 'unicode/std_string.h', + 'unicode/strenum.h', + 'unicode/stringoptions.h', + 'unicode/stringpiece.h', + 'unicode/stringtriebuilder.h', + 'unicode/symtable.h', + 'unicode/ubidi.h', + 'unicode/ubiditransform.h', + 'unicode/ubrk.h', + 'unicode/ucasemap.h', + 'unicode/ucat.h', + 'unicode/uchar.h', + 'unicode/ucharstrie.h', + 'unicode/ucharstriebuilder.h', + 'unicode/uchriter.h', + 'unicode/uclean.h', + 'unicode/ucnv.h', + 'unicode/ucnv_cb.h', + 'unicode/ucnv_err.h', + 'unicode/ucnvsel.h', + 'unicode/uconfig.h', + 'unicode/ucpmap.h', + 'unicode/ucptrie.h', + 'unicode/ucurr.h', + 'unicode/udata.h', + 'unicode/udisplaycontext.h', + 'unicode/uenum.h', + 'unicode/uidna.h', + 'unicode/uiter.h', + 'unicode/uldnames.h', + 'unicode/uloc.h', + 'unicode/umachine.h', + 'unicode/umisc.h', + 'unicode/umutablecptrie.h', + 'unicode/unifilt.h', + 'unicode/unifunct.h', + 'unicode/unimatch.h', + 'unicode/uniset.h', + 'unicode/unistr.h', + 'unicode/unorm.h', + 'unicode/unorm2.h', + 'unicode/uobject.h', + 'unicode/urename.h', + 'unicode/urep.h', + 'unicode/ures.h', + 'unicode/uscript.h', + 'unicode/uset.h', + 'unicode/usetiter.h', + 'unicode/ushape.h', + 'unicode/usprep.h', + 'unicode/ustring.h', + 'unicode/ustringtrie.h', + 'unicode/utext.h', + 'unicode/utf.h', + 'unicode/utf16.h', + 'unicode/utf32.h', + 'unicode/utf8.h', + 'unicode/utf_old.h', + 'unicode/utrace.h', + 'unicode/utypes.h', + 'unicode/uvernum.h', + 'unicode/uversion.h', +) + +install_headers( + headers, + subdir: 'unicode', +) + +pkg.generate( + common_lib, + name: 'icu-uc', + description: '@0@: Common and Data libraries'.format(PACKAGE_ICU_DESCRIPTION), + url: PACKAGE_ICU_URL, +) diff --git a/core/subprojects/packagefiles/icu/source/data/export_module.py b/core/subprojects/packagefiles/icu/source/data/export_module.py new file mode 100644 index 0000000000000000000000000000000000000000..d29b1c898da773b663e7140f783f1e48a7c55c7b --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/data/export_module.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2022 L. E. Segovia +# SPDX-License-Identifier: MIT + +from argparse import ArgumentParser, FileType +from sys import stdout + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('symbol', type=str, help='Symbol to export') + parser.add_argument('outfile', nargs='?', type=FileType('w', encoding='utf-8'), + default=stdout, help='Module definition file') + args = parser.parse_args() + + with args.outfile as f: + f.write("EXPORTS\n") + f.write(f"\t{args.symbol}\n") diff --git a/core/subprojects/packagefiles/icu/source/data/meson.build b/core/subprojects/packagefiles/icu/source/data/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..80fd51ba6c0e110f9862328904231f581d9bdca9 --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/data/meson.build @@ -0,0 +1,84 @@ +U_ICUDATA_NAME = 'icudt@0@'.format(U_ICU_VERSION.split('.')[0]) + +icudata_command = [ + genccode_native_exe, + '-d', + '@OUTDIR@', + '-e', + U_ICUDATA_NAME, + '-f', + U_ICUDATA_NAME, + '@CURRENT_SOURCE_DIR@/in/@0@l.dat'.format(U_ICUDATA_NAME), +] + +if host_machine.system() == 'windows' and get_option('default_library') == 'static' + icudata_command += ['--skip-dll-export'] +endif + +if cpp.get_argument_syntax() == 'msvc' + ## Uncomment if you want to use MASM (>= 0.64), but be aware + ## it is Very Slow to build. + # if add_languages('masm', required: false) + # icudata_asm_output = '@0@.masm'.format(U_ICUDATA_NAME) + # icudata_command += ['-a', 'masm'] + # else + icudata_asm_output = '@0@.c'.format(U_ICUDATA_NAME) + # endif +elif host_machine.system() == 'cygwin' + icudata_asm_output = '@0@.S'.format(U_ICUDATA_NAME) + icudata_command += ['-a', 'gcc-cygwin'] +elif host_machine.system() == 'darwin' + icudata_asm_output = '@0@.S'.format(U_ICUDATA_NAME) + icudata_command += ['-a', 'gcc-darwin'] +elif cpp.get_id() == 'gcc' or cpp.get_id() == 'clang' + icudata_asm_output = '@0@.S'.format(U_ICUDATA_NAME) + if host_machine.system() == 'windows' + icudata_command += ['-a', 'gcc-mingw64'] + else + icudata_command += ['-a', 'gcc'] + endif +else + icudata_asm_output = '@0@.c'.format(U_ICUDATA_NAME) +endif + +icudata_asm = custom_target( + 'icudata_asm', + command: icudata_command, + output: icudata_asm_output, +) + +sources = [icudata_asm] + +python_exe = import('python').find_installation('python3') + +icudata_exports = custom_target( + 'icudata_exports', + command: [python_exe, '@INPUT@', '@0@_dat'.format(U_ICUDATA_NAME), '@OUTDIR@/module.def'], + input: 'export_module.py', + output: 'module.def', +) + +if host_machine.system() == 'windows' + sources += windows.compile_resources('misc/icudata.rc', include_directories: incdir) +endif + +if cpp.get_id() != 'msvc' + icudata = library( + icudata_name, + sources, + include_directories: incdir, + version: U_ICU_VERSION, + vs_module_defs: icudata_exports, + install: true, + ) + + icudata_dep = declare_dependency( + link_with: icudata, + include_directories: incdir, + compile_args: usage_args, + ) + + if meson.version().version_compare('>=0.54.0') + meson.override_dependency('icu-data', icudata_dep) + endif +endif diff --git a/core/subprojects/packagefiles/icu/source/i18n/meson.build b/core/subprojects/packagefiles/icu/source/i18n/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..787afe339049813d5d9a93b2dd69888b42e475da --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/i18n/meson.build @@ -0,0 +1,408 @@ +sources = files( + 'alphaindex.cpp', + 'anytrans.cpp', + 'astro.cpp', + 'basictz.cpp', + 'bocsu.cpp', + 'brktrans.cpp', + 'buddhcal.cpp', + 'calendar.cpp', + 'casetrn.cpp', + 'cecal.cpp', + 'chnsecal.cpp', + 'choicfmt.cpp', + 'coleitr.cpp', + 'coll.cpp', + 'collation.cpp', + 'collationbuilder.cpp', + 'collationcompare.cpp', + 'collationdata.cpp', + 'collationdatabuilder.cpp', + 'collationdatareader.cpp', + 'collationdatawriter.cpp', + 'collationfastlatin.cpp', + 'collationfastlatinbuilder.cpp', + 'collationfcd.cpp', + 'collationiterator.cpp', + 'collationkeys.cpp', + 'collationroot.cpp', + 'collationrootelements.cpp', + 'collationruleparser.cpp', + 'collationsets.cpp', + 'collationsettings.cpp', + 'collationtailoring.cpp', + 'collationweights.cpp', + 'compactdecimalformat.cpp', + 'coptccal.cpp', + 'cpdtrans.cpp', + 'csdetect.cpp', + 'csmatch.cpp', + 'csr2022.cpp', + 'csrecog.cpp', + 'csrmbcs.cpp', + 'csrsbcs.cpp', + 'csrucode.cpp', + 'csrutf8.cpp', + 'curramt.cpp', + 'currfmt.cpp', + 'currpinf.cpp', + 'currunit.cpp', + 'dangical.cpp', + 'datefmt.cpp', + 'dayperiodrules.cpp', + 'dcfmtsym.cpp', + 'decContext.cpp', + 'decNumber.cpp', + 'decimfmt.cpp', + 'displayoptions.cpp', + 'double-conversion-bignum-dtoa.cpp', + 'double-conversion-bignum.cpp', + 'double-conversion-cached-powers.cpp', + 'double-conversion-double-to-string.cpp', + 'double-conversion-fast-dtoa.cpp', + 'double-conversion-string-to-double.cpp', + 'double-conversion-strtod.cpp', + 'dtfmtsym.cpp', + 'dtitvfmt.cpp', + 'dtitvinf.cpp', + 'dtptngen.cpp', + 'dtrule.cpp', + 'erarules.cpp', + 'esctrn.cpp', + 'ethpccal.cpp', + 'fmtable.cpp', + 'fmtable_cnv.cpp', + 'format.cpp', + 'formatted_string_builder.cpp', + 'formattedval_iterimpl.cpp', + 'formattedval_sbimpl.cpp', + 'formattedvalue.cpp', + 'fphdlimp.cpp', + 'fpositer.cpp', + 'funcrepl.cpp', + 'gender.cpp', + 'gregocal.cpp', + 'gregoimp.cpp', + 'hebrwcal.cpp', + 'indiancal.cpp', + 'inputext.cpp', + 'islamcal.cpp', + 'japancal.cpp', + 'listformatter.cpp', + 'measfmt.cpp', + 'measunit.cpp', + 'measunit_extra.cpp', + 'measure.cpp', + 'msgfmt.cpp', + 'name2uni.cpp', + 'nfrs.cpp', + 'nfrule.cpp', + 'nfsubs.cpp', + 'nortrans.cpp', + 'nultrans.cpp', + 'number_affixutils.cpp', + 'number_asformat.cpp', + 'number_capi.cpp', + 'number_compact.cpp', + 'number_currencysymbols.cpp', + 'number_decimalquantity.cpp', + 'number_decimfmtprops.cpp', + 'number_fluent.cpp', + 'number_formatimpl.cpp', + 'number_formatimpl.cpp', + 'number_grouping.cpp', + 'number_integerwidth.cpp', + 'number_longnames.cpp', + 'number_mapper.cpp', + 'number_modifiers.cpp', + 'number_multiplier.cpp', + 'number_notation.cpp', + 'number_output.cpp', + 'number_padding.cpp', + 'number_patternmodifier.cpp', + 'number_patternstring.cpp', + 'number_rounding.cpp', + 'number_scientific.cpp', + 'number_skeletons.cpp', + 'number_symbolswrapper.cpp', + 'number_usageprefs.cpp', + 'number_utils.cpp', + 'numfmt.cpp', + 'numparse_affixes.cpp', + 'numparse_compositions.cpp', + 'numparse_currency.cpp', + 'numparse_decimal.cpp', + 'numparse_impl.cpp', + 'numparse_parsednumber.cpp', + 'numparse_scientific.cpp', + 'numparse_symbols.cpp', + 'numparse_validators.cpp', + 'numrange_capi.cpp', + 'numrange_fluent.cpp', + 'numrange_impl.cpp', + 'numsys.cpp', + 'olsontz.cpp', + 'persncal.cpp', + 'pluralranges.cpp', + 'plurfmt.cpp', + 'plurrule.cpp', + 'quant.cpp', + 'quantityformatter.cpp', + 'rbnf.cpp', + 'rbt.cpp', + 'rbt_data.cpp', + 'rbt_pars.cpp', + 'rbt_rule.cpp', + 'rbt_set.cpp', + 'rbtz.cpp', + 'regexcmp.cpp', + 'regeximp.cpp', + 'regexst.cpp', + 'regextxt.cpp', + 'region.cpp', + 'reldatefmt.cpp', + 'reldtfmt.cpp', + 'rematch.cpp', + 'remtrans.cpp', + 'repattrn.cpp', + 'rulebasedcollator.cpp', + 'scientificnumberformatter.cpp', + 'scriptset.cpp', + 'search.cpp', + 'selfmt.cpp', + 'sharedbreakiterator.cpp', + 'simpletz.cpp', + 'smpdtfmt.cpp', + 'smpdtfst.cpp', + 'sortkey.cpp', + 'standardplural.cpp', + 'string_segment.cpp', + 'strmatch.cpp', + 'strrepl.cpp', + 'stsearch.cpp', + 'taiwncal.cpp', + 'timezone.cpp', + 'titletrn.cpp', + 'tmunit.cpp', + 'tmutamt.cpp', + 'tmutfmt.cpp', + 'tolowtrn.cpp', + 'toupptrn.cpp', + 'translit.cpp', + 'transreg.cpp', + 'tridpars.cpp', + 'tzfmt.cpp', + 'tzgnames.cpp', + 'tznames.cpp', + 'tznames_impl.cpp', + 'tzrule.cpp', + 'tztrans.cpp', + 'ucal.cpp', + 'ucln_in.cpp', + 'ucol.cpp', + 'ucol_res.cpp', + 'ucol_sit.cpp', + 'ucoleitr.cpp', + 'ucsdet.cpp', + 'udat.cpp', + 'udateintervalformat.cpp', + 'udatpg.cpp', + 'ufieldpositer.cpp', + 'uitercollationiterator.cpp', + 'ulistformatter.cpp', + 'ulocdata.cpp', + 'umsg.cpp', + 'unesctrn.cpp', + 'uni2name.cpp', + 'units_complexconverter.cpp', + 'units_converter.cpp', + 'units_data.cpp', + 'units_router.cpp', + 'unum.cpp', + 'unumsys.cpp', + 'upluralrules.cpp', + 'uregex.cpp', + 'uregexc.cpp', + 'uregion.cpp', + 'usearch.cpp', + 'uspoof.cpp', + 'uspoof_build.cpp', + 'uspoof_conf.cpp', + 'uspoof_impl.cpp', + 'utf16collationiterator.cpp', + 'utf8collationiterator.cpp', + 'utmscale.cpp', + 'utrans.cpp', + 'vtzone.cpp', + 'vzone.cpp', + 'windtfmt.cpp', + 'winnmfmt.cpp', + 'wintzimpl.cpp', + 'zonemeta.cpp', + 'zrule.cpp', + 'ztrans.cpp', + # 'decimalformatpattern.cpp', + # 'ucurr.cpp', + # 'digitlst.cpp', + # 'uspoof_wsconf.cpp', + # 'decfmtst.cpp', + # 'locdspnm.cpp', + # 'identifier_info.cpp', + # 'filteredbrk.cpp', +) + +if host_machine.system() == 'windows' + sources += windows.compile_resources('i18n.rc', include_directories: incdir) +endif + +if host_machine.system() == 'windows' + i18n_name = '@0@icuin@1@'.format(library_prefix, library_suffix) +else + i18n_name = '@0@icui18n@1@'.format(library_prefix, library_suffix) +endif + +i18n_lib = library( + i18n_name, + sources, + dependencies: icuuc_dep, + c_args: '-DU_I18N_IMPLEMENTATION', + cpp_args: '-DU_I18N_IMPLEMENTATION', + version: U_ICU_VERSION, + install: true, +) + +# if meson.can_run_host_binaries() + i18n_native_lib = i18n_lib +# else +# i18n_native_lib = library( +# '@0@-native'.format(i18n_name), +# sources, +# dependencies: icuuc_native_dep, +# c_args: '-DU_I18N_IMPLEMENTATION', +# cpp_args: '-DU_I18N_IMPLEMENTATION', +# version: U_ICU_VERSION, +# native: true, +# ) +# endif + +i18ninc = include_directories('.') + +icui18n_dep = declare_dependency( + include_directories: i18ninc, + link_with: i18n_lib, + dependencies: icuuc_dep, +) + +# icui18n_native_dep = declare_dependency( +# include_directories: i18ninc, +# link_with: i18n_native_lib, +# dependencies: icuuc_native_dep, +# ) + +if meson.version().version_compare('>=0.54.0') + meson.override_dependency('icu-i18n', icui18n_dep) +endif + +headers = files( + 'unicode/alphaindex.h', + 'unicode/basictz.h', + 'unicode/calendar.h', + 'unicode/choicfmt.h', + 'unicode/coleitr.h', + 'unicode/coll.h', + 'unicode/compactdecimalformat.h', + 'unicode/curramt.h', + 'unicode/currpinf.h', + 'unicode/currunit.h', + 'unicode/datefmt.h', + 'unicode/dcfmtsym.h', + 'unicode/decimfmt.h', + 'unicode/displayoptions.h', + 'unicode/dtfmtsym.h', + 'unicode/dtitvfmt.h', + 'unicode/dtitvinf.h', + 'unicode/dtptngen.h', + 'unicode/dtrule.h', + 'unicode/fieldpos.h', + 'unicode/fmtable.h', + 'unicode/format.h', + 'unicode/formattedvalue.h', + 'unicode/fpositer.h', + 'unicode/gender.h', + 'unicode/gregocal.h', + 'unicode/listformatter.h', + 'unicode/measfmt.h', + 'unicode/measunit.h', + 'unicode/measure.h', + 'unicode/msgfmt.h', + 'unicode/nounit.h', + 'unicode/numberformatter.h', + 'unicode/numberrangeformatter.h', + 'unicode/numfmt.h', + 'unicode/numsys.h', + 'unicode/plurfmt.h', + 'unicode/plurrule.h', + 'unicode/rbnf.h', + 'unicode/rbtz.h', + 'unicode/regex.h', + 'unicode/region.h', + 'unicode/reldatefmt.h', + 'unicode/scientificnumberformatter.h', + 'unicode/search.h', + 'unicode/selfmt.h', + 'unicode/simpletz.h', + 'unicode/smpdtfmt.h', + 'unicode/sortkey.h', + 'unicode/stsearch.h', + 'unicode/tblcoll.h', + 'unicode/timezone.h', + 'unicode/tmunit.h', + 'unicode/tmutamt.h', + 'unicode/tmutfmt.h', + 'unicode/translit.h', + 'unicode/tzfmt.h', + 'unicode/tznames.h', + 'unicode/tzrule.h', + 'unicode/tztrans.h', + 'unicode/ucal.h', + 'unicode/ucol.h', + 'unicode/ucoleitr.h', + 'unicode/ucsdet.h', + 'unicode/udat.h', + 'unicode/udateintervalformat.h', + 'unicode/udatpg.h', + 'unicode/udisplayoptions.h', + 'unicode/ufieldpositer.h', + 'unicode/uformattable.h', + 'unicode/uformattedvalue.h', + 'unicode/ugender.h', + 'unicode/ulistformatter.h', + 'unicode/ulocdata.h', + 'unicode/umsg.h', + 'unicode/unirepl.h', + 'unicode/unum.h', + 'unicode/unumberformatter.h', + 'unicode/unumberrangeformatter.h', + 'unicode/unumsys.h', + 'unicode/upluralrules.h', + 'unicode/uregex.h', + 'unicode/uregion.h', + 'unicode/ureldatefmt.h', + 'unicode/usearch.h', + 'unicode/uspoof.h', + 'unicode/utmscale.h', + 'unicode/utrans.h', + 'unicode/vtzone.h', +) + +install_headers( + headers, + subdir: 'unicode', +) + +pkg.generate( + i18n_lib, + name: 'icu-i18n', + description: '@0@: Internationalization library'.format(PACKAGE_ICU_DESCRIPTION), + url: PACKAGE_ICU_URL, +) diff --git a/core/subprojects/packagefiles/icu/source/meson.build b/core/subprojects/packagefiles/icu/source/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..7d08a1e3cb1ba362501e3e4c4221e9470a2d6163 --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/meson.build @@ -0,0 +1,22 @@ +incdir = include_directories('..', 'common') + +if host_machine.system() == 'windows' + icudata_name = '@0@icudt@1@'.format(library_prefix, library_suffix) +else + icudata_name = '@0@icudata@1@'.format(library_prefix, library_suffix) +endif + +subdir('stubdata') +subdir('common') + +# TODO-LDML: Not used now, will need for regex +# subdir('i18n') + +## Note: The following subdirs are not used by Keyman (at present) + +# subdir('data') +# subdir('io') +# subdir('layout') +# subdir('layoutex') +# subdir('test') +# subdir('tools') diff --git a/core/subprojects/packagefiles/icu/source/stubdata/meson.build b/core/subprojects/packagefiles/icu/source/stubdata/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f5ee86712aa4a543b99de6c4ac7a2ff469b85ce3 --- /dev/null +++ b/core/subprojects/packagefiles/icu/source/stubdata/meson.build @@ -0,0 +1,26 @@ +sources = files( + 'stubdata.cpp', +) + +stubdata_lib = library( + icudata_name, + sources, + c_args: '-DSTUBDATA_BUILD', + cpp_args: '-DSTUBDATA_BUILD', + include_directories: incdir, + version: U_ICU_VERSION, +) + +# if meson.can_run_host_binaries() + stubdata_native_lib = stubdata_lib +# else +# stubdata_native_lib = library( +# '@0@-native'.format(icudata_name), +# sources, +# c_args: '-DSTUBDATA_BUILD', +# cpp_args: '-DSTUBDATA_BUILD', +# include_directories: incdir, +# version: U_ICU_VERSION, +# native: true, +# ) +# endif diff --git a/core/tests/kmx_test_source/meson.build b/core/tests/kmx_test_source/meson.build index 4cfdd5661d1dd1d9ffbf7fe1a7139d4cc917cd3d..691839047f82f7995ec23b79682eb698ff9e0e92 100644 --- a/core/tests/kmx_test_source/meson.build +++ b/core/tests/kmx_test_source/meson.build @@ -1,4 +1,6 @@ -if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' or compiler.get_id() == 'emscripten' + +# TODO -- why are these differing from the standard.meson.build flags? +if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' or cpp_compiler.get_id() == 'emscripten' warns = [ '-Wno-missing-field-initializers', '-Wno-unused-parameter' diff --git a/core/tests/meson.build b/core/tests/meson.build index 1d37806947fd80f3e3b9b0fbff5a4cdacb2534a6..e2e29975822dcf9918f41d98ca64ca629289f625 100644 --- a/core/tests/meson.build +++ b/core/tests/meson.build @@ -22,7 +22,7 @@ if get_option('keyman_core_tests') if get_option('default_library') != 'static' ctypes_void_p_size = ['-c', 'import ctypes; print(ctypes.sizeof(ctypes.c_void_p))'] r = run_command(python, ctypes_void_p_size) - python_ctypes_compatible = r.stdout().to_int() == compiler.sizeof('void *') + python_ctypes_compatible = r.stdout().to_int() == cpp_compiler.sizeof('void *') if not python_ctypes_compatible message('Python ctypes is incompatible with built shared object. Disabling some tests.') endif diff --git a/core/tests/unit/emscripten_filesystem.cpp b/core/tests/unit/emscripten_filesystem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5e180f6beabf67d14f0d526f76b83718a4d564a --- /dev/null +++ b/core/tests/unit/emscripten_filesystem.cpp @@ -0,0 +1,72 @@ + + +#ifdef __EMSCRIPTEN__ + +#include +#include +#include + +const std::string get_wasm_file_path(const std::string& filename) { + // Verify that we are passing a fully-qualified path + // TODO: we need to support relative paths based on CWD +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path ENTER (" << filename << ")" << std::endl; +#endif + + assert( + (filename.length() > 0 && filename.at(0) == '/') || + (filename.length() > 1 && filename.at(1) == ':') + ); + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path assert passed " << std::endl; +#endif + + EM_ASM_({ + //console.log('EM_ASM enter'); + let path = Module.UTF8ToString($0); + const isWin32Path = path.match(/^[a-zA-Z]:/); + + //console.log('EM_ASM path = '+path); + let root = '/nodefs-mount'; + if(!FS.analyzePath(root).exists) { + //console.log('EM_ASM mkdir '+root); + FS.mkdir(root); + if(!isWin32Path) { + //console.log('EM_ASM mount '+root); + FS.mount(NODEFS, {root : '/'}, root); + } + } + + if(isWin32Path) { + // Win32 path, one mount per drive + root += "/" + path.charAt(0); + if(!FS.analyzePath(root).exists) { + //console.log('EM_ASM mkdir '+root); + FS.mkdir(root); + //console.log('EM_ASM mount '+root); + FS.mount(NODEFS, {root : path.substr(0,3) }, root); + } + } + }, filename.c_str()); + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path EM_ASM_ passed " << std::endl; +#endif + + std::string f = std::string("/nodefs-mount"); + + if(filename.length() > 2 && filename.at(1) == ':') { + f += std::string("/") + filename.at(0) + filename.substr(2); + } else { + f += filename; + } + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path opening virtual path: " << f << std::endl; +#endif + + return f; +} + +#endif diff --git a/core/tests/unit/emscripten_filesystem.h b/core/tests/unit/emscripten_filesystem.h new file mode 100644 index 0000000000000000000000000000000000000000..c5fb8e016a077464ca17c72cbf760c293a8deee1 --- /dev/null +++ b/core/tests/unit/emscripten_filesystem.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef __EMSCRIPTEN__ +#include + +const std::string get_wasm_file_path(const std::string& filename); + +#endif \ No newline at end of file diff --git a/core/tests/unit/json/meson.build b/core/tests/unit/json/meson.build index 42106976cd27d5864751950d24c06e0fd120cb6a..6289d7db0cb1f6bba414e7ff816a80615600a2e9 100644 --- a/core/tests/unit/json/meson.build +++ b/core/tests/unit/json/meson.build @@ -4,7 +4,7 @@ # Authors: Tim Eves (TSE) # -if compiler.get_id() == 'emscripten' +if cpp_compiler.get_id() == 'emscripten' tests_flags = ['-lnodefs.js'] else tests_flags = [] diff --git a/core/tests/unit/kmnkbd/action_items.hpp b/core/tests/unit/kmnkbd/action_items.hpp index 676e904eed15482e09611a606ee36162f06bbc21..253b8c740a5aea83f74a796cee69f2380704a189 100644 --- a/core/tests/unit/kmnkbd/action_items.hpp +++ b/core/tests/unit/kmnkbd/action_items.hpp @@ -2,7 +2,7 @@ #include #include -#include "../test_assert.h" +#include namespace { diff --git a/core/tests/unit/kmnkbd/debug_api.cpp b/core/tests/unit/kmnkbd/debug_api.cpp index b8c5b4c6ecb88e72df19b5e2703e05a41ec4e8af..b4cfae6ab39494a095978f0ebf4a9c16e1a98420 100644 --- a/core/tests/unit/kmnkbd/debug_api.cpp +++ b/core/tests/unit/kmnkbd/debug_api.cpp @@ -18,7 +18,8 @@ #include "action_items.hpp" #include "debug_items.hpp" -#include "../test_assert.h" +#include +#include "../emscripten_filesystem.h" using namespace km::kbp::kmx; @@ -30,7 +31,7 @@ km_kbp_option_item test_env_opts[] = km_kbp_keyboard * test_kb = nullptr; km_kbp_state * test_state = nullptr; km_kbp_context_item * citems = nullptr; -char * arg_path; +std::string arg_path; void teardown() { if(citems) { @@ -71,7 +72,7 @@ void setup(const char *keyboard) { * Test 1: Start with debugging disabled */ void test_debugging_disabled() { - setup("000 - null keyboard.kmx"); + setup("k_000___null_keyboard.kmx"); try_status(km_kbp_state_debug_set(test_state, 0)); try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT, 1, KM_KBP_EVENT_FLAG_DEFAULT)); assert(debug_items(test_state, { @@ -88,7 +89,7 @@ void test_debugging_disabled() { * Test 2: Debugging enabled, no rule match */ void test_debugging_no_rule_match() { - setup("000 - null keyboard.kmx"); + setup("k_000___null_keyboard.kmx"); DEBUG_GROUP gp = {u"Main"}; try_status(km_kbp_state_debug_set(test_state, 1)); try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT, 1, KM_KBP_EVENT_FLAG_DEFAULT)); @@ -109,7 +110,7 @@ void test_debugging_no_rule_match() { * Test 3: Debugging enabled, function key pressed, no rule match */ void test_debugging_function_key() { - setup("000 - null keyboard.kmx"); + setup("k_000___null_keyboard.kmx"); DEBUG_GROUP gp = {u"Main"}; try_status(km_kbp_state_debug_set(test_state, 1)); try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F1, 0, 1, KM_KBP_EVENT_FLAG_DEFAULT)); @@ -131,9 +132,9 @@ void test_debugging_function_key() { * Test 4: basic rule match */ void test_basic_rule_matches() { - setup("002 - basic input Unicode.kmx"); + setup("k_002___basic_input_unicode.kmx"); DEBUG_GROUP gp = {u"Main"}; - DEBUG_KEY kp = { 'F', /*line*/16, /*shift*/0 }; // vkey is a char + DEBUG_KEY kp = { 'F', /*line*/17, /*shift*/0 }; // vkey is a char try_status(km_kbp_state_debug_set(test_state, 1)); // 'DE' + 'F' > U+0E04 U+0E05 U+0E06 @@ -197,12 +198,12 @@ void test_basic_rule_matches() { * Test 5: Multiple groups */ void test_multiple_groups() { - setup("030 - multiple groups.kmx"); + setup("k_030___multiple_groups.kmx"); DEBUG_GROUP gp = {u"Main"}, gpa = {u"a"}, gpb = {u"b"}; - DEBUG_KEY kp1 = { KM_KBP_VKEY_1, /*line*/12, /*shift*/KM_KBP_MODIFIER_VIRTUALKEY }, - kp2 = { KM_KBP_VKEY_2, /*line*/13, /*shift*/KM_KBP_MODIFIER_VIRTUALKEY }, - kpa = { 0, /*line*/19, /*shift*/0 }, - kpb = { 0, /*line*/23, /*shift*/0 }; + DEBUG_KEY kp1 = { KM_KBP_VKEY_1, /*line*/13, /*shift*/KM_KBP_MODIFIER_VIRTUALKEY }, + kp2 = { KM_KBP_VKEY_2, /*line*/14, /*shift*/KM_KBP_MODIFIER_VIRTUALKEY }, + kpa = { 0, /*line*/20, /*shift*/0 }, + kpb = { 0, /*line*/24, /*shift*/0 }; try_status(km_kbp_state_debug_set(test_state, 1)); @@ -278,10 +279,10 @@ void test_multiple_groups() { * Test 6: store offsets */ void test_store_offsets() { - setup("044 - if and context.kmx"); + setup("k_044___if_and_context.kmx"); DEBUG_GROUP gp = {u"Main"}; - DEBUG_KEY kpa = { 'a', /*line*/15, }, - kpb = { 'b', /*line*/19, }; + DEBUG_KEY kpa = { 'a', /*line*/16, }, + kpb = { 'b', /*line*/20, }; try_status(km_kbp_state_debug_set(test_state, 1)); @@ -352,9 +353,9 @@ void test_store_offsets() { * Test 7: set option */ void test_set_option() { - setup("021 - options.kmx"); + setup("k_021___options.kmx"); DEBUG_GROUP gp = {u"Main"}; - DEBUG_KEY kp1 = { '1', /*line*/16, }; + DEBUG_KEY kp1 = { '1', /*line*/17, }; DEBUG_STORE sp = {0, u"foo", u"1"}; try_status(km_kbp_state_debug_set(test_state, 1)); @@ -379,9 +380,9 @@ void test_set_option() { * Test 8: save option */ void test_save_option() { - setup("023 - options with save.kmx"); + setup("k_023___options_with_save.kmx"); DEBUG_GROUP gp = {u"Main"}; - DEBUG_KEY kp1 = { '2', /*line*/19, }; + DEBUG_KEY kp1 = { '2', /*line*/21, }; // DEBUG_STORE sp = {0, u"foo", u"0"}; km_kbp_option_item opt = {u"foo", u"0", KM_KBP_OPT_KEYBOARD}; @@ -412,7 +413,7 @@ void test_save_option() { * an 'unknown' backspace because we are at start-of-context */ void test_backspace_markers() { - setup("000 - null keyboard.kmx"); + setup("k_000___null_keyboard.kmx"); km_kbp_context_item marker_context[] = { {KM_KBP_CT_MARKER, {0,}, {1}}, {KM_KBP_CT_MARKER, {0,}, {1}}, @@ -496,7 +497,11 @@ int main(int argc, char *argv []) { } console_color::enabled = console_color::isaterminal() || arg_color; +#ifdef __EMSCRIPTEN__ + arg_path = get_wasm_file_path(argv[arg_color ? 2 : 1]); +#else arg_path = argv[arg_color ? 2 : 1]; +#endif test_debugging_disabled(); test_debugging_no_rule_match(); diff --git a/core/tests/unit/kmnkbd/debug_items.hpp b/core/tests/unit/kmnkbd/debug_items.hpp index 137325d7315b2e228ac690c5e90bad64f45eb115..95703005c45e573a8e57bd955dbac2396de5bf48 100644 --- a/core/tests/unit/kmnkbd/debug_items.hpp +++ b/core/tests/unit/kmnkbd/debug_items.hpp @@ -4,7 +4,7 @@ #include "kmx/kmx_base.h" #include "kmx/kmx_xstring.h" -#include "../test_assert.h" +#include using namespace km::kbp::kmx; diff --git a/core/tests/unit/kmnkbd/meson.build b/core/tests/unit/kmnkbd/meson.build index 16884298e574ec81307019594aa5c39b9c09fad9..049770fa2f7bebf3786cc24b042abcb0c8fcf8bf 100644 --- a/core/tests/unit/kmnkbd/meson.build +++ b/core/tests/unit/kmnkbd/meson.build @@ -4,7 +4,8 @@ # Authors: Tim Eves (TSE) # -if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' or compiler.get_id() == 'emscripten' +# TODO -- why are these differing from the standard.meson.build flags? +if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' or cpp_compiler.get_id() == 'emscripten' warns = [ '-Wno-missing-field-initializers', '-Wno-unused-parameter' @@ -13,7 +14,7 @@ else warns = [] endif -defns+=['-DKMN_KBP_STATIC'] +local_defns = ['-DKMN_KBP_STATIC'] tests = [ ['context-api', 'context_api.cpp'], ['keyboard-api', 'keyboard_api.cpp'], @@ -24,19 +25,16 @@ tests = [ ['kmx_context', 'test_kmx_context.cpp'], ] -if compiler.get_id() == 'emscripten' - tests_flags = ['--embed-file', join_paths(meson.current_source_dir(),'..','kmx','@/')] - source_path = '/' - test_path = '/' -else - tests_flags = [] - source_path = meson.current_source_dir() - test_path = join_paths(meson.current_source_dir(), '..', 'kmx') +test_path = join_paths(meson.current_build_dir(), '..', 'kmx') +tests_flags = [] + +if cpp_compiler.get_id() == 'emscripten' + tests_flags += ['-lnodefs.js', '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\']'] endif foreach t : tests - bin = executable(t[0], t[1], - cpp_args: defns + warns, + bin = executable(t[0], [t[1], '../emscripten_filesystem.cpp'], + cpp_args: local_defns + defns + warns, include_directories: [inc, libsrc], link_args: links + tests_flags, objects: lib.extract_all_objects(recursive: false)) diff --git a/core/tests/unit/kmnkbd/state_api.cpp b/core/tests/unit/kmnkbd/state_api.cpp index 37964489b78fb05dc62076e1857f8465fa94a859..3ae446541c0f2a97d9ab6e6dd77ade78fc92cd18 100644 --- a/core/tests/unit/kmnkbd/state_api.cpp +++ b/core/tests/unit/kmnkbd/state_api.cpp @@ -15,7 +15,7 @@ #include "state.hpp" #include "action_items.hpp" -#include "../test_assert.h" +#include #if defined(__GNUC__) || defined(__clang__) #define PRAGMA(X) _Pragma(#X) diff --git a/core/tests/unit/kmnkbd/test_kmx_context.cpp b/core/tests/unit/kmnkbd/test_kmx_context.cpp index a05dfed543b070aec5c0ca939659775bf7120210..1023d1611e3244eee02b25f48667b9d7f73f30cb 100644 --- a/core/tests/unit/kmnkbd/test_kmx_context.cpp +++ b/core/tests/unit/kmnkbd/test_kmx_context.cpp @@ -13,7 +13,7 @@ #include "../../../src/kmx/kmx_context.h" #include "../../../src/kmx/kmx_processevent.h" // for Debug_UnicodeString #include "../../../src/kmx/kmx_xstring.h" -#include "../test_assert.h" +#include using namespace km::kbp::kmx; using namespace std; diff --git a/core/tests/unit/kmnkbd/test_kmx_xstring.cpp b/core/tests/unit/kmnkbd/test_kmx_xstring.cpp index c4b94c52b21e4cca4258ebcbab5885d3cc23e30c..d578fa7e3813aa0e86e64a8855a360d5c78a5988 100644 --- a/core/tests/unit/kmnkbd/test_kmx_xstring.cpp +++ b/core/tests/unit/kmnkbd/test_kmx_xstring.cpp @@ -19,7 +19,7 @@ #include #include "../../../src/kmx/kmx_xstring.h" #include -#include "../test_assert.h" +#include using namespace km::kbp::kmx; using namespace std; diff --git a/core/tests/unit/kmx/000 - null keyboard.kmx b/core/tests/unit/kmx/000 - null keyboard.kmx deleted file mode 100644 index f0e9da6bcc360f9316a509cccb5cc1efad7d2718..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/000 - null keyboard.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/001 - basic input UnicodeI.kmx b/core/tests/unit/kmx/001 - basic input UnicodeI.kmx deleted file mode 100644 index fc59fa6a2d6ac3b030a34a8f49d555e87921c20e..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/001 - basic input UnicodeI.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/002 - basic input Unicode.kmx b/core/tests/unit/kmx/002 - basic input Unicode.kmx deleted file mode 100644 index 0d2539a641a2f064f9670526ef56c6903486ace3..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/002 - basic input Unicode.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/003 - nul.kmx b/core/tests/unit/kmx/003 - nul.kmx deleted file mode 100644 index 79046c521a884a372174b08c1cef5814648fcd9b..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/003 - nul.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/004 - basic input (shift 2).kmx b/core/tests/unit/kmx/004 - basic input (shift 2).kmx deleted file mode 100644 index a31840fa2432fb62870858a2cb01dd1582792eff..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/004 - basic input (shift 2).kmx and /dev/null differ diff --git a/core/tests/unit/kmx/005 - nul with initial context.kmx b/core/tests/unit/kmx/005 - nul with initial context.kmx deleted file mode 100644 index 79046c521a884a372174b08c1cef5814648fcd9b..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/005 - nul with initial context.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/006 - vkey input (shift ctrl).kmx b/core/tests/unit/kmx/006 - vkey input (shift ctrl).kmx deleted file mode 100644 index 5320c4405c43259e98b48c703396d92a8b0e09d5..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/006 - vkey input (shift ctrl).kmx and /dev/null differ diff --git a/core/tests/unit/kmx/007 - vkey input (ctrl alt).kmx b/core/tests/unit/kmx/007 - vkey input (ctrl alt).kmx deleted file mode 100644 index a76e77d6d4f3826d54902938a897a709427896a0..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/007 - vkey input (ctrl alt).kmx and /dev/null differ diff --git a/core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmx b/core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmx deleted file mode 100644 index c197fa727a4accf5354be51c45ab050b7f967a45..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/008 - vkey input (ctrl alt 2).kmx and /dev/null differ diff --git a/core/tests/unit/kmx/012 - ralt.kmx b/core/tests/unit/kmx/012 - ralt.kmx deleted file mode 100644 index cbab878a8a91bcbc1fc0bfdfbd5217843ce73df6..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/012 - ralt.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/013 - deadkeys.kmx b/core/tests/unit/kmx/013 - deadkeys.kmx deleted file mode 100644 index 68d8f4a313dd749435ea44f965d33f73d5e2f867..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/013 - deadkeys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/014 - groups and virtual keys.kmx b/core/tests/unit/kmx/014 - groups and virtual keys.kmx deleted file mode 100644 index 5a24d0909e5de0e01a61f54b55e6a4b2b6aa8f5d..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/014 - groups and virtual keys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/015 - ralt 2.kmx b/core/tests/unit/kmx/015 - ralt 2.kmx deleted file mode 100644 index f565b4fee44512c9a04ca4241c6e9cc0625f2576..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/015 - ralt 2.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/017 - space mnemonic kbd.kmx b/core/tests/unit/kmx/017 - space mnemonic kbd.kmx deleted file mode 100644 index 797014bda1e64c5e00a8458fb0450cb3a7fda348..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/017 - space mnemonic kbd.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/018 - nul testing.kmx b/core/tests/unit/kmx/018 - nul testing.kmx deleted file mode 100644 index 13204ad251e30822fec75021ef01248bd8b14688..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/018 - nul testing.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/019 - multiple deadkeys.kmx b/core/tests/unit/kmx/019 - multiple deadkeys.kmx deleted file mode 100644 index 44113e2d350ffba99dc565b5a32717b810f9d0c2..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/019 - multiple deadkeys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/020 - deadkeys and backspace.kmx b/core/tests/unit/kmx/020 - deadkeys and backspace.kmx deleted file mode 100644 index 3c245041b06b12ae74bc6dc6be8af2275805597e..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/020 - deadkeys and backspace.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/021 - options.kmx b/core/tests/unit/kmx/021 - options.kmx deleted file mode 100644 index c5550c1018678e1bbc9710042d906831e8a75f34..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/021 - options.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/022 - options with preset.kmx b/core/tests/unit/kmx/022 - options with preset.kmx deleted file mode 100644 index 57f3720de7a20f83ab4cdf0f40c0c7bc0dd563c7..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/022 - options with preset.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/023 - options with save.kmx b/core/tests/unit/kmx/023 - options with save.kmx deleted file mode 100644 index f6689ce47ee12a3dc9a4f9253ba049a7f568d15d..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/023 - options with save.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/024 - options with save and preset.kmx b/core/tests/unit/kmx/024 - options with save and preset.kmx deleted file mode 100644 index 029a136f7881af7758744280096d116ed0c57dd6..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/024 - options with save and preset.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/025 - options with reset.kmx b/core/tests/unit/kmx/025 - options with reset.kmx deleted file mode 100644 index e1595c84667399496c72de3a8f6afc2baf926f77..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/025 - options with reset.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/026 - system stores.kmx b/core/tests/unit/kmx/026 - system stores.kmx deleted file mode 100644 index da2e0b30c091ee33aff3a38498015793bd8d10cf..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/026 - system stores.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/027 - system stores 2.kmx b/core/tests/unit/kmx/027 - system stores 2.kmx deleted file mode 100644 index 43d58aaa2bcd557f66e55ab294e582e5dde9c0ba..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/027 - system stores 2.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/028 - smp.kmx b/core/tests/unit/kmx/028 - smp.kmx deleted file mode 100644 index da89544b3e99343683c943c08c0dc690632990be..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/028 - smp.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/029 - beep.kmx b/core/tests/unit/kmx/029 - beep.kmx deleted file mode 100644 index 1e809bdb338e230c2397218ec049c5f8ca4254d1..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/029 - beep.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/030 - multiple groups.kmx b/core/tests/unit/kmx/030 - multiple groups.kmx deleted file mode 100644 index 9b75bfca40c4ba3ec248c47f42e4ecc0f2e982a2..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/030 - multiple groups.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/031 - caps lock.kmx b/core/tests/unit/kmx/031 - caps lock.kmx deleted file mode 100644 index e317f13349c7e2582fea5f7b1cd91b59e30820a3..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/031 - caps lock.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/032 - caps control.kmx b/core/tests/unit/kmx/032 - caps control.kmx deleted file mode 100644 index 67d7ac2102871d8273f32634bb3929be43f3562e..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/032 - caps control.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/033 - caps always off.kmx b/core/tests/unit/kmx/033 - caps always off.kmx deleted file mode 100644 index 08bd6521fe6c458c83b95fb857eff1a91ff6eba6..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/033 - caps always off.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/034 - options double set reset.kmx b/core/tests/unit/kmx/034 - options double set reset.kmx deleted file mode 100644 index 92ef0da0ad6ebd1a189e6447dad159ee0e0e8341..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/034 - options double set reset.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/035 - options double set staged.kmx b/core/tests/unit/kmx/035 - options double set staged.kmx deleted file mode 100644 index 61970709ea2fa70ccd38d8ec89a5cd1cd8cfd90b..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/035 - options double set staged.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/036 - options - double reset staged.kmx b/core/tests/unit/kmx/036 - options - double reset staged.kmx deleted file mode 100644 index 2cc4b21993b47a726ce8dd160c1cefc403e4b336..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/036 - options - double reset staged.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/037 - options - double reset.kmx b/core/tests/unit/kmx/037 - options - double reset.kmx deleted file mode 100644 index 2e8120f6e501b4f97829be0c99eacb2cafa5cb51..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/037 - options - double reset.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/038 - punctkeys.kmx b/core/tests/unit/kmx/038 - punctkeys.kmx deleted file mode 100644 index 2b892d51b8a326d45a3c6592df2b08a9de5ee489..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/038 - punctkeys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/039 - generic ctrlalt.kmx b/core/tests/unit/kmx/039 - generic ctrlalt.kmx deleted file mode 100644 index 897c02624daf5dbdb092a801d23c61267060a617..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/039 - generic ctrlalt.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/040 - long context.kmx b/core/tests/unit/kmx/040 - long context.kmx deleted file mode 100644 index c6025add0a57e2deb6a66f062c75836a74de1dac..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/040 - long context.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/041 - long context and deadkeys.kmx b/core/tests/unit/kmx/041 - long context and deadkeys.kmx deleted file mode 100644 index d92dbb46ee67632f241d64f62efea1ef663d961e..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/041 - long context and deadkeys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/042 - long context and split deadkeys.kmx b/core/tests/unit/kmx/042 - long context and split deadkeys.kmx deleted file mode 100644 index 49b539ffb104000a192767736f40a4bfc8d123d1..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/042 - long context and split deadkeys.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/043 - output and keystroke.kmx b/core/tests/unit/kmx/043 - output and keystroke.kmx deleted file mode 100644 index 120b42e2bc5539e3b825d750deba5a6ab9c5e345..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/043 - output and keystroke.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/044 - if and context.kmx b/core/tests/unit/kmx/044 - if and context.kmx deleted file mode 100644 index ae70c6ab81338650ff8173175ab3a36b0c78ed37..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/044 - if and context.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/045 - deadkey and context.kmx b/core/tests/unit/kmx/045 - deadkey and context.kmx deleted file mode 100644 index 2ae9ff2eb60e6cff911968f99f5b84bac19ad41f..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/045 - deadkey and context.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/046 - deadkey and contextex.kmx b/core/tests/unit/kmx/046 - deadkey and contextex.kmx deleted file mode 100644 index 8a1a97b556a1404c36b29fab22b1119efc920633..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/046 - deadkey and contextex.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/047 - caps always off initially on.kmx b/core/tests/unit/kmx/047 - caps always off initially on.kmx deleted file mode 100644 index a890017c4cd0de5589a95a9b974bee5972773a4d..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/047 - caps always off initially on.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/048 - modifier keys keep context.kmx b/core/tests/unit/kmx/048 - modifier keys keep context.kmx deleted file mode 100644 index 52c70f61e048f18e841e18d1137ba6a7c988e63b..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/048 - modifier keys keep context.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/README.md b/core/tests/unit/kmx/README.md index a1ced307f9b68198ec1df22c5f03900af6b7d3ce..909d891f7da9dbe7b894c1592cdaf46572afa173 100644 --- a/core/tests/unit/kmx/README.md +++ b/core/tests/unit/kmx/README.md @@ -65,12 +65,12 @@ Alternatively it's possible to run a single test with: ```bash cd core -build/arch/debug/tests/unit/kmx/kmx 'tests/unit/kmx/038 - punctkeys.kmn' 'tests/unit/kmx/038 - punctkeys.kmx' +build/arch/debug/tests/unit/kmx/kmx '../common/test/keyboards/baseline/k_038___punctkeys.kmn' '../common/test/keyboards/baseline/k_038___punctkeys.kmx' ``` or shorter: ```bash cd core -build/arch/debug/tests/unit/kmx/kmx 'tests/unit/kmx/038 - punctkeys'.km{n,x} +build/arch/debug/tests/unit/kmx/kmx '../common/test/keyboards/baseline/k_038___punctkeys'.km{n,x} ``` diff --git a/core/tests/unit/kmx/fixtures/binary/meson.build b/core/tests/unit/kmx/fixtures/binary/meson.build index 5998a3b1e96de454acda8006b5fe7d7b3d532dbf..fb01836edeb241fba410be0e60d57e4f7d04a94a 100644 --- a/core/tests/unit/kmx/fixtures/binary/meson.build +++ b/core/tests/unit/kmx/fixtures/binary/meson.build @@ -9,11 +9,7 @@ # Build all binary test keyboards -if compiler.get_id() == 'emscripten' - binary_test_path = '/' -else - binary_test_path = meson.current_build_dir() -endif +binary_test_path = meson.current_build_dir() binary_tests = [ 'b_000_zero_checksum' diff --git a/core/tests/unit/kmx/kmx.cpp b/core/tests/unit/kmx/kmx.cpp index 8f91844fbb7c1e4f821f36caabb9baea5e791560..a0c8eaa1b2cba2c5e61732df15c4128ec5419247 100644 --- a/core/tests/unit/kmx/kmx.cpp +++ b/core/tests/unit/kmx/kmx.cpp @@ -24,8 +24,9 @@ #include "state.hpp" #include "utfcodec.hpp" -#include "../test_assert.h" -#include "../test_color.h" +#include +#include +#include "../emscripten_filesystem.h" #include "kmx_test_source.hpp" @@ -334,5 +335,10 @@ int main(int argc, char *argv[]) { console_color::enabled = console_color::isaterminal() || arg_color; km::kbp::kmx::g_debug_ToConsole = TRUE; + +#ifdef __EMSCRIPTEN__ + return run_test(get_wasm_file_path(argv[first_arg]), get_wasm_file_path(argv[first_arg + 1])); +#else return run_test(argv[first_arg], argv[first_arg + 1]); +#endif } diff --git a/core/tests/unit/kmx/kmx_imsample.kmx b/core/tests/unit/kmx/kmx_imsample.kmx deleted file mode 100644 index 754c0985ae2bf73e99d134f34a1254319968f309..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/kmx_imsample.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/kmx_imx.cpp b/core/tests/unit/kmx/kmx_imx.cpp index 2afb9c63bc037631db4fc7c3201f5f98f0097077..b93525724633f32da92a33abd61e49962af3c760 100644 --- a/core/tests/unit/kmx/kmx_imx.cpp +++ b/core/tests/unit/kmx/kmx_imx.cpp @@ -11,12 +11,13 @@ #include "path.hpp" #include "state.hpp" #include "../kmnkbd/action_items.hpp" -#include "../test_assert.h" -#include "../test_color.h" +#include +#include +#include "../emscripten_filesystem.h" #include -#include -#include +#include +#include /** This test will test the infrastructure around IMX third party librays * and callback. The functions tested are: @@ -31,10 +32,10 @@ km_kbp_option_item test_env_opts[] = KM_KBP_OPTIONS_END }; -std::map expected_imx_map { {4, "imsample.dll:DF"}, - {5, "imsample.dll:func2"}, - {6, "another.dll:func3"}, - {7, "another.dll:func4"}, +std::map expected_imx_map { {3, "imsample.dll:DF"}, + {4, "imsample.dll:func2"}, + {5, "another.dll:func3"}, + {6, "another.dll:func4"}, }; std::map g_extract_imx_map; @@ -97,7 +98,9 @@ uint8_t test_imx_callback(km_kbp_state *state, uint32_t imx_id, void *callback_o std::cout << "test_imx_callback function name: " << it->second << std::endl; km_kbp_action_item *a_items = new km_kbp_action_item[10]; switch (imx_id) { - case 4: + // this corresponds to store index; if these change, the values + // here will need updating + case 3: a_items[0].type = KM_KBP_IT_CHAR; a_items[0].character = km_kbp_usv('X'); @@ -105,7 +108,7 @@ uint8_t test_imx_callback(km_kbp_state *state, uint32_t imx_id, void *callback_o a_items[2].type = KM_KBP_IT_END; km_kbp_state_queue_action_items(state, a_items); break; - case 5: + case 4: a_items[0].type = KM_KBP_IT_CHAR; a_items[0].character = km_kbp_usv('Y'); @@ -116,7 +119,7 @@ uint8_t test_imx_callback(km_kbp_state *state, uint32_t imx_id, void *callback_o a_items[3].type = KM_KBP_IT_END; km_kbp_state_queue_action_items(state, a_items); break; - case 6: + case 5: a_items[0].type = KM_KBP_IT_BACK; a_items[0].backspace.expected_type = KM_KBP_BT_CHAR; a_items[0].backspace.expected_value = km_kbp_usv('A'); @@ -125,7 +128,7 @@ uint8_t test_imx_callback(km_kbp_state *state, uint32_t imx_id, void *callback_o a_items[2].type = KM_KBP_IT_END; km_kbp_state_queue_action_items(state, a_items); break; - case 7: { + case 6: { a_items[0].type = KM_KBP_IT_INVALIDATE_CONTEXT; a_items[1].type = KM_KBP_IT_END; km_kbp_state_queue_action_items(state, a_items); @@ -260,7 +263,14 @@ int main(int argc, char *argv []) { } console_color::enabled = console_color::isaterminal() || arg_color; km::kbp::kmx::g_debug_ToConsole = TRUE; + +#ifdef __EMSCRIPTEN__ + test_imx_list(get_wasm_file_path(argv[first_arg])); + test_queue_actions(get_wasm_file_path(argv[first_arg])); +#else test_imx_list(argv[first_arg]); test_queue_actions(argv[first_arg]); +#endif + return 0; } diff --git a/core/tests/unit/kmx/kmx_key_list.cpp b/core/tests/unit/kmx/kmx_key_list.cpp index 0bd2bce2483c0971f57537030422aeff8de760b8..d4750c44d3a9a1a6448d3715647640a16b68a685 100644 --- a/core/tests/unit/kmx/kmx_key_list.cpp +++ b/core/tests/unit/kmx/kmx_key_list.cpp @@ -12,8 +12,9 @@ #include "state.hpp" #include -#include "../test_assert.h" -#include "../test_color.h" +#include +#include +#include "../emscripten_filesystem.h" using namespace km::kbp::kmx; @@ -107,7 +108,11 @@ int main(int argc, char *argv []) { } console_color::enabled = console_color::isaterminal() || arg_color; +#ifdef __EMSCRIPTEN__ + test_key_list(get_wasm_file_path(argv[first_arg])); +#else test_key_list(argv[first_arg]); +#endif return 0; } diff --git a/core/tests/unit/kmx/kmx_key_list.kmx b/core/tests/unit/kmx/kmx_key_list.kmx deleted file mode 100644 index efb6540fcec6057653b03180095b2da757b0c222..0000000000000000000000000000000000000000 Binary files a/core/tests/unit/kmx/kmx_key_list.kmx and /dev/null differ diff --git a/core/tests/unit/kmx/meson.build b/core/tests/unit/kmx/meson.build index 048b8880112b59989b7bd9ba2e4e04078d87bbfc..7e4b9f8a77c349935debb0dd4febb62eac1e0d0f 100644 --- a/core/tests/unit/kmx/meson.build +++ b/core/tests/unit/kmx/meson.build @@ -5,7 +5,8 @@ # History: 19 Oct 2018 - TSE - Added test for context API functions. # -if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' or compiler.get_id() == 'emscripten' +# TODO -- why are these differing from the standard.meson.build flags? +if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' or cpp_compiler.get_id() == 'emscripten' warns = [ '-Wno-missing-field-initializers', '-Wno-unused-parameter' @@ -14,116 +15,94 @@ else warns = [] endif +source_path = meson.current_source_dir() +test_path = meson.current_build_dir() +tests_flags = [] +common_test_keyboards_baseline = source_path / '../../../../common/test/keyboards/baseline' -if compiler.get_id() == 'emscripten' - tests_flags = ['--embed-file', join_paths(meson.current_source_dir(),'@/')] - # we have to link in files from the binary folder also as the binary tests - # use the kmx test runner. - tests_flags += ['--embed-file', join_paths(meson.current_build_dir(),'fixtures','binary','@')] - source_path = '/' - test_path = '/' -else - tests_flags = [] - source_path = meson.current_source_dir() - test_path = meson.current_build_dir() +if cpp_compiler.get_id() == 'emscripten' + # TODO: we will move the tests to ES6 in the future + # '-sMODULARIZE', '-sEXPORT_ES6', + tests_flags += ['-lnodefs.js', '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\']'] endif kmx = executable('kmx', - 'kmx.cpp', + ['kmx.cpp', '../emscripten_filesystem.cpp'], cpp_args: defns + warns, include_directories: [inc, libsrc, '../../kmx_test_source'], link_args: links + tests_flags, objects: [lib.extract_all_objects(recursive: false), kmx_test_source_lib.extract_all_objects(recursive: false)]) tests = [ - '000 - null keyboard', - '001 - basic input UnicodeI', - '002 - basic input Unicode', - '003 - nul', - '004 - basic input (shift 2)', - '005 - nul with initial context', - '006 - vkey input (shift ctrl)', - '007 - vkey input (ctrl alt)', - '008 - vkey input (ctrl alt 2)', - '012 - ralt', - '013 - deadkeys', - '014 - groups and virtual keys', - '015 - ralt 2', - '017 - space mnemonic kbd', - '018 - nul testing', - '019 - multiple deadkeys', - '020 - deadkeys and backspace', - - '021 - options', - '022 - options with preset', - '023 - options with save', - '024 - options with save and preset', - '025 - options with reset', - - '026 - system stores', - '027 - system stores 2', + 'k_000___null_keyboard', + 'k_001___basic_input_unicodei', + 'k_002___basic_input_unicode', + 'k_003___nul', + 'k_004___basic_input__shift_2_', + 'k_005___nul_with_initial_context', + 'k_006___vkey_input__shift_ctrl_', + 'k_007___vkey_input__ctrl_alt_', + 'k_008___vkey_input__ctrl_alt_2_', + 'k_012___ralt', + 'k_013___deadkeys', + 'k_014___groups_and_virtual_keys', + 'k_015___ralt_2', + 'k_017___space_mnemonic_kbd', + 'k_018___nul_testing', + 'k_019___multiple_deadkeys', + 'k_020___deadkeys_and_backspace', + 'k_021___options', + 'k_022___options_with_preset', + 'k_023___options_with_save', + 'k_024___options_with_save_and_preset', + 'k_025___options_with_reset', + 'k_026___system_stores', + 'k_027___system_stores_2', + 'k_028___smp', + 'k_029___beep', + 'k_030___multiple_groups', + 'k_031___caps_lock', + 'k_032___caps_control', + 'k_033___caps_always_off', + 'k_034___options_double_set_reset', + 'k_035___options_double_set_staged', + 'k_036___options___double_reset_staged', + 'k_037___options___double_reset', + 'k_038___punctkeys', + 'k_039___generic_ctrlalt', + 'k_040___long_context', + 'k_041___long_context_and_deadkeys', + 'k_042___long_context_and_split_deadkeys', + 'k_043___output_and_keystroke', + 'k_044___if_and_context', + 'k_045___deadkey_and_context', + 'k_046___deadkey_and_contextex', + 'k_047___caps_always_off_initially_on', + 'k_048___modifier_keys_keep_context' +] - '028 - smp', - '029 - beep', +node = find_program('node', required: true) - '030 - multiple groups', - '031 - caps lock', - '032 - caps control', - '033 - caps always off', - '034 - options double set reset', - '035 - options double set staged', - '036 - options - double reset staged', - '037 - options - double reset', - '038 - punctkeys', - '039 - generic ctrlalt', - '040 - long context', - '041 - long context and deadkeys', - '042 - long context and split deadkeys', - '043 - output and keystroke', - - '044 - if and context', - '045 - deadkey and context', - '046 - deadkey and contextex', - '047 - caps always off initially on', - '048 - modifier keys keep context' -] +kmc_root = meson.current_source_dir() / '../../../../developer/src/kmc/build/src/kmc.js' +kmc_cmd = [node, kmc_root] if build_machine.system() == 'windows' - kmcomp = find_program(join_paths(meson.current_source_dir(),'..','..','..','..','windows','bin','developer','kmcomp.exe'), 'kmcomp.exe', required: false) - kmcomp_cmd = [kmcomp] copy_cmd = [find_program('cmd.exe', required: true), '/c', 'copy'] cat_cmd = [find_program('cmd.exe', required: true), '/c', 'type'] else - kmcomp = find_program('kmcomp', required: false) - kmcomp_cmd = [kmcomp] - if not kmcomp.found() - wine = find_program('wine', required: false) - kmcomp = find_program('kmcomp.exe', required: false) - kmcomp_cmd = [wine, kmcomp] - if not wine.found() - kmcomp = disabler() - endif - endif copy_cmd = [find_program('cp', required: true)] cat_cmd = [find_program('cat', required: true)] endif -if compiler.get_id() == 'emscripten' - # The .kmx files are linked in to the unit test, - # so kmcomp is too late here - kmcomp = disabler() -endif - test_keyboards = '' foreach kbd : tests if test_keyboards.startswith('{') test_keyboards += ''', ''' endif - kbd_basename = 'k_' + kbd.underscorify().to_lower() test_keyboards += '''{ - "name": "''' + kbd_basename + '''", - "id": "''' + kbd_basename + '''", + "name": "''' + kbd + '''", + "id": "''' + kbd + '''", "version": "0.0", "languages": [ { @@ -132,18 +111,6 @@ foreach kbd : tests } ] }''' - if not kmcomp.found() - configure_file( - command: copy_cmd + ['@INPUT@', '@OUTPUT@'], - input: kbd + '.kmn', - output: kbd_basename + '.kmn' - ) - configure_file( - command: copy_cmd + ['@INPUT@', '@OUTPUT@'], - input: kbd + '.kmx', - output: kbd_basename + '.kmx' - ) - endif endforeach cfg = configuration_data() @@ -154,69 +121,47 @@ configure_file( output: 'kmp.json' ) -if kmcomp.found() - foreach kbd : tests - kbd_basename = 'k_' + kbd.underscorify().to_lower() - - content = run_command( - cat_cmd, files(kbd + '.kmn'), - ).stdout().strip() - - cfg = configuration_data() - cfg.set('keyboard', kbd) - cfg.set('keyboard_underscore', kbd_basename) - cfg.set('id1', 'id_' + kbd_basename + '_1') - cfg.set('id2', 'id_' + kbd_basename + '_2') - cfg.set('id3', 'id_' + kbd_basename + '_3') - cfg.set('build_dir', meson.current_build_dir()) - cfg.set('NAME', kbd) - cfg.set('CONTENT', content) - - kbd_kmn = configure_file( - configuration: cfg, - input: join_paths('template', 'keyboard.kmn.in'), - output: kbd_basename + '.kmn' - ) - kbd_kpj = configure_file( - configuration: cfg, - input: join_paths('template', 'keyboard.kpj.in'), - output: kbd_basename + '.kpj' - ) - kbd_kps = configure_file( - configuration: cfg, - input: join_paths('template', 'keyboard.kps.in'), - output: kbd_basename + '.kps' - ) - kbd_kmp = custom_target(kbd_basename + '.kmp', - input: kbd_kpj, - output: [kbd_basename + '.kmp', kbd_basename + '.kmx'], - depend_files: [kbd_kps, kbd_kmn], - depfile: kbd_basename + '.dep', - command: kmcomp_cmd + ['-s', '-d', '@INPUT@']) - - kbd_src = files(join_paths(test_path, kbd_basename) + '.kmn') - kbd_obj = join_paths(test_path, kbd_basename) + '.kmx' - # kbd_log = custom_target(kbd.underscorify() + '.kmx', - # output: kbd + '.log', - # input: kbd_src, - # command: kmcomp_cmd + ['-s', '-d', '@INPUT@', kbd_obj, '@OUTPUT@'] - # ) - - test(kbd, kmx, depends: [kbd_kmp], args: [kbd_src, kbd_obj]) - endforeach -else - foreach kbd : tests - if compiler.get_id() == 'emscripten' - kbd_basename = kbd - else - kbd_basename = 'k_' + kbd.underscorify().to_lower() - endif - kbd_src = join_paths(test_path, kbd_basename) + '.kmn' - kbd_obj = join_paths(test_path, kbd_basename) + '.kmx' - - test(kbd, kmx, args: [kbd_src, kbd_obj]) - endforeach -endif +foreach kbd : tests + kbd_src_path = common_test_keyboards_baseline / kbd + '.kmn' + + content = run_command( + cat_cmd, files(kbd_src_path), + ).stdout().strip() + + cfg = configuration_data() + cfg.set('keyboard', kbd) + cfg.set('keyboard_underscore', kbd) + cfg.set('id1', 'id_' + kbd + '_1') + cfg.set('id2', 'id_' + kbd + '_2') + cfg.set('id3', 'id_' + kbd + '_3') + cfg.set('build_dir', meson.current_build_dir()) + cfg.set('NAME', kbd) + cfg.set('CONTENT', content) + + kbd_kmn = configure_file( + copy: true, + input: kbd_src_path, + output: kbd + '.kmn' + ) + kbd_kpj = configure_file( + configuration: cfg, + input: join_paths('template', 'keyboard.kpj.in'), + output: kbd + '.kpj' + ) + kbd_kps = configure_file( + configuration: cfg, + input: join_paths('template', 'keyboard.kps.in'), + output: kbd + '.kps' + ) + kbd_kmp = custom_target(kbd + '.kmp', + input: kbd_kpj, + output: [kbd + '.kmp', kbd + '.kmx'], + depend_files: [kbd_kps, kbd_kmn], + depfile: kbd + '.dep', + command: kmc_cmd + ['build', '--debug', '--no-compiler-version', '@INPUT@']) + + test(kbd, kmx, depends: [kbd_kmp], args: [test_path / kbd + '.kmn', test_path / kbd + '.kmx']) +endforeach # binary file unit tests @@ -229,48 +174,37 @@ subdir('fixtures') # should work for Linux, macOS, and WASM. test_path = source_path -key_e = executable('key_list', 'kmx_key_list.cpp', +key_e = executable('key_list', ['kmx_key_list.cpp', '../emscripten_filesystem.cpp'], cpp_args: defns + warns, include_directories: [inc, libsrc], link_args: links + tests_flags, objects: lib.extract_all_objects(recursive: false)) test_kbd = 'kmx_key_list' -if kmcomp.found() - kbd_src = files(test_kbd + '.kmn') - kbd_obj = join_paths(meson.current_source_dir(), test_kbd) + '.kmx' - kbd_log = custom_target(test_kbd + '.kmx'.underscorify(), - output: test_kbd + '.log', - input: kbd_src, - command: kmcomp_cmd + ['-s', '-d', '@INPUT@', kbd_obj, '@OUTPUT@'] - ) - test('key_list', key_e, depends: kbd_log, args: [kbd_obj] ) -else - kbd_obj = join_paths(test_path, test_kbd) + '.kmx' - test('key_list', key_e, args: [kbd_obj] ) -endif + +kbd_src = files(test_kbd + '.kmn') +kbd_obj = join_paths(meson.current_build_dir(), test_kbd) + '.kmx' +kbd_log = custom_target(test_kbd + '.kmx'.underscorify(), + output: test_kbd + '.log', + input: kbd_src, + command: kmc_cmd + ['build', '--debug', '--no-compiler-version', '@INPUT@', '--out-file', kbd_obj] +) +test('key_list', key_e, depends: kbd_log, args: [kbd_obj] ) # test for imx list -imx_e = executable('imx_list', 'kmx_imx.cpp', +imx_e = executable('imx_list', ['kmx_imx.cpp', '../emscripten_filesystem.cpp'], cpp_args: defns + warns, include_directories: [inc, libsrc], link_args: links + tests_flags, objects: lib.extract_all_objects(recursive: false)) test_kbd = 'kmx_imsample' -if kmcomp.found() - kbd_src = files(test_kbd + '.kmn') - kbd_obj = join_paths(meson.current_source_dir(), test_kbd) + '.kmx' - kbd_log = custom_target(test_kbd + '.kmx'.underscorify(), - output: test_kbd + '.log', - input: kbd_src, - command: kmcomp_cmd + ['-s', '-d', '@INPUT@', kbd_obj, '@OUTPUT@'] - ) - test('imx_list', imx_e, depends: kbd_log, args: [kbd_obj] ) -else - kbd_obj = join_paths(test_path, test_kbd) + '.kmx' - test('imx_list', imx_e, args: [kbd_obj] ) -endif - - +kbd_src = files(test_kbd + '.kmn') +kbd_obj = join_paths(meson.current_build_dir(), test_kbd) + '.kmx' +kbd_log = custom_target(test_kbd + '.kmx'.underscorify(), + output: test_kbd + '.log', + input: kbd_src, + command: kmc_cmd + ['build', '--debug', '--no-compiler-version', '@INPUT@', '--out-file', kbd_obj] +) + test('imx_list', imx_e, depends: kbd_log, args: [kbd_obj] ) diff --git a/core/tests/unit/kmx/template/keyboard.kpj.in b/core/tests/unit/kmx/template/keyboard.kpj.in index ba612eccaaa73de22774bdd6a0a1e93b45526fce..4c49cfe418d1dcc868fe36ce66c3eaa0b6f8b225 100644 --- a/core/tests/unit/kmx/template/keyboard.kpj.in +++ b/core/tests/unit/kmx/template/keyboard.kpj.in @@ -2,9 +2,9 @@ $PROJECTPATH - false - false - false + False + False + False keyboard diff --git a/core/tests/unit/ldml/keyboards/fr-t-k0-azerty-test.xml b/core/tests/unit/ldml/keyboards/fr-t-k0-azerty-test.xml index c97825385ad96a7df206fdea9e47c36f5cefc283..21f9d64cc4f6611afa51f339a671c7cde48d8cd7 100644 --- a/core/tests/unit/ldml/keyboards/fr-t-k0-azerty-test.xml +++ b/core/tests/unit/ldml/keyboards/fr-t-k0-azerty-test.xml @@ -2,7 +2,7 @@ - + diff --git a/core/tests/unit/ldml/keyboards/fr-t-k0-azerty.xml b/core/tests/unit/ldml/keyboards/fr-t-k0-azerty.xml index b0c02180a866dd47329b502002375c39c0c2433e..8a634bda65d786574e1e21c6fdfdd9eaaa686d22 100644 --- a/core/tests/unit/ldml/keyboards/fr-t-k0-azerty.xml +++ b/core/tests/unit/ldml/keyboards/fr-t-k0-azerty.xml @@ -14,7 +14,7 @@ - + @@ -148,47 +148,50 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/unit/ldml/keyboards/k_001_tiny-test.xml b/core/tests/unit/ldml/keyboards/k_001_tiny-test.xml index 43310b71f8ef3467e31f3847898f075e40f9b8ca..63fba5778a79dbeba25bedc5ebe45ab4e368faa6 100644 --- a/core/tests/unit/ldml/keyboards/k_001_tiny-test.xml +++ b/core/tests/unit/ldml/keyboards/k_001_tiny-test.xml @@ -2,7 +2,8 @@ - + + diff --git a/core/tests/unit/ldml/keyboards/k_003_transform.xml b/core/tests/unit/ldml/keyboards/k_003_transform.xml index 747c2c0d3bb7d8c99a3b3cb3c4896ab3cf1da712..577b0e314d5990d322022ec74453268d6b0e3005 100644 --- a/core/tests/unit/ldml/keyboards/k_003_transform.xml +++ b/core/tests/unit/ldml/keyboards/k_003_transform.xml @@ -5,8 +5,12 @@ from https://github.com/unicode-org/cldr/blob/keyboard-preview/docs/ldml/tr35-ke Note: keys should start with [K_A][K_B][K_C] not [K_Y] when transforms are functioning properly. -@@keys: [K_Y][K_BKQUOTE][K_A][K_B][K_D][K_BKQUOTE][K_A][K_B][K_E][K_Q][K_BKQUOTE][K_B][K_C] -@@expected: y\u0020xd\u0020xeq\u0020m +#TODO-LDML: transforms broken +#@@keys: [K_Y][K_BKQUOTE][K_A][K_B][K_D][K_BKQUOTE][K_A][K_B][K_E][K_Q][K_BKQUOTE][K_B][K_C] +#@@expected: y\u0020xd\u0020xeq\u0020m + +@@keys: [K_T][K_O][K_D][K_O] +@@expected: todo --> @@ -18,10 +22,10 @@ Note: keys should start with [K_A][K_B][K_C] not [K_Y] when transforms are funct - - - - + + + + @@ -35,10 +39,12 @@ Note: keys should start with [K_A][K_B][K_C] not [K_Y] when transforms are funct - - - - - + + + + + + + diff --git a/core/tests/unit/ldml/keyboards/meson.build b/core/tests/unit/ldml/keyboards/meson.build index 10a64a1b98544f7caa091a3d83223a32ff3b8a44..367e0056d92ad415f82286cecc8403c82d3aaf67 100644 --- a/core/tests/unit/ldml/keyboards/meson.build +++ b/core/tests/unit/ldml/keyboards/meson.build @@ -5,14 +5,15 @@ # -# These tests have a k_001_tiny-test.xml file as well. - -tests_with_testdata = [ - 'k_001_tiny', - 'fr-t-k0-azerty', +# keyboards in resources/standards-data/ldml-keyboards/techpreview/3.0/ +# tests in resources/standards-data/ldml-keyboards/techpreview/test/ +tests_from_cldr = [ + 'ja-Latn', + 'pt-k0-abnt2', + # 'fr-t-k0-azerty', # vkey issues ] -tests = [ +tests_without_testdata = [ # disabling 000 until we have updates to core or to the keyboard so that it passes # 'k_000_null_keyboard', 'k_002_tinyu32', @@ -26,33 +27,65 @@ tests = [ 'k_102_keytest', ] +# These tests have a k_001_tiny-test.xml file as well. +tests_with_testdata = [ + 'k_001_tiny', + 'fr-t-k0-azerty', # TODO-LDML: move to cldr above (fix vkey) +] + +tests = tests_without_testdata tests += tests_with_testdata +tests_needing_copy = tests # not including cldr + +tests += tests_from_cldr # Setup kmc kmc_root = join_paths(meson.source_root(),'..','developer','src','kmc') +ldml_root = join_paths(meson.source_root(),'..','resources','standards-data','ldml-keyboards','techpreview') +ldml_data = join_paths(ldml_root, '3.0') +ldml_testdata = join_paths(ldml_root, 'test') kmc_cmd = [node, kmc_root] # Build all keyboards in output folder -foreach kbd : tests +foreach kbd : tests_needing_copy configure_file( - command: copy_cmd + ['@INPUT@', '@OUTPUT@'], + copy: true, input: kbd + '.xml', output: kbd + '.xml' ) configure_file( command: kmc_cmd + ['build', '@INPUT@', '--out-file', '@OUTPUT@'], + input: kbd + '.xml', output: kbd + '.kmx', - input: kbd + '.xml' ) endforeach foreach kbd : tests_with_testdata configure_file( command: kmc_cmd + ['build-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], + input: kbd + '-test.xml', + output: kbd + '-test.json', + ) +endforeach + + +foreach kbd : tests_from_cldr + configure_file( + copy: true, + input: ldml_data / kbd + '.xml', + output: kbd + '.xml' + ) + configure_file( + command: kmc_cmd + ['build', '@INPUT@', '--out-file', '@OUTPUT@'], + input: join_paths(ldml_data, kbd + '.xml'), + output: kbd + '.kmx', + ) + configure_file( + command: kmc_cmd + ['build-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], + input: join_paths(ldml_testdata, kbd + '-test.xml'), output: kbd + '-test.json', - input: kbd + '-test.xml' ) endforeach diff --git a/core/tests/unit/ldml/ldml.cpp b/core/tests/unit/ldml/ldml.cpp index 26210ab2f3b487fc45ecc7c9f8915fa474ac38e6..bf78d0093655cffb6c6064fcfed2775622b7f5d6 100644 --- a/core/tests/unit/ldml/ldml.cpp +++ b/core/tests/unit/ldml/ldml.cpp @@ -21,8 +21,8 @@ #include "state.hpp" #include "utfcodec.hpp" -#include "../test_assert.h" -#include "../test_color.h" +#include +#include #include // for surrogate pair macros @@ -254,8 +254,16 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests:: std::cout << "text store: " << string_to_hex(text_store) << " [" << text_store << "]" << std::endl; // Compare internal context with expected result if (text_store != action.string) return __LINE__; + } else if (action.type == km::tests::LDML_ACTION_FAIL) { + // test requested failure + std::cout << "- FAIL: " << action.string << std::endl; + return __LINE__; + } else { + std::cerr << " Err: unhandled action type " << action.type << std::endl; + return __LINE__; } } + std::cout << "- DONE" << std::endl; // Test if the beep action was as expected if (g_beep_found != test_source.get_expected_beep()) @@ -292,13 +300,19 @@ int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) { km::tests::LdmlEmbeddedTestSource embedded_test_source; + std::vector failures; // track failures for summary + int embedded_result = embedded_test_source.load_source(source); if (embedded_result == 0) { // embedded loaded OK, try it std::cout << "TEST: " << source.name() << " (embedded)" << std::endl; embedded_result = run_test(source, compiled, embedded_test_source); + if (embedded_result != 0) { + failures.push_back("in-XML (@@ comment) embedded test failed"); + } } else { + // Not an error in itself, if JSON is present. embedded_result = -1; // load failed } @@ -317,6 +331,7 @@ int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) { int sub_test = run_test(source, compiled, *n.second); if (sub_test != 0) { std::cout << " FAIL: " << json_path.stem() << "/" << n.first << std::endl; + failures.push_back(json_path.stem() + "/" + n.first); json_result = sub_test; // set to last failure } else { std::cout << " PASS: " << json_path.stem() << "/" << n.first << std::endl; @@ -334,23 +349,21 @@ int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) { std::cout << "Note: No json test." << std::endl; } + // if both are missing, that's an error in itself. if (embedded_result == -1 && json_result == -1) { // Can't both be missing. - std::cout << "Error: Need either embedded test (@@ directives in " << source.name() << ") or " << json_path.name() << std::endl; - return __LINE__; - } else if (embedded_result == -1) { - return json_result; // Return JSON if embedded missing. - } else if (json_result == -1) { - return embedded_result; // Return embedded if JSON missing + failures.push_back("Error: Need either embedded test (@@ directives) or json test"); } - // we have both tests. - if (embedded_result == 0) { - return json_result; // Both passed or JSON failed - } else if(json_result == 0) { - return embedded_result; // Embedded may have failed. + // recap the failures + if (failures.size() > 0) { + for (const auto& f : failures) { + std::cerr << "failure summary: " << f << std::endl; + } + return -1; } else { - return json_result; + std::cout << "run_all_tests passed" << std::endl; + return 0; } } diff --git a/core/tests/unit/ldml/ldml_test_source.cpp b/core/tests/unit/ldml/ldml_test_source.cpp index 219a143632c75c677aa1c412cb07772fcd6ab056..614a4606888876ff499fdd3e2d9b4c730786ef42 100644 --- a/core/tests/unit/ldml/ldml_test_source.cpp +++ b/core/tests/unit/ldml/ldml_test_source.cpp @@ -31,6 +31,16 @@ #include "utfcodec.hpp" #include "ldml_test_source.hpp" +#include "ldml_test_utils.hpp" + +#if defined(HAVE_ICU4C) +// TODO-LDML: Needed this for some compiler warnings +#define U_FALLTHROUGH +#include "unicode/uniset.h" +#include "unicode/usetiter.h" +#else +#error icu4c is required for this test +#endif #define assert_or_return(expr) if(!(expr)) { \ std::wcerr << __FILE__ << ":" << __LINE__ << ": " << \ @@ -45,7 +55,7 @@ namespace km { namespace tests { -#include "../test_color.h" +#include LdmlTestSource::LdmlTestSource() { @@ -270,16 +280,6 @@ LdmlTestSource::get_modifier(std::string const m) { return 0; } -km_kbp_virtual_key -LdmlTestSource::get_vk(std::string const &vk) { - for (int i = 1; i < 256; i++) { - if (vk == km::kbp::kmx::s_key_names[i]) { - return i; - } - } - return 0; -} - key_event LdmlEmbeddedTestSource::vkey_to_event(std::string const &vk_event) { // vkey format is MODIFIER MODIFIER K_NAME @@ -398,13 +398,15 @@ void LdmlJsonTestSource::set_key_from_id(key_event& k, const std::u16string& id) // First, find the string KMX_DWORD strId = kmxplus->strs->find(id); - assert(strId != 0); - if (strId == 0) { // will also get here if id is empty. + if (strId == 0) { + // will also get here if id is empty. + std::cerr << "ERROR: could not find string for " << id << std::endl; + assert(false); return; } // OK. Now we can search the keybag - KMX_DWORD keyIndex = 0; + KMX_DWORD keyIndex = 0; // initialize loop auto *key2 = kmxplus->key2Helper.findKeyByStringId(strId, keyIndex); assert(key2 != nullptr); if (key2 == nullptr) { @@ -480,6 +482,165 @@ int LdmlJsonTestSource::load(const nlohmann::json &data) { return 0; } +#if defined(HAVE_ICU4C) +class LdmlJsonRepertoireTestSource : public LdmlTestSource { +public: + LdmlJsonRepertoireTestSource(const std::string &path, km::kbp::kmx::kmx_plus *kmxplus); + virtual ~LdmlJsonRepertoireTestSource(); + virtual const std::u16string &get_context() const; + int load(const nlohmann::json &test); + virtual void next_action(ldml_action &fillin); +private: + std::string path; + nlohmann::json data; // maybe + std::string type; + std::u16string context; + std::u16string expected; + std::string chars; + std::unique_ptr uset; + std::unique_ptr iterator; + bool need_check = false; // set this after each char + const km::kbp::kmx::kmx_plus *kmxplus; + /** + * Helpers + */ + void set_key_from_id(key_event& k, const std::u16string& id); +}; + +LdmlJsonRepertoireTestSource::LdmlJsonRepertoireTestSource(const std::string &path, km::kbp::kmx::kmx_plus *k) +:path(path), kmxplus(k){ + +} + +LdmlJsonRepertoireTestSource::~LdmlJsonRepertoireTestSource() { +} + +void +LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) { + if (type != "simple") { + std::cerr << "TODO-LDML: Warning: only 'simple' is supported now, not " << type << std::endl; + fillin.type = LDML_ACTION_DONE; + return; + } + + if (!iterator->next()) { + std::cout << "TODO-LDML: end of unicode set iterator" << std::endl; + fillin.type = LDML_ACTION_DONE; + return; + } + + if (need_check) { + need_check = false; + fillin.type = LDML_ACTION_CHECK_EXPECTED; + fillin.string = expected; + return; + } + + // we have already excluded strings, so just get the codepoint + const km_kbp_usv ch = iterator->getCodepoint(); + + // as string for debugging. + // const icu::UnicodeString& str = iterator->getString(); + + km::kbp::kmx::char16_single ch16; + std::size_t len = km::kbp::kmx::Utf32CharToUtf16(ch, ch16); + std::u16string chstr = std::u16string(ch16.ch, len); + // append to expected + expected.append(chstr); + need_check = true; + + // --------------------------------------------------- + // find a key that can emit this string. + // TODO-LDML: no transforms yet. + // TODO-LDML: looking for an exact single key for now + + assert(kmxplus != nullptr); + // lookup the id + assert(kmxplus->strs != nullptr); + assert(kmxplus->key2 != nullptr); + assert(kmxplus->layr != nullptr); + + assert(kmxplus->key2Helper.valid()); + assert(kmxplus->layrHelper.valid()); + + // First, find the string as an id + // TODO-LDML: will not work for multi string cases + KMX_DWORD strId = kmxplus->strs->find(chstr); + if (strId == 0) { // will also get here if id is empty. + fillin.string = u"No string for repertoire test: "; + fillin.string.append(chstr); + fillin.type = LDML_ACTION_FAIL; + return; + } + assert(strId != 0); + + // OK. Now we can search the keybag + KMX_DWORD keyIndex = 0; + auto *key2 = kmxplus->key2Helper.findKeyByStringTo(strId, keyIndex); + if (key2 == nullptr) { + fillin.string = u"No key for repertoire test: "; + fillin.string.append(chstr); + fillin.type = LDML_ACTION_FAIL; + return; + } + + // Now, look for the _first_ candidate vkey match in the kmap. + for (KMX_DWORD kmapIndex = 0; kmapIndex < kmxplus->key2->kmapCount; kmapIndex++) { + auto *kmap = kmxplus->key2Helper.getKmap(kmapIndex); + assert(kmap != nullptr); + if (kmap->key == keyIndex) { + fillin.k = {(km_kbp_virtual_key)kmap->vkey, (uint16_t)kmap->mod}; + std::cout << "found vkey " << fillin.k.vk << ":" << fillin.k.modifier_state << std::endl; + fillin.type = LDML_ACTION_KEY_EVENT; + return; + } + } + + fillin.type = LDML_ACTION_FAIL; + fillin.string = u"Could not find candidate vkey: "; + fillin.string.append(chstr); +} + +const std::u16string & +LdmlJsonRepertoireTestSource::get_context() const { + return context; // no context needed +} + +int LdmlJsonRepertoireTestSource::load(const nlohmann::json &data) { + this->data = data; // TODO-LDML + // Need an update to json.hpp to use contains() + // if (data.contains("/type"_json_pointer)) { + if (data.find("type") != data.end()) { + type = data["/type"_json_pointer].get(); + } else { + type = "default"; + } + chars = data["/chars"_json_pointer].get(); + std::cout << "Loaded " << path << " = " << this->type << " || " << this->chars << std::endl; + UErrorCode status = U_ZERO_ERROR; + icu::StringPiece piece(chars); + icu::UnicodeString pattern = icu::UnicodeString::fromUTF8(piece); + // icu::UnicodeString pattern(chars.data(), (int32_t)chars.length()); + uset = std::unique_ptr(new icu::UnicodeSet(pattern, status)); + if (U_FAILURE(status)) { + // could be a malformed syntax isue + std::cerr << "UnicodeSet c'tor problem " << u_errorName(status) << std::endl; + return 1; + } + std::cout << "Got UnicodeSet of " << uset->size() << " char(s)." << std::endl; + // #if (U_ICU_VERSION_MAJOR_NUM >= 70) + // // TODO-LDML: function was private previously + // if (uset->hasStrings()) { + // // illegal unicodeset of this form: [a b c {this_is_a_string}] + // std::cerr << "Spec err: may not have strings. " << chars << std::endl; + // return 1; + // } + // #endif + iterator = std::unique_ptr(new icu::UnicodeSetIterator(*uset)); + return 0; +} +#endif // HAVE_ICU4C + LdmlJsonTestSourceFactory::LdmlJsonTestSourceFactory() : test_map() { } @@ -528,17 +689,15 @@ int LdmlJsonTestSourceFactory::load(const km::kbp::path &compiled, const km::kbp // TODO-LDML: store these elsewhere? std::cout << "JSON: reading " << info_name << " test of " << info_keyboard << " by " << info_author << std::endl; - // TODO-LDML: repertoire test #8435 - auto all_tests = data["/keyboardTest/tests"_json_pointer]; - assert_or_return((!all_tests.empty()) && (all_tests.size() > 0)); + assert_or_return((!all_tests.empty()) && (all_tests.size() > 0)); // TODO-LDML: can be empty if repertoire only? for(auto tests : all_tests) { auto tests_name = tests["/name"_json_pointer].get(); for (auto test : tests["/test"_json_pointer]) { auto test_name = test["/name"_json_pointer].get(); std::string test_path; - test_path.append(tests_name).append("/").append(test_name); + test_path.append(info_name).append("/tests/").append(tests_name).append("/").append(test_name); std::cout << "JSON: reading " << info_name << "/" << test_path << std::endl; std::unique_ptr subtest(new LdmlJsonTestSource(test_path, kmxplus.get())); @@ -547,6 +706,26 @@ int LdmlJsonTestSourceFactory::load(const km::kbp::path &compiled, const km::kbp } } +#if defined(HAVE_ICU4C) + auto rep_tests = data["/keyboardTest/repertoire"_json_pointer]; + assert_or_return((!rep_tests.empty()) && (rep_tests.size() > 0)); // TODO-LDML: can be empty if tests only? + + for(auto rep : rep_tests) { + auto rep_name = rep["/name"_json_pointer].get(); + + std::string test_path; + test_path.append(info_name).append("/repertoire/").append(rep_name); + + std::unique_ptr reptest(new LdmlJsonRepertoireTestSource(test_path, kmxplus.get())); + assert_or_return(reptest->load(rep) == 0); + test_map[test_path] = std::unique_ptr(reptest.release()); + } + + +#else + std::cerr << "Warning: HAVE_ICU4C not defined, so not enabling repertoire tests" << std::endl; +#endif + return 0; } diff --git a/core/tests/unit/ldml/ldml_test_source.hpp b/core/tests/unit/ldml/ldml_test_source.hpp index 7f69112b8e9b51ef016627b1a8e09377241a4455..340f0bd0e905dce09e0e661dcb9ba86e1d85a5cf 100644 --- a/core/tests/unit/ldml/ldml_test_source.hpp +++ b/core/tests/unit/ldml/ldml_test_source.hpp @@ -34,6 +34,11 @@ enum ldml_action_type { */ LDML_ACTION_CHECK_EXPECTED, // TODO-LDML: gestures, etc? Depends on touch. + + /** + * fail test. 'string' has message. + */ + LDML_ACTION_FAIL, }; struct ldml_action { @@ -60,7 +65,6 @@ public: // helper functions static key_event char_to_event(char ch); static uint16_t get_modifier(std::string const m); - static km_kbp_virtual_key get_vk(std::string const &vk); static std::u16string parse_source_string(std::string const &s); static std::u16string parse_u8_source_string(std::string const &s); diff --git a/core/tests/unit/ldml/ldml_test_utils.cpp b/core/tests/unit/ldml/ldml_test_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57c71dce37580f6a828aad566e8d4dbb98eebaec --- /dev/null +++ b/core/tests/unit/ldml/ldml_test_utils.cpp @@ -0,0 +1,27 @@ +/* + Copyright: © 2022-2023 SIL International. + Description: Internal utils for ldml tests + Create Date: 16 Mar 2023 + Authors: Steven R. Loomis (SRL) + History: Refactored out of ldml_test_source.cpp +*/ + +#include // for char to vk mapping tables + +#include "ldml_test_utils.hpp" + +namespace km { +namespace tests { + +km_kbp_virtual_key +get_vk(std::string const &vk) { + for (int i = 1; i < 256; i++) { + if (vk == km::kbp::kmx::s_key_names[i]) { + return i; + } + } + return 0; +} + +} // namespace tests +} // namespace km diff --git a/core/tests/unit/ldml/ldml_test_utils.hpp b/core/tests/unit/ldml/ldml_test_utils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8b19edf8d33d6f33ff612ef017c56cf760eb12e1 --- /dev/null +++ b/core/tests/unit/ldml/ldml_test_utils.hpp @@ -0,0 +1,24 @@ +/* + Copyright: © 2022-2023 SIL International. + Description: Internal utils for ldml tests + Create Date: 16 Mar 2023 + Authors: Steven R. Loomis (SRL) + History: Refactored out of ldml_test_source.cpp +*/ + +#pragma once + +#include +#include +#include // for char to vk mapping tables +#include // for surrogate pair macros +#include +#include + +namespace km { +namespace tests { + +km_kbp_virtual_key get_vk(std::string const &vk); + +} // namespace tests +} // namespace km diff --git a/core/tests/unit/ldml/meson.build b/core/tests/unit/ldml/meson.build index 6ef36cc5e89d88e992bfbda3b67e4d0a6c7ecb7e..a816658134e0ff263dd018294e3583e207ec32d1 100644 --- a/core/tests/unit/ldml/meson.build +++ b/core/tests/unit/ldml/meson.build @@ -4,7 +4,27 @@ # Authors: Marc Durdin # -if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' or compiler.get_id() == 'emscripten' +# ICU4C is used for repertoire tests + + +if target_machine.system() == 'linux' + # use pkg-config when targetting linux + pkgconfig = 'pkg-config' + icu_uc = dependency('icu-uc', required: true) +else + # load ICU from wrap + # Requires meson of about 0.57+ + icu4c = subproject('icu-minimal', default_options: [ 'default_library=static', 'cpp_std=c++17', 'warning_level=0', +'werror=false']) # TODO-LDML: options: static, no data (these are in the meson build files) + icu_uc = icu4c.get_variable('icuuc_dep') +endif + +if icu_uc.found() + defns += '-DHAVE_ICU4C' +endif + +# TODO -- why are these differing from the standard.meson.build flags? +if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' or cpp_compiler.get_id() == 'emscripten' warns = [ '-Wno-missing-field-initializers', '-Wno-unused-parameter' @@ -37,7 +57,7 @@ endif # Build ldml test executable -if compiler.get_id() == 'emscripten' +if cpp_compiler.get_id() == 'emscripten' tests_flags = ['--embed-file', join_paths(meson.current_build_dir(),'keyboards','@')] tests_flags += ['--embed-file', join_paths(meson.current_build_dir(),'invalid-keyboards','@')] test_path = '/' @@ -51,10 +71,13 @@ endif ldml = executable('ldml', 'ldml.cpp', 'ldml_test_source.cpp', + 'ldml_test_utils.cpp', cpp_args: defns + warns, include_directories: [inc, libsrc, '../../../../developer/src/ext/json'], link_args: links + tests_flags, - objects: lib.extract_all_objects(recursive: false)) + objects: lib.extract_all_objects(recursive: false), + dependencies: [icu_uc], + ) # Run tests on all keyboards (`tests` defined in keyboards/meson.build) @@ -76,7 +99,7 @@ endforeach # Build and run additional test_kmx_plus test e = executable('test_kmx_plus', 'test_kmx_plus.cpp', - 'ldml_test_source.cpp', + 'ldml_test_utils.cpp', cpp_args: defns + warns, include_directories: [inc, libsrc, '../../../../developer/src/ext/json'], link_args: links + tests_flags, diff --git a/core/tests/unit/ldml/test_kmx_plus.cpp b/core/tests/unit/ldml/test_kmx_plus.cpp index 9518c0bd787a3ef1e0f1a6e1e1b0dcd8a4bc9ed3..a4d83a69a7294653e49925e4c5b45aa825d8cce3 100644 --- a/core/tests/unit/ldml/test_kmx_plus.cpp +++ b/core/tests/unit/ldml/test_kmx_plus.cpp @@ -1,9 +1,13 @@ -#include "../test_assert.h" +#include #include "kmx/kmx_plus.h" #include "kmx/kmx_xstring.h" #include "../../../src/ldml/ldml_vkeys.hpp" -#include "ldml_test_source.hpp" #include +#include "ldml_test_utils.hpp" + +// needed for streaming operators +#include "utfcodec.hpp" + using namespace km::kbp::kmx; @@ -62,7 +66,7 @@ int test_ldml_vkeys() { #define ADD_VKEY(k, m) { \ const char* str = k "-" #m ; \ PKMX_WCHAR wstr = km::kbp::kmx::strtowstr(const_cast(str)); \ - vk.add(km::tests::LdmlTestSource::get_vk(k), m, wstr); \ + vk.add(km::tests::get_vk(k), m, wstr); \ delete [] wstr; \ } @@ -81,61 +85,60 @@ int test_ldml_vkeys() { ADD_VKEY("K_E", K_ALTFLAG|RCTRLFLAG); #undef ADD_VKEY - - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_A"), 0), u"K_A-0"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_A"), LCTRLFLAG), u"K_A-LCTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_A"), RCTRLFLAG), u"K_A-RCTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_A"), LALTFLAG), u"K_A-LALTFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_A"), RALTFLAG), u"K_A-RALTFLAG"); // now try either-side keys :should get the same result with either or both - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), LCTRLFLAG), u"K_B-K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), RCTRLFLAG), u"K_B-K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), LCTRLFLAG|RCTRLFLAG), u"K_B-K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), LALTFLAG), u"K_B-K_ALTFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), RALTFLAG), u"K_B-K_ALTFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_B"), LALTFLAG|RALTFLAG), u"K_B-K_ALTFLAG"); // OOOkay now try BOTH side - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_C"), LCTRLFLAG|LALTFLAG), u"K_C-K_ALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_C"), LCTRLFLAG|RALTFLAG), u"K_C-K_ALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_C"), RCTRLFLAG|LALTFLAG), u"K_C-K_ALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_C"), RCTRLFLAG|RALTFLAG), u"K_C-K_ALTFLAG|K_CTRLFLAG"); // OOOkay now try either alt - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_D"), LCTRLFLAG|LALTFLAG), u"K_D-LALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_D"), LCTRLFLAG|RALTFLAG), u"K_D-RALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_D"), RCTRLFLAG|LALTFLAG), u"K_D-LALTFLAG|K_CTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_D"), RCTRLFLAG|RALTFLAG), u"K_D-RALTFLAG|K_CTRLFLAG"); // OOOkay now try either ctrl - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_E"), LCTRLFLAG|LALTFLAG), u"K_E-K_ALTFLAG|LCTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_E"), LCTRLFLAG|RALTFLAG), u"K_E-K_ALTFLAG|LCTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_E"), RCTRLFLAG|LALTFLAG), u"K_E-K_ALTFLAG|RCTRLFLAG"); - assert_equal(vk.lookup(km::tests::LdmlTestSource::get_vk( + assert_equal(vk.lookup(km::tests::get_vk( "K_E"), RCTRLFLAG|RALTFLAG), u"K_E-K_ALTFLAG|RCTRLFLAG"); return EXIT_SUCCESS; diff --git a/developer/build.sh b/developer/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..76fc3c94a729561a576a7e59529c9d5b069805e1 --- /dev/null +++ b/developer/build.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# Build Keyman Developer components +# +# TODO: convert existing Makefiles/ into build.sh scripts; for now, +# this only builds kmc and kmcmplib + +#set -x +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +################################ Main script ################################ + +builder_describe \ + "Build Keyman Developer + + **NOTE:** this only builds kmc and kmcmplib for now (TODO on other modules)" \ + \ + clean \ + configure \ + build \ + test \ + ":kmcmplib=src/kmcmplib Compiler - .kmn compiler" \ + ":kmc-kmn=src/kmc-kmn Compiler - .kmn wrapper Keyboard Module" \ + ":kmc-ldml=src/kmc-ldml Compiler - LDML Keyboard Module" \ + ":kmc-model=src/kmc-model Compiler - Lexical Model Module" \ + ":kmc-model-info=src/kmc-model-info Compiler - .model_info Module" \ + ":kmc-package=src/kmc-package Compiler - Package Module" \ + ":kmc=src/kmc Compiler - Command Line Interface" + +builder_parse "$@" + +builder_run_child_actions clean configure build test diff --git a/developer/src/.eslintrc.cjs b/developer/src/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..0b4001b57255e2e2c43e038d7f5d72d8f47ee5c0 --- /dev/null +++ b/developer/src/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + ignorePatterns: ["**/build/**/*", "**/coverage/**/*"], + rules: {}, +}; diff --git a/developer/src/Makefile b/developer/src/Makefile index b2059a0cd99612af630d8435fa24ce97ece5a2df..d4c6bd9384ab4264dade66f3a08bbd779abc27c0 100644 --- a/developer/src/Makefile +++ b/developer/src/Makefile @@ -3,9 +3,9 @@ # !ifdef NODELPHI -TARGETS=build-tools kmcmpdll kmanalyze kmdecomp server kmc +TARGETS=build-tools kmcmplib kmcmpdll kmanalyze kmdecomp server kmc !else -TARGETS=build-tools kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst kmdecomp server kmc +TARGETS=build-tools kmcmplib kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst kmdecomp server kmc !endif EXCLUDEPATHDEFINES=1 @@ -29,7 +29,11 @@ imsample: .virtual cd $(DEVELOPER_ROOT)\src\imsample $(MAKE) $(TARGET) -kmcmpdll: .virtual +kmcmplib: global-versions .virtual + cd $(DEVELOPER_ROOT)\src\kmcmplib + $(MAKE) $(TARGET) + +kmcmpdll: kmcmplib .virtual cd $(DEVELOPER_ROOT)\src\kmcmpdll $(MAKE) $(TARGET) @@ -65,12 +69,16 @@ server: .virtual cd $(DEVELOPER_ROOT)\src\server $(MAKE) $(TARGET) -kmc: kmc-keyboard kmc-model kmc-model-info kmc-package +kmc: kmc-ldml kmc-kmn kmc-model kmc-model-info kmc-package cd $(DEVELOPER_ROOT)\src\kmc $(MAKE) $(TARGET) -kmc-keyboard: .virtual - cd $(DEVELOPER_ROOT)\src\kmc-keyboard +kmc-ldml: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-ldml + $(MAKE) $(TARGET) + +kmc-kmn: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-kmn $(MAKE) $(TARGET) kmc-model: .virtual @@ -249,7 +257,7 @@ build-tools: global-versions: cd $(COMMON_ROOT) - @if not exist $(KEYMAN_ROOT)\common\windows\cpp\include\keymanversion_build.h $(MAKE) global-versions + @if not exist $(KEYMAN_ROOT)\include\keymanversion_build.h $(MAKE) global-versions cd $(DEVELOPER_ROOT)\src build-tools-internal: diff --git a/developer/src/README.md b/developer/src/README.md index e4de6c13284b2672cd454995e92ff3fa6543a6a4..95b33c36f44be4364cb3c71f9cdf5e74f1773f10 100644 --- a/developer/src/README.md +++ b/developer/src/README.md @@ -70,7 +70,7 @@ details. node-based next generation compiler, hosts kmc, kmlmi, kmlmc, kmlmp -### src/kmc-keyboard - LDML Keyboard Compiler +### src/kmc-ldml - LDML Keyboard Compiler Next Generation keyboard compiler package - LDML keyboards only at present. Command line access through kmc. diff --git a/developer/src/common/delphi/compiler/compile.pas b/developer/src/common/delphi/compiler/compile.pas index ba136af97308b88f502837f14e9f0135a2cf1770..b89ef0eb72f63f230ba04a9142175b0ff8c278b0 100644 --- a/developer/src/common/delphi/compiler/compile.pas +++ b/developer/src/common/delphi/compiler/compile.pas @@ -64,7 +64,7 @@ type FILE_KEY = record Key: WCHAR; // WCHAR -- actually a WORD - packing: WORD; + LineStoreIndex: WORD; Line: DWORD; ShiftFlags: DWORD; dpOutput: PWideChar; // from start of key structure @@ -126,6 +126,8 @@ type dpDeadKeyArray: PFILE_DEADKEY; // temp - dead key array cxVKDictionary: DWORD; // I3438 dpVKDictionary: PFILE_VKDICTIONARY; // I3438 // temp - virtual key dictionary + + extra: Pointer; end; PFILE_KEYBOARD = ^FILE_KEYBOARD; @@ -152,13 +154,13 @@ const const // kcframe --sizeof returns these values {$IFDEF WIN64} - FILE_KEYBOARD_SIZE = 2984; + FILE_KEYBOARD_SIZE = 2992; FILE_GROUP_SIZE = 200; FILE_STORE_SIZE = 200; FILE_KEY_SIZE = 32; FILE_DEADKEY_SIZE = 160; {$ELSE} - FILE_KEYBOARD_SIZE = 2952; + FILE_KEYBOARD_SIZE = 2956; FILE_GROUP_SIZE = 188; FILE_STORE_SIZE = 192; FILE_KEY_SIZE = 20; @@ -174,6 +176,7 @@ type TCompilerOptions = record dwSize: DWORD; ShouldAddCompilerVersion: BOOL; + UseKmcmpLib: BOOL; end; COMPILER_OPTIONS = TCompilerOptions; diff --git a/developer/src/kmcmpdll/comperr.h b/developer/src/common/include/kmn_compiler_errors.h similarity index 97% rename from developer/src/kmcmpdll/comperr.h rename to developer/src/common/include/kmn_compiler_errors.h index 258825dab4c8055ad8e1f3c417928d36ee8de235..6cfae03fc9ea00c559fe53f1b55ddb0e5759b66a 100644 --- a/developer/src/kmcmpdll/comperr.h +++ b/developer/src/common/include/kmn_compiler_errors.h @@ -20,8 +20,8 @@ 22 Jul 2020 - eddieantonio - Add lexical model range https://github.com/keymanapp/keyman/pull/3385 */ // I4061 -#ifndef _COMPERR_H -#define _COMPERR_H +#ifndef _kmn_compiler_errors_h +#define _kmn_compiler_errors_h // Note: these are not be a bitmask #define CERR_FATAL 0x00008000 @@ -101,9 +101,6 @@ #define CERR_InvalidNamedCode 0x0000403F #define CERR_InvalidSystemStore 0x00004040 -#define CERR_CallIsProfessionalFeature 0x00004041 -#define CERR_IncludeCodesIsProfessionalFeature 0x00004042 -#define CERR_MnemonicLayoutIsProfessionalFeature 0x00004043 #define CERR_60FeatureOnly_VirtualCharKey 0x00004044 #define CERR_VersionAlreadyIncluded 0x00004045 @@ -233,4 +230,4 @@ #define CERR_BufferOverflow 0x000080C0 #define CERR_Break 0x000080C1 -#endif // _COMPERR_H +#endif // _kmn_compiler_errors_h diff --git a/developer/src/common/web/test-helpers/.gitignore b/developer/src/common/web/test-helpers/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d16386367f7cd7dd3c1842c484239e9e82a25efc --- /dev/null +++ b/developer/src/common/web/test-helpers/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/developer/src/common/web/test-helpers/build.sh b/developer/src/common/web/test-helpers/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..77db81e63a06ad5d3353e07896aa684c4dadb9b7 --- /dev/null +++ b/developer/src/common/web/test-helpers/build.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# +# Compiles developer test helpers +# + +# Exit on command failure and when using unset variables: +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +cd "$THIS_SCRIPT_PATH" + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +builder_describe "Build Keyman Developer test helpers" \ + "@/common/web/types" \ + "clean" \ + "configure" \ + "build" + +builder_describe_outputs \ + configure /node_modules \ + build /developer/src/common/web/test-helpers/build/index.js + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action clean; then + rm -rf ./build/ + builder_finish_action success clean +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action configure; then + verify_npm_setup + builder_finish_action success configure +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action build; then + tsc --build + builder_finish_action success build +fi diff --git a/developer/src/common/web/test-helpers/index.ts b/developer/src/common/web/test-helpers/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f34107dc91a8406b6d82c395f9fc0b0a6f21e36 --- /dev/null +++ b/developer/src/common/web/test-helpers/index.ts @@ -0,0 +1,87 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { CompilerEvent, CompilerCallbacks, CompilerSchema, CompilerPathCallbacks, CompilerFileSystemCallbacks, compilerErrorSeverityName } from '@keymanapp/common-types'; +export { verifyCompilerMessagesObject } from './verifyCompilerMessagesObject.js'; + +// TODO: schemas are only used by kmc-ldml for now, so this works at this +// time, but it's a little fragile if we need them elsewhere in the future +const SCHEMA_BASE = '../../../../kmc-ldml/build/src/'; + +/** + * A CompilerCallbacks implementation for testing + */ +export class TestCompilerCallbacks implements CompilerCallbacks { + /* TestCompilerCallbacks */ + + messages: CompilerEvent[] = []; + + clear() { + this.messages = []; + } + + printMessages() { + this.messages.forEach(event => { + const code = event.code.toString(16); + if(event.line) { + console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`); + } else { + console.log(`${compilerErrorSeverityName(event.code)} ${code}: ${event.message}`); + } + }); + } + + hasMessage(code: number): boolean { + return this.messages.find((item) => item.code == code) === undefined ? false : true; + } + + /* CompilerCallbacks */ + + loadFile(filename: string | URL): Buffer { + try { + return fs.readFileSync(filename); + } catch(e) { + if (e.code === 'ENOENT') { + return null; + } else { + throw e; + } + } + } + + get path(): CompilerPathCallbacks { + return path; + } + + get fs(): CompilerFileSystemCallbacks { + return fs; + } + + resolveFilename(baseFilename: string, filename: string): string { + const basePath = path.dirname(baseFilename); + // Transform separators to platform separators -- we are agnostic + // in our use here but path prefers files may use + // either / or \, although older kps files were always \. + if(path.sep == '/') { + filename = filename.replace(/\\/g, '/'); + } else { + filename = filename.replace(/\//g, '\\'); + } + if(!path.isAbsolute(filename)) { + filename = path.resolve(basePath, filename); + } + return filename; + } + + reportMessage(event: CompilerEvent): void { + // console.log(event.message); + this.messages.push(event); + } + + loadSchema(schema: CompilerSchema): Buffer { + return fs.readFileSync(new URL(SCHEMA_BASE + schema + '.schema.json', import.meta.url)); + } + + debug(msg: string) { + console.debug(msg); + } +}; \ No newline at end of file diff --git a/developer/src/common/web/test-helpers/package.json b/developer/src/common/web/test-helpers/package.json new file mode 100644 index 0000000000000000000000000000000000000000..09b5778f3bc4d9f9cf48253508695dc2291b7015 --- /dev/null +++ b/developer/src/common/web/test-helpers/package.json @@ -0,0 +1,23 @@ +{ + "name": "@keymanapp/developer-test-helpers", + "description": "Keyman Developer test helpers", + "type": "module", + "exports": { + ".": "./build/index.js" + }, + "files": [ + "/build/" + ], + "devDependencies": { + "@types/node": "^10.14.6", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + }, +"scripts": { + "build": "tsc -b" + }, + "license": "MIT", + "dependencies": { + "@keymanapp/common-types": "*" + } +} diff --git a/developer/src/common/web/test-helpers/tsconfig.json b/developer/src/common/web/test-helpers/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..47646be3a61554882b8095c8277a7ad496552bd2 --- /dev/null +++ b/developer/src/common/web/test-helpers/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../../tsconfig.esm-base.json", + + "compilerOptions": { + "rootDir": ".", + "outDir": "./build/", + "baseUrl": ".", + "paths": { + "@keymanapp/common-types": ["../../../../../common/web/types/src/main"], + }, + }, + "include": [ + "./*.ts", + ], + "references": [ + { "path": "../../../../../common/web/types/" }, + ] +} \ No newline at end of file diff --git a/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts b/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts new file mode 100644 index 0000000000000000000000000000000000000000..223ab430f3319d58b294be7695c2dacafc4bae5a --- /dev/null +++ b/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts @@ -0,0 +1,66 @@ +import { CompilerErrorSeverity, compilerErrorSeverityName } from '@keymanapp/common-types'; +import {assert, expect} from 'chai'; + +// +// The purpose of these tests is to ensure that our slightly WET constant +// definitions for compiler errors are consistent in their usage: +// +// * that names are consistent between the message functions and constants +// * that constants have unique codes +// * that constants have the right error mask for their type +// +// This means we don't have to worry about DRYing out the constants, which would +// probably require us to have a preprocessing step, which has its own +// negatives. +// +// These are used in each of the test-messages.ts units to verify the +// CompilerMessages classes + +const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); + +export function verifyCompilerMessagesObject(source: Record) { + const keys = Object.keys(source); + + const m = source as Record; + + const codes: number[] = []; + + for(const key of keys) { + + // Verify each object member matches the pattern we expect + + if(typeof m[key] == 'function') { + const o = /^(Info|Hint|Warn|Error|Fatal)_([A-Za-z0-9_]+)$/.exec(key); + expect(o).to.be.instanceOf(Array, `Expected member ${key} to be a valid message function name`); + + const c = o[1].toUpperCase() + '_' + o[2]; + expect(m[c]).to.be.a('number', `Expected constant name ${c} to exist`); + + const v = m[key]('','','','','','','','','','','','' /* ignore arguments*/); + expect(v.code).to.equal(m[c], `Function ${key} returns the wrong code`); + } + else if(typeof m[key] == 'number') { + const o = /^(INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key); + expect(o).to.be.instanceOf(Array); + + const f = toTitleCase(o[1]) + '_' + o[2]; + expect(m[f]).to.be.a('function', `Expected function name ${f} to exist`); + } else { + assert.fail(`Unexepected compiler message member ${key}`); + } + + // Verify severify masks + + if(typeof m[key] == 'number') { + const o = /^(INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key); + expect(o).to.be.instanceOf(Array); + + const mask = compilerErrorSeverityName(m[key]); + expect(o[1]).to.equal(mask, `Mask value for ${key} does not match`); + + const code = m[key] & CompilerErrorSeverity.Error_Mask; + expect(codes).to.not.contain(code, `Constant value ${key} is not unique`); + codes.push(code); + } + } +} diff --git a/developer/src/ext/json-schema-validator/json-validator.cpp b/developer/src/ext/json-schema-validator/json-validator.cpp index 8e8ffaee68c9b6cef878a78e6c4d0003f39368a0..28988f37b54d4e96ca0666f4c2c7dddcd723bc56 100644 --- a/developer/src/ext/json-schema-validator/json-validator.cpp +++ b/developer/src/ext/json-schema-validator/json-validator.cpp @@ -683,8 +683,8 @@ void json_validator::validate_object(const json &instance, const json &schema, c static std::size_t utf8_length(const std::string &s) { size_t len = 0; - for (const unsigned char &c : s) - if ((c & 0xc0) != 0x80) + for (const char &c : s) + if (((unsigned char)c & 0xc0) != 0x80) len++; return len; } diff --git a/developer/src/inst/kmdev.wxs b/developer/src/inst/kmdev.wxs index 0847b93cc92accd6c19a4e2ccb5582d42d4a947f..1c685ff113e8ad0637820563cf76d008c00cf782 100644 --- a/developer/src/inst/kmdev.wxs +++ b/developer/src/inst/kmdev.wxs @@ -55,7 +55,7 @@ - + @@ -211,7 +211,7 @@ - + diff --git a/developer/src/kmc-keyboard/src/main.ts b/developer/src/kmc-keyboard/src/main.ts deleted file mode 100644 index 3bf1c37f1c476e79fab366b0fac0ff592f2e34bb..0000000000000000000000000000000000000000 --- a/developer/src/kmc-keyboard/src/main.ts +++ /dev/null @@ -1,10 +0,0 @@ - -export { default as Compiler } from './compiler/compiler.js'; -export { KeymanWebCompiler } from './compiler/keymanweb-compiler.js'; -export { default as VisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js'; -export { TouchLayoutCompiler } from './compiler/touch-layout-compiler.js'; -export { default as CompilerOptions } from './compiler/compiler-options.js'; -export { CompilerMessages } from './compiler/messages.js'; -export { default as KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js'; - -export { KMXBuilder } from "@keymanapp/common-types"; diff --git a/developer/src/kmc-keyboard/test/test-messages.ts b/developer/src/kmc-keyboard/test/test-messages.ts deleted file mode 100644 index e9d897b5491c06eca566fe63cfa369eec4322253..0000000000000000000000000000000000000000 --- a/developer/src/kmc-keyboard/test/test-messages.ts +++ /dev/null @@ -1,75 +0,0 @@ -import 'mocha'; -import {assert, expect} from 'chai'; -import { CompilerMessages } from '../src/compiler/messages.js'; -import { CompilerErrorSeverity } from '@keymanapp/common-types'; - -const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); - -// -// The purpose of these tests is to ensure that our slightly WET constant -// definitions for compiler errors are consistent in their usage: -// -// * that names are consistent between the message functions and constants -// * that constants have unique codes -// * that constants have the right error mask for their type -// -// This means we don't have to worry about DRYing out the constants, which would -// probably require us to have a preprocessing step, which has its own negatives. -// - -describe('compiler messages', function () { - it('each compiler message function has a corresponding constant', function() { - let keys = Object.keys(CompilerMessages); - - const m = CompilerMessages as Record; - - for(let key of keys) { - if(key == 'severityName') { - // any helper functions we skip here - continue; - } - - if(typeof m[key] == 'function') { - const o = /^(Info|Hint|Warning|Error|Fatal)_([A-Za-z0-9_]+)$/.exec(key); - expect(o).to.be.instanceOf(Array); - - const c = o[1].toUpperCase() + '_' + o[2]; - expect(m[c]).to.be.a('number', `Expected constant name ${c} to exist`); - - let v = m[key]('','','','','','','','','','','','' /* ignore arguments*/); - expect(v.code).to.equal(m[c], `Function ${key} returns the wrong code`); - } - else if(typeof m[key] == 'number') { - const o = /^(INFO|HINT|WARNING|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key); - expect(o).to.be.instanceOf(Array); - - const f = toTitleCase(o[1]) + '_' + o[2]; - expect(m[f]).to.be.a('function', `Expected function name ${f} to exist`); - } else { - assert.fail(`Unexepected compiler message member ${key}`); - } - } - }); - - it('each compiler message constant to be have the right mask and be unique', function() { - let keys = Object.keys(CompilerMessages); - - const m = CompilerMessages as Record; - - let codes: number[] = []; - - for(let key of keys) { - if(typeof m[key] == 'number') { - const o = /^(INFO|HINT|WARNING|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key); - expect(o).to.be.instanceOf(Array); - - const mask = CompilerMessages.severityName(m[key]); - expect(o[1]).to.equal(mask, `Mask value for ${key} does not match`); - - const code = m[key] & CompilerErrorSeverity.Error_Mask; - expect(codes).to.not.contain(code, `Constant value ${key} is not unique`); - codes.push(code); - } - } - }); -}); diff --git a/developer/src/kmc-keyboard/test/test-ordr.ts b/developer/src/kmc-keyboard/test/test-ordr.ts deleted file mode 100644 index d39208ba92692e62a915c3a9046916e2712d0a8f..0000000000000000000000000000000000000000 --- a/developer/src/kmc-keyboard/test/test-ordr.ts +++ /dev/null @@ -1,29 +0,0 @@ -import 'mocha'; -import { assert } from 'chai'; -import { OrdrCompiler } from '../src/compiler/ordr.js'; -import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; -import { KMXPlus } from '@keymanapp/common-types'; - -import Ordr = KMXPlus.Ordr; - -describe('ordr', function () { - this.slow(500); // 0.5 sec -- json schema validation takes a while - - it('should compile minimal ordr data', function() { - let ordr = loadSectionFixture(OrdrCompiler, 'sections/ordr/minimal.xml', compilerTestCallbacks) as Ordr; - assert.lengthOf(compilerTestCallbacks.messages, 0); - - assert.lengthOf(ordr.items, 1); - assert.lengthOf(ordr.items[0].elements, 4); - assert.strictEqual(ordr.items[0].elements[0].value.value, "ខ"); - assert.strictEqual(ordr.items[0].elements[1].value.value, "ែ"); - assert.strictEqual(ordr.items[0].elements[2].value.value, "្"); - assert.strictEqual(ordr.items[0].elements[3].value.value, "ម"); - assert.strictEqual(ordr.items[0].elements[0].order, 1); - assert.strictEqual(ordr.items[0].elements[1].order, 3); - assert.strictEqual(ordr.items[0].elements[2].order, 4); - assert.strictEqual(ordr.items[0].elements[3].order, 2); - assert.isEmpty(ordr.items[0].before); - }); -}); - diff --git a/developer/src/kmc-kmn/.eslintrc.cjs b/developer/src/kmc-kmn/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..01dae6b737e69e50f7ece590cc3fa7716d70ed11 --- /dev/null +++ b/developer/src/kmc-kmn/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + overrides: [ + { + files:"src/**/*.ts", + // extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + } + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc-kmn/.gitignore b/developer/src/kmc-kmn/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6a608d487b85755178ab0c6f1367d719cdd61406 --- /dev/null +++ b/developer/src/kmc-kmn/.gitignore @@ -0,0 +1,3 @@ +# WASM interfaces from kmcmplib are copied here so we can avoid having +# to make tsc aware of debug vs release paths +src/import/ diff --git a/developer/src/kmc-kmn/Makefile b/developer/src/kmc-kmn/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..31ce8fcb9ca46bd8103dcdb74174eb46259a1948 --- /dev/null +++ b/developer/src/kmc-kmn/Makefile @@ -0,0 +1,37 @@ +# +# Keyman Developer - kmc kmn Compiler Makefile +# + +!include ..\Defines.mak + +build: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh build + +configure: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh configure + +clean: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh clean + +test: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh test + +# build.sh bundle must be run from shell as it requires a temp folder to be +# passed in. See inst/download.in.mak for instantiation. + +publish: .virtual + $(GIT_BASH_FOR_KEYMAN) build.sh publish + +signcode: + @rem nothing to do + +wrap-symbols: + @rem nothing to do + +test-manifest: + @rem nothing to do + +install: + @rem nothing to do + +!include ..\Target.mak diff --git a/developer/src/kmc-kmn/build.sh b/developer/src/kmc-kmn/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..a69dec7646607d8ae3d79176e0fe019d08637031 --- /dev/null +++ b/developer/src/kmc-kmn/build.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# +# Compiles the kmc-kmn keyboard compiler. +# + +# Exit on command failure and when using unset variables: +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +cd "$THIS_SCRIPT_PATH" + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +builder_describe "Build Keyman Developer Compiler Module for .kmn to .kmx" \ + "@/common/web/keyman-version" \ + "@/common/web/types" \ + "@/developer/src/common/web/test-helpers" \ + "@/developer/src/kmcmplib:wasm" \ + "configure" \ + "build" \ + "clean" \ + "test" \ + "pack build a local .tgz pack for testing" \ + "publish publish to npm" \ + "--dry-run,-n don't actually publish, just dry run" + +builder_describe_outputs \ + configure /node_modules \ + build /developer/src/kmc-kmn/build/src/main.js + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action clean; then + rm -rf ./build/ ./tsconfig.tsbuildinfo + builder_finish_action success clean +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action configure; then + verify_npm_setup + builder_finish_action success configure +fi + +#------------------------------------------------------------------------------------------------------------------- + +function copy_deps() { + mkdir -p build/src/import/kmcmplib + mkdir -p src/import/kmcmplib + cp ../kmcmplib/build/wasm/$BUILDER_CONFIGURATION/src/wasm-host.js ./src/import/kmcmplib/wasm-host.js + cp ../kmcmplib/build/wasm/$BUILDER_CONFIGURATION/src/wasm-host.wasm ./build/src/import/kmcmplib/wasm-host.wasm +} + +if builder_start_action build; then + copy_deps + tsc --build + builder_finish_action success build +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action test; then + copy_deps + tsc --build test/ + npm run lint + c8 --reporter=lcov --reporter=text mocha "${builder_extra_params[@]}" + builder_finish_action success test +fi + +#------------------------------------------------------------------------------------------------------------------- + +if builder_start_action publish; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm + builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack +fi diff --git a/developer/src/kmc-kmn/package.json b/developer/src/kmc-kmn/package.json new file mode 100644 index 0000000000000000000000000000000000000000..108ce2564b7c0fda78d11170d0045b25153c8f4d --- /dev/null +++ b/developer/src/kmc-kmn/package.json @@ -0,0 +1,71 @@ +{ + "name": "@keymanapp/kmc-kmn", + "description": "Keyman Developer Compiler Module for .kmn to .kmx", + "keywords": [ + "keyboard", + "keyman", + "kmn", + "kmx", + "unicode" + ], + "type": "module", + "exports": { + ".": "./build/src/main.js" + }, + "files": [ + "/build/src/" + ], + "scripts": { + "build": "tsc -b", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "prepublishOnly": "npm run build" + }, + "author": "Marc Durdin (https://github.com/mcdurdin)", + "license": "MIT", + "bugs": { + "url": "https://github.com/keymanapp/keyman/issues" + }, + "dependencies": { + "@keymanapp/keyman-version": "*" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.6", + "@types/semver": "^7.3.12", + "@types/sinon": "^10.0.13", + "@types/sinon-chai": "^3.2.9", + "@types/xml2js": "^0.4.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "sinon": "^15.0.2", + "sinon-chai": "^3.7.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5", + "@keymanapp/developer-test-helpers": "*" + }, + "mocha": { + "spec": "build/test/**/test-*.js", + "require": [ + "source-map-support/register" + ] + }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "src/import/", + "test/" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/keymanapp/keyman.git" + } +} diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts new file mode 100644 index 0000000000000000000000000000000000000000..c46b894cb3caa3be6ee6cb430f9befc671468cac --- /dev/null +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -0,0 +1,299 @@ +/* +TODO: implement additional interfaces: + + typedef bool (*kmcmp_ValidateJsonMessageProc)(); + extern "C" bool kmcmp_ValidateJsonFile(); +*/ + +// TODO: rename wasm-host? +import { CompilerCallbacks, CompilerEvent, KvkFileWriter, KvksFileReader } from '@keymanapp/common-types'; +import loadWasmHost from '../import/kmcmplib/wasm-host.js'; +import { CompilerMessages, mapErrorFromKmcmplib } from './messages.js'; + +export interface CompilerResultFile { + filename: string; + data: Uint8Array; +}; + +export interface CompilerResult { + kmx?: CompilerResultFile; + kvk?: CompilerResultFile; + js?: CompilerResultFile; +}; + +export interface CompilerOptions { + shouldAddCompilerVersion?: boolean; + saveDebug?: boolean; + compilerWarningsAsErrors?: boolean; + warnDeprecatedCode?: boolean; +}; + +const baseOptions: CompilerOptions = { + shouldAddCompilerVersion: true, + saveDebug: true, + compilerWarningsAsErrors: false, + warnDeprecatedCode: true +}; + +/** + * Allows multiple instances of the Compiler class, by ensuring that the + * 'unique' kmnCompilerCallback global will be correlated with a specific + * instance of the Compiler class + */ +let callbackProcIdentifier = 0; + +/** + * Pointer in wasm-space + */ +type WasmPtr = number; + +/** + * The wrapped functions + */ +class WasmWrapper { + Module: any; + + compileKeyboardFile?: (pszInfile: string, pszOutfile: string, aSaveDebug: number, aCompilerWarningsAsErrors: number, aWarnDeprecatedCode: number, msgProc: string) => boolean; + parseUnicodeSet?: (pat: string, buf: WasmPtr, length: number) => number; + setCompilerOptions?: (shouldAddCompilerVersion: number) => boolean; + + constructor(wasmModule: any) { + this.Module = wasmModule; + if (!wasmModule) { + throw Error(`wasm host did not load`); + } + this.compileKeyboardFile = this.Module.cwrap('kmcmp_Wasm_CompileKeyboardFile', 'boolean', ['string', 'string', 'number', 'number', 'number', 'string']); + this.parseUnicodeSet = this.Module.cwrap('kmcmp_Wasm_ParseUnicodeSet', 'number', ['string', 'number', 'number']); + this.setCompilerOptions = this.Module.cwrap('kmcmp_Wasm_SetCompilerOptions', 'boolean', ['number']); + + if (this.parseUnicodeSet === undefined + || this.setCompilerOptions === undefined + || this.compileKeyboardFile === undefined) { + throw Error(`some wasm functions did not load properly.`); + } + } + + /** + * Entry point into Wasm functions + * @returns WasmWrapper + */ + public static async load() : Promise { + return new WasmWrapper(await loadWasmHost()); + } +}; + +export class KmnCompiler { + callbackName: string; + callbacks: CompilerCallbacks; + wasm: WasmWrapper; + + constructor() { + this.callbackName = 'kmnCompilerCallback' + callbackProcIdentifier; + callbackProcIdentifier++; + } + + public async init(callbacks: CompilerCallbacks): Promise { + this.callbacks = callbacks; + if(!this.wasm) { + try { + this.wasm = await WasmWrapper.load(); + } catch(e: any) { + this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({e})); + return false; + } + } + return this.verifyInitialized(); + } + + /** + * Verify that wasm is spun up OK. + * @returns true if OK + */ + public verifyInitialized() : boolean { + if(!this.callbacks) { + // Can't report a message here. + throw Error('Must call Compiler.init(callbacks) before proceeding'); + } + if(!this.wasm) { // fail if wasm not loaded or function not found + this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({})); + return false; + } + return true; + } + + public run(infile: string, outfile: string, options?: CompilerOptions): boolean { + if(!this.verifyInitialized()) { + return false; + } + + options = {...baseOptions, ...options}; + (globalThis as any)[this.callbackName] = this.compilerMessageCallback; + // TODO: use callbacks for file access -- so kmc-kmn is entirely fs agnostic + let result = this.runCompiler(infile, outfile, options); + delete (globalThis as any)[this.callbackName]; + //TODO: write the file out! + if(result) { + if(result.kmx) { + this.callbacks.fs.writeFileSync(result.kmx.filename, result.kmx.data); + } + if(result.kvk) { + this.callbacks.fs.writeFileSync(result.kvk.filename, result.kvk.data); + } + if(result.js) { + this.callbacks.fs.writeFileSync(result.js.filename, result.js.data); + } + } + return !!result; + } + + private compilerMessageCallback = (line: number, code: number, msg: string): number => { + this.callbacks.reportMessage(mapErrorFromKmcmplib(line, code, msg)); + return 1; + } + + private runCompiler(infile: string, outfile: string, options: CompilerOptions): CompilerResult { + let result: CompilerResult = {}; + let wasm_interface = new this.wasm.Module.CompilerInterface(); + let wasm_result = null; + try { + wasm_interface.saveDebug = options.saveDebug; + wasm_interface.compilerWarningsAsErrors = options.compilerWarningsAsErrors; + wasm_interface.warnDeprecatedCode = options.warnDeprecatedCode; + wasm_interface.messageCallback = this.callbackName; + wasm_interface.loadFileCallback = this.callbackName; // TODO: this is wrong, needs to be a new callback; not yet used though + wasm_result = this.wasm.Module.kmcmp_compile(infile, wasm_interface); + if(!wasm_result.result) { + return null; + } + + if(wasm_result.kvksFilename) { + result.kvk = this.runKvkCompiler(wasm_result.kvksFilename, infile, outfile); + if(!result.kvk) { + return null; + } + } + + result.kmx = { + filename: outfile, + data: new Uint8Array(this.wasm.Module.HEAP8.buffer, wasm_result.kmx, wasm_result.kmxSize) + }; + + return result; + } catch(e) { + this.callbacks.reportMessage(CompilerMessages.Fatal_UnexpectedException({e:e})); + return null; + } finally { + if(wasm_result) { + wasm_result.delete(); + } + wasm_interface.delete(); + } + } + + private runKvkCompiler(kvksFilename: string, kmnFilename: string, kmxFilename: string) { + // The compiler detected a .kvks file, which needs to be captured + let reader = new KvksFileReader(); + kvksFilename = this.callbacks.resolveFilename(kmnFilename, kvksFilename); + let kvks = reader.read(this.callbacks.loadFile(kvksFilename)); + try { + reader.validate(kvks, this.callbacks.loadSchema('kvks')); + } catch(e) { + console.log(e); + // TODO: this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({e})); + return null; + } + let errors: any = []; //TODO: KVKSParseError[]; + let vk = reader.transform(kvks, errors); + if(!vk || errors.length) { + console.dir(errors); + // TODO: this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({e})); + return null; + } + let writer = new KvkFileWriter(); + return { + filename: this.callbacks.path.join(this.callbacks.path.dirname(kmxFilename), this.callbacks.path.basename(kvksFilename, '.kvks') + '.kvk'), + data: writer.write(vk) + }; + } + + /** + * + * @param pattern UnicodeSet pattern such as `[a-z]` + * @param bufferSize guess as to the buffer size + * @returns UnicodeSet accessor object, or null on failure + */ + public parseUnicodeSet(pattern: string, bufferSize: number) : UnicodeSet | null { + if(!this.verifyInitialized()) { + return null; + } + + if (!bufferSize) { + bufferSize = 100; // TODO-LDML: Preflight mode? Reuse buffer? + } + const { Module } = this.wasm; + const buf = Module.asm.malloc(bufferSize * 2 * Module.HEAPU32.BYTES_PER_ELEMENT); + // TODO-LDML: Catch OOM + const rc = this.wasm.parseUnicodeSet(pattern, buf, bufferSize); + if (rc >= 0) { + const ranges = []; + const startu = (buf / Module.HEAPU32.BYTES_PER_ELEMENT); + for (let i = 0; i < rc; i++) { + const low = Module.HEAPU32[startu + (i * 2) + 0]; + const high = Module.HEAPU32[startu + (i * 2) + 1]; + ranges.push([low, high]); + } + // TODO-LDML: no free?? + // Module.asm.free(buf); + return new UnicodeSet(pattern, ranges); + } else { + // translate error + // TODO-LDML: no free?? + // Module.asm.free(buf); + this.callbacks.reportMessage(getUnicodeSetError(rc)); + return null; + } + } +} + +/** + * translate UnicodeSet return code into a compiler event + * @param rc parseUnicodeSet error code + * @returns the compiler event + */ +function getUnicodeSetError(rc: number) : CompilerEvent { + // from kmcmplib.h + const KMCMP_ERROR_SYNTAX_ERR = -1; + const KMCMP_ERROR_HAS_STRINGS = -2; + const KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3; + const KMCMP_FATAL_OUT_OF_RANGE = -4; + switch(rc) { + case KMCMP_ERROR_SYNTAX_ERR: + return CompilerMessages.Error_UnicodeSetSyntaxError(); + case KMCMP_ERROR_HAS_STRINGS: + return CompilerMessages.Error_UnicodeSetHasStrings(); + case KMCMP_ERROR_UNSUPPORTED_PROPERTY: + return CompilerMessages.Error_UnicodeSetHasProperties(); + case KMCMP_FATAL_OUT_OF_RANGE: + return CompilerMessages.Fatal_UnicodeSetOutOfRange(); + default: + return CompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`}); + } +} + +/** + * Represents a parsed UnicodeSet + */ +export class UnicodeSet { + constructor(public pattern: string, public ranges: number[][]) { + } + /** + * Number of ranges + */ + get length() : number { + return this.ranges.length; + } + + toString() : string { + return this.pattern; + } +} diff --git a/developer/src/kmc-kmn/src/compiler/messages.ts b/developer/src/kmc-kmn/src/compiler/messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..10578870d7c1499350936abe16981a503cc8167a --- /dev/null +++ b/developer/src/kmc-kmn/src/compiler/messages.ts @@ -0,0 +1,81 @@ +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, compilerExceptionToString as exc } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.KmnCompiler; +const SevInfo = CompilerErrorSeverity.Info | Namespace; +const SevHint = CompilerErrorSeverity.Hint | Namespace; +const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +/** + * LogLevel comes from kmn_compiler_errors.h, for legacy compiler error messages + */ +const enum LogLevel { + LEVEL_MASK = 0xF000, + CODE_MASK = 0x0FFF, + CERR_FATAL = 0x8000, + CERR_ERROR = 0x4000, + CERR_WARNING = 0x2000, + CERR_HINT = 0x1000, + CERR_INFO = 0 +}; + +/** + * Translate the legacy compiler error messages to Severity codes + */ +const LogLevelToSeverity: Record = { + [LogLevel.CERR_FATAL]: SevFatal, + [LogLevel.CERR_ERROR]: SevError, + [LogLevel.CERR_WARNING]: SevWarn, + [LogLevel.CERR_HINT]: SevHint, + [LogLevel.CERR_INFO]: SevInfo +} + +export const enum KmnCompilerMessageRanges { + RANGE_KMN_COMPILER_MIN = 0x0001, // from kmn_compiler_errors.h + RANGE_KMN_COMPILER_MAX = 0x07FF, // from kmn_compiler_errors.h + RANGE_LEXICAL_MODEL_MIN = 0x0800, // from kmn_compiler_errors.h, deprecated -- this range will not be used in future versions + RANGE_LEXICAL_MODEL_MAX = 0x08FF, // from kmn_compiler_errors.h, deprecated -- this range will not be used in future versions + RANGE_CompilerMessage_Min = 0x1000, // All compiler messages listed here must be >= this value +} + +/* + The messages in this class share the namespace with messages from kmn_compiler_errors.h + and the below ranges are reserved. +*/ +export class CompilerMessages { + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${exc(o.e)}`); + static FATAL_UnexpectedException = SevFatal | 0x1000; + + static Fatal_MissingWasmModule = (o:{e?: any}) => m(this.FATAL_MissingWasmModule, `Could not instantiate WASM compiler module or initialization failed: ${exc(o.e)}`); + static FATAL_MissingWasmModule = SevFatal | 0x1001; + + static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, `Unable to set compiler options`); + static FATAL_UnableToSetCompilerOptions = SevFatal | 0x1002; + + static Fatal_CallbacksNotSet = () => m(this.FATAL_CallbacksNotSet, `Callbacks were not set with init`); + static FATAL_CallbacksNotSet = SevFatal | 0x1003; + + static Fatal_UnicodeSetOutOfRange = () => m(this.FATAL_UnicodeSetOutOfRange, `UnicodeSet buffer was too small`); + static FATAL_UnicodeSetOutOfRange = SevFatal | 0x1004; + + static Error_UnicodeSetHasStrings = () => m(this.ERROR_UnicodeSetHasStrings, `UnicodeSet contains strings, not allowed`); + static ERROR_UnicodeSetHasStrings = SevError | 0x1005; + + static Error_UnicodeSetHasProperties = () => m(this.ERROR_UnicodeSetHasProperties, `UnicodeSet contains properties, not allowed`); + static ERROR_UnicodeSetHasProperties = SevError | 0x1006; + + static Error_UnicodeSetSyntaxError = () => m(this.ERROR_UnicodeSetSyntaxError, `UnicodeSet had a Syntax Error while parsing`); + static ERROR_UnicodeSetSyntaxError = SevError | 0x1007; +} + +export function mapErrorFromKmcmplib(line: number, code: number, msg: string): CompilerEvent { + const severity = LogLevelToSeverity[code & LogLevel.LEVEL_MASK]; + const baseCode = code & LogLevel.CODE_MASK; + const event: CompilerEvent = { + line: line, + code: severity | CompilerErrorNamespace.KmnCompiler | baseCode, + message: msg + }; + return event; +}; diff --git a/developer/src/kmc-kmn/src/main.ts b/developer/src/kmc-kmn/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..70d11989c6bfca468558bb217b42c628cf7017de --- /dev/null +++ b/developer/src/kmc-kmn/src/main.ts @@ -0,0 +1,2 @@ + +export { KmnCompiler } from './compiler/compiler.js'; diff --git a/developer/src/kmc-kmn/test/README.md b/developer/src/kmc-kmn/test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..96a198a0d33cfceb2154296d90b246744ea64107 --- /dev/null +++ b/developer/src/kmc-kmn/test/README.md @@ -0,0 +1,7 @@ +Keyman kmn Keyboard Compiler Tests +=================================== + +Test +---- + + ../build.sh test diff --git a/developer/src/kmc-kmn/test/test-compiler.ts b/developer/src/kmc-kmn/test/test-compiler.ts new file mode 100644 index 0000000000000000000000000000000000000000..925656f8f554e938141b13bbb35f4f7de817ff76 --- /dev/null +++ b/developer/src/kmc-kmn/test/test-compiler.ts @@ -0,0 +1,108 @@ +import 'mocha'; +import { assert } from 'chai'; +import { KmnCompiler } from '../src/main.js'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import fs from 'fs'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; + +const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/'); +const keyboardsDir = __dirname + '/../../../../../common/test/keyboards/'; +const baselineDir = keyboardsDir + 'baseline/'; + +describe('Compiler class', function() { + it('should throw on failure', async function() { + const compiler = new KmnCompiler(); + const callbacks : any = null; // ERROR + try { + await compiler.init(callbacks) + assert.fail('Expected exception'); + } catch(e) { + assert.ok(e); + } + assert.throws(() => compiler.verifyInitialized()); + }); + + it('should start', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + }); + + it('should compile a basic keyboard', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const fixtureName = baselineDir + 'k_000___null_keyboard.kmx'; + const infile = baselineDir + 'k_000___null_keyboard.kmn'; + const outfile = __dirname + '/k_000___null_keyboard.kmx'; + + assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); + + assert(fs.existsSync(outfile)); + const outfileData = fs.readFileSync(outfile); + const fixtureData = fs.readFileSync(fixtureName); + assert.equal(outfileData.byteLength, fixtureData.byteLength); + assert.deepEqual(outfileData, fixtureData); + }); + + // Note, above test case is essentially a subset of this one, but will leave both because + // the basic keyboard test is slightly simpler to read + it('should build all baseline fixtures', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const files = fs.readdirSync(baselineDir); + for(let file of files) { + if(file.match(/\.kmx$/)) { + const fixtureName = baselineDir + file; + const infile = baselineDir + file.replace(/x$/, 'n'); + const outfile = __dirname + '/' + file; + + assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); + + assert(fs.existsSync(outfile)); + const outfileData = fs.readFileSync(outfile); + const fixtureData = fs.readFileSync(fixtureName); + assert.equal(outfileData.byteLength, fixtureData.byteLength); + assert.deepEqual(outfileData, fixtureData); + } + } + }); + + it('should compile a keyboard with visual keyboard', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const fixtureDir = keyboardsDir + 'caps_lock_layer_3620/' + const infile = fixtureDir + 'source/caps_lock_layer_3620.kmn'; + const kmxFixture = fixtureDir + '/binary/caps_lock_layer_3620.kmx'; + const kvkFixture = fixtureDir + '/binary/caps_lock_layer_3620.kvk'; + + const kmxfile = __dirname + '/caps_lock_layer_3620.kmx'; + const kvkfile = __dirname + '/caps_lock_layer_3620.kvk'; + + assert(compiler.run(infile, kmxfile, {saveDebug: true, shouldAddCompilerVersion: false})); + + assert(fs.existsSync(kmxfile)); + assert(fs.existsSync(kvkfile)); + + const kmxData = fs.readFileSync(kmxfile); + const kmxFixtureData = fs.readFileSync(kmxFixture); + assert.equal(kmxData.byteLength, kmxFixtureData.byteLength); + assert.deepEqual(kmxData, kmxFixtureData); + + const kvkData = fs.readFileSync(kvkfile); + const kvkFixtureData = fs.readFileSync(kvkFixture); + assert.equal(kvkData.byteLength, kvkFixtureData.byteLength); + assert.deepEqual(kvkData, kvkFixtureData); + }); + +}); diff --git a/developer/src/kmc-kmn/test/test-messages.ts b/developer/src/kmc-kmn/test/test-messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..0dee53f12112d47b67ca19bc266630a791dd4bf9 --- /dev/null +++ b/developer/src/kmc-kmn/test/test-messages.ts @@ -0,0 +1,9 @@ +import 'mocha'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; + +describe('CompilerMessages', function () { + it('should have a valid CompilerMessages object', function() { + return verifyCompilerMessagesObject(CompilerMessages); + }); +}); diff --git a/developer/src/kmc-kmn/test/test-wasm-uset.ts b/developer/src/kmc-kmn/test/test-wasm-uset.ts new file mode 100644 index 0000000000000000000000000000000000000000..a63b408a959dfbe410703b3f73b28df5ce915bb4 --- /dev/null +++ b/developer/src/kmc-kmn/test/test-wasm-uset.ts @@ -0,0 +1,66 @@ +import 'mocha'; +import { assert } from 'chai'; +import { KmnCompiler } from '../src/main.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { compilerErrorFormatCode } from '@keymanapp/common-types'; + +describe('Compiler UnicodeSet function', function() { + it('should start', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + }); + + it('should compile a basic uset', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const pat = "[abc]"; + const set = compiler.parseUnicodeSet(pat, 23); + + assert(set.length === 1); + assert(set.ranges[0][0] === 'a'.charCodeAt(0)); + assert(set.ranges[0][1] === 'c'.charCodeAt(0)); + }); + it('should compile a more complex uset', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const pat = "[[🙀A-C]-[CB]]"; + const set = compiler.parseUnicodeSet(pat, 23); + + assert.equal(set.length, 2); + assert.equal(set.ranges[0][0], 'A'.charCodeAt(0)); + assert.equal(set.ranges[0][1], 'A'.charCodeAt(0)); + assert.equal(set.ranges[1][0], 0x1F640); + assert.equal(set.ranges[1][1], 0x1F640); + }); + it('should fail in various ways', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + // map from string to failing error + const failures = { + '[:Adlm:]': CompilerMessages.ERROR_UnicodeSetHasProperties, // what it saye + '[acegik]': CompilerMessages.FATAL_UnicodeSetOutOfRange, // 6 ranges, allocated 1 + '[[\\p{Mn}]&[A-Z]]': CompilerMessages.ERROR_UnicodeSetHasProperties, + '[abc{def}]': CompilerMessages.ERROR_UnicodeSetHasStrings, + '[[]': CompilerMessages.ERROR_UnicodeSetSyntaxError, + }; + for(const [pat, expected] of Object.entries(failures)) { + callbacks.clear(); + assert.notOk(compiler.parseUnicodeSet(pat, 1)); + assert.equal(callbacks.messages.length, 1); + const firstMessage = callbacks.messages[0]; + const code = firstMessage.code; + assert.equal(code, expected, `${compilerErrorFormatCode(code)}≠${compilerErrorFormatCode(expected)} got ${firstMessage.message} for ${pat}`); + } + }); +}); diff --git a/developer/src/kmc-kmn/test/tsconfig.json b/developer/src/kmc-kmn/test/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..115412f57f397294d7bb8336dfed238cd9b3040c --- /dev/null +++ b/developer/src/kmc-kmn/test/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../kmc/tsconfig.kmc-base.json", + + "compilerOptions": { + "rootDir": ".", + "rootDirs": ["./", "../src/"], + "outDir": "../build/test", + "baseUrl": ".", + "allowSyntheticDefaultImports": true, // for chai + "paths": { + "@keymanapp/common-types": ["../../../../common/web/types/src/main"], + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], + }, + }, + "include": [ + "**/test-*.ts" + ], + "references": [ + { "path": "../../../../common/web/keyman-version/tsconfig.esm.json" }, + { "path": "../../../../common/web/types/" }, + { "path": "../../../../common/tools/hextobin/" }, + { "path": "../../common/web/test-helpers/" }, + { "path": "../" } + ] +} \ No newline at end of file diff --git a/developer/src/kmc-kmn/tsconfig.json b/developer/src/kmc-kmn/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..b828a5076487c095a83cfaf700f73801f44d758b --- /dev/null +++ b/developer/src/kmc-kmn/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.esm-base.json", + + "compilerOptions": { + "outDir": "build/src/", + "rootDir": "src/", + "baseUrl": ".", + "allowJs": true, + "paths": { + "@keymanapp/common-types": ["../../../common/web/types/src/main"], + }, + + }, + "include": [ + "src/**/*.ts", + "src/import/kmcmplib/wasm-host.js" + ], + "references": [ + { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, + { "path": "../../../common/web/types/" }, + ] +} diff --git a/developer/src/kmc-ldml/.eslintrc.cjs b/developer/src/kmc-ldml/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..09038ae9291d0ce786656be735b38cff1049976d --- /dev/null +++ b/developer/src/kmc-ldml/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + ignorePatterns: ["test/fixtures/**/*"], + overrides: [ + { + files:"src/**/*.ts", + extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + } + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc-keyboard/Makefile b/developer/src/kmc-ldml/Makefile similarity index 100% rename from developer/src/kmc-keyboard/Makefile rename to developer/src/kmc-ldml/Makefile diff --git a/developer/src/kmc-keyboard/build.sh b/developer/src/kmc-ldml/build.sh similarity index 85% rename from developer/src/kmc-keyboard/build.sh rename to developer/src/kmc-ldml/build.sh index e6ac49b35dacaf06bc73eb9e981d9b09d2fc5eb5..71f146e835adc94adac256997240c25f4a21317b 100755 --- a/developer/src/kmc-keyboard/build.sh +++ b/developer/src/kmc-ldml/build.sh @@ -19,16 +19,18 @@ cd "$THIS_SCRIPT_PATH" builder_describe "Build Keyman kmc Keyboard Compiler module" \ "@/common/web/keyman-version" \ "@/common/web/types" \ + "@/developer/src/common/web/test-helpers" \ "configure" \ "build" \ "clean" \ "test" \ "build-fixtures builds test fixtures for manual examination" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ configure /node_modules \ - build /developer/src/kmc-keyboard/build/src/main.js + build /developer/src/kmc-ldml/build/src/main.js builder_parse "$@" @@ -77,7 +79,7 @@ if builder_start_action build-fixtures; then # Build basic.kmx and emit its checksum mkdir -p ./build/test/fixtures - node ../kmc ./test/fixtures/basic.xml --no-compiler-version --debug --out-file ./build/test/fixtures/basic-xml.kmx + node ../kmc build ./test/fixtures/basic.xml --no-compiler-version --debug --out-file ./build/test/fixtures/basic-xml.kmx printf "${COLOR_GREY}Checksum for basic-xml.kmx: ${COLOR_PURPLE}%s${COLOR_RESET}\n" \ "$(xxd -g 1 -l 12 ./build/test/fixtures/basic-xml.kmx | cut -d' ' -f 10-13)" @@ -99,7 +101,12 @@ fi if builder_start_action publish; then copy_schemas - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm builder_finish_action success publish +elif builder_start_action pack; then + copy_schemas + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/developer/src/kmc-keyboard/package.json b/developer/src/kmc-ldml/package.json similarity index 71% rename from developer/src/kmc-keyboard/package.json rename to developer/src/kmc-ldml/package.json index 92e99f31b2f20bcdbceda7e6f67d3167cd4d0a66..fda0b44272aa05a36300be18b6e3fa9a5877a790 100644 --- a/developer/src/kmc-keyboard/package.json +++ b/developer/src/kmc-ldml/package.json @@ -1,6 +1,6 @@ { - "name": "@keymanapp/kmc-keyboard", - "description": "Keyman Developer keyboard compiler", + "name": "@keymanapp/kmc-ldml", + "description": "Keyman Developer LDML keyboard compiler", "keywords": [ "keyboard", "keyman", @@ -11,9 +11,13 @@ "exports": { ".": "./build/src/main.js" }, + "files": [ + "/build/src/" + ], "scripts": { "build": "tsc -b", - "test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", "prepublishOnly": "npm run build" }, "author": "Marc Durdin (https://github.com/mcdurdin)", @@ -23,6 +27,7 @@ }, "dependencies": { "@keymanapp/keyman-version": "*", + "@keymanapp/ldml-keyboard-constants": "*", "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50", "semver": "^7.3.7", @@ -39,7 +44,8 @@ "chalk": "^2.4.2", "mocha": "^8.4.0", "ts-node": "^9.1.1", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "@keymanapp/developer-test-helpers": "*" }, "mocha": { "spec": "build/test/**/test-*.js", @@ -47,6 +53,16 @@ "source-map-support/register" ] }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "test/" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/keymanapp/keyman.git" diff --git a/developer/src/kmc-keyboard/src/compiler/compiler-options.ts b/developer/src/kmc-ldml/src/compiler/compiler-options.ts similarity index 83% rename from developer/src/kmc-keyboard/src/compiler/compiler-options.ts rename to developer/src/kmc-ldml/src/compiler/compiler-options.ts index d36322513bf1d5057cc5285875ce97c5728e4ef6..3d29e6393ad3de65cca0db87f464c8cc4f91c8f1 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler-options.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler-options.ts @@ -1,6 +1,6 @@ -export default interface CompilerOptions { +export interface CompilerOptions { /** * Add debug information to the .kmx file when compiling */ diff --git a/developer/src/kmc-keyboard/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts similarity index 93% rename from developer/src/kmc-keyboard/src/compiler/compiler.ts rename to developer/src/kmc-ldml/src/compiler/compiler.ts index 7d6a8142b4979a7515098efcbe6d57e1ccdd28f0..b7b46e49b2a2e9304f0b2616ff9d22eab6ab0268 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler.ts @@ -1,5 +1,5 @@ import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; -import CompilerOptions from './compiler-options.js'; +import { CompilerOptions } from './compiler-options.js'; import { CompilerMessages } from './messages.js'; import { BkspCompiler, FinlCompiler, TranCompiler } from './tran.js'; import { DispCompiler } from './disp.js'; @@ -28,7 +28,7 @@ const SECTION_COMPILERS = [ VkeyCompiler, ]; -export default class Compiler { +export class LdmlKeyboardCompiler { private readonly callbacks: CompilerCallbacks; // private readonly options: CompilerOptions; // not currently used @@ -55,7 +55,7 @@ export default class Compiler { */ public load(filename: string): LDMLKeyboardXMLSourceFile | null { const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks); - const data = this.callbacks.loadFile(filename, filename); + const data = this.callbacks.loadFile(filename); if(!data) { this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'})); return null; @@ -66,7 +66,7 @@ export default class Compiler { return null; } try { - if (!reader.validate(source, this.callbacks.loadLdmlKeyboardSchema())) { + if (!reader.validate(source, this.callbacks.loadSchema('ldml-keyboard'))) { return null; } } catch(e) { @@ -85,7 +85,7 @@ export default class Compiler { */ public loadTestData(filename: string): LDMLKeyboardTestDataXMLSourceFile | null { const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks); - const data = this.callbacks.loadFile(filename, filename); + const data = this.callbacks.loadFile(filename); if(!data) { this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'})); return null; @@ -98,7 +98,7 @@ export default class Compiler { // TODO-LDML: The unboxed data doesn't match the schema anymore. Skipping validation, for now. // try { - // if (!reader.validate(source, this.callbacks.loadLdmlKeyboardTestSchema())) { + // if (!reader.validate(source, this.callbacks.loadSchema('ldml-keyboard-test'))) { // return null; // } // } catch(e) { diff --git a/developer/src/kmc-keyboard/src/compiler/disp.ts b/developer/src/kmc-ldml/src/compiler/disp.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/disp.ts rename to developer/src/kmc-ldml/src/compiler/disp.ts diff --git a/developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts b/developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts similarity index 85% rename from developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts rename to developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts index ded8a4f7cd64319173bf83aa8387c2aa6fc54e0b..7ecdbe5937dabcc013fc44af1f16bef71054188a 100644 --- a/developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts @@ -1,21 +1,19 @@ -import { VisualKeyboard, LDMLKeyboard, TouchLayoutFileWriter } from "@keymanapp/common-types"; - -import * as path from 'path'; -import CompilerOptions from "./compiler-options.js"; +import { CompilerCallbacks, VisualKeyboard, LDMLKeyboard, TouchLayoutFileWriter } from "@keymanapp/common-types"; +import { CompilerOptions } from "./compiler-options.js"; import { TouchLayoutCompiler } from "./touch-layout-compiler.js"; -import VisualKeyboardCompiler from "./visual-keyboard-compiler.js"; +import { LdmlKeyboardVisualKeyboardCompiler } from "./visual-keyboard-compiler.js"; const MINIMUM_KMW_VERSION = '16.0'; -export interface KeymanWebCompilerOptions extends CompilerOptions { +export interface LdmlKeyboardKeymanWebCompilerOptions extends CompilerOptions { }; -export class KeymanWebCompiler { - private readonly options: KeymanWebCompilerOptions; +export class LdmlKeyboardKeymanWebCompiler { + private readonly options: LdmlKeyboardKeymanWebCompilerOptions; private readonly nl: string; private readonly tab: string; - constructor(options?: KeymanWebCompilerOptions) { + constructor(private callbacks: CompilerCallbacks, options?: LdmlKeyboardKeymanWebCompilerOptions) { this.options = { ...options }; this.nl = this.options.debug ? "\n" : ''; this.tab = this.options.debug ? " " : ''; @@ -23,7 +21,7 @@ export class KeymanWebCompiler { public compileVisualKeyboard(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile) { const nl = this.nl, tab = this.tab; - const vkc = new VisualKeyboardCompiler(); + const vkc = new LdmlKeyboardVisualKeyboardCompiler(); const vk: VisualKeyboard.VisualKeyboard = vkc.compile(source); let result = @@ -45,10 +43,11 @@ export class KeymanWebCompiler { } private cleanName(name: string): string { - let result = path.basename(name, '.xml').toLowerCase(); - if(!result.length) { + let result = this.callbacks.path.basename(name, '.xml'); + if(!result) { throw new Error(`Invalid file name ${name}`); } + result = result.replaceAll(/[^a-z0-9]/g, '_'); if(result.match(/^[0-9]/)) { // Can't have a digit as initial diff --git a/developer/src/kmc-keyboard/src/compiler/keys.ts b/developer/src/kmc-ldml/src/compiler/keys.ts similarity index 97% rename from developer/src/kmc-keyboard/src/compiler/keys.ts rename to developer/src/kmc-ldml/src/compiler/keys.ts index 5eeaadcbad0b422c07b517245d17dbf23eb8312c..fab1d8b28ab23391bebb4672b3be1b937bc66ed3 100644 --- a/developer/src/kmc-keyboard/src/compiler/keys.ts +++ b/developer/src/kmc-ldml/src/compiler/keys.ts @@ -173,7 +173,8 @@ export class KeysCompiler extends SectionCompiler { const keymap = Constants.HardwareToKeymap.get(hardware); if (!keymap) { - throw Error(`TODO-LDML: ${hardware} not supported, see #8161`); + this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({ form: hardware })); + valid = false; } const uniqueKeys = calculateUniqueKeys([...this.keyboard.keys?.key]); @@ -186,7 +187,7 @@ export class KeysCompiler extends SectionCompiler { const keys = layer.row[y].keys.split(' '); if (keys.length > keymap[y].length) { - this.callbacks.reportMessage(CompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({ row: y + 1, hardware })); + this.callbacks.reportMessage(CompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({ row: y + 1, hardware, modifier })); valid = false; } diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-ldml/src/compiler/layr.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/layr.ts rename to developer/src/kmc-ldml/src/compiler/layr.ts diff --git a/developer/src/kmc-keyboard/src/compiler/loca.ts b/developer/src/kmc-ldml/src/compiler/loca.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/loca.ts rename to developer/src/kmc-ldml/src/compiler/loca.ts diff --git a/developer/src/kmc-keyboard/src/compiler/messages.ts b/developer/src/kmc-ldml/src/compiler/messages.ts similarity index 87% rename from developer/src/kmc-keyboard/src/compiler/messages.ts rename to developer/src/kmc-ldml/src/compiler/messages.ts index 202a5bfa4295fd81fc8dd04c430c950d12fa1342..32eb943a683ee6ef1995932b685e6f9a467f3669 100644 --- a/developer/src/kmc-keyboard/src/compiler/messages.ts +++ b/developer/src/kmc-ldml/src/compiler/messages.ts @@ -1,10 +1,10 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; -const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.KeyboardCompiler; -const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.KeyboardCompiler; +const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.LdmlKeyboardCompiler; +const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.LdmlKeyboardCompiler; // const SevWarn = CompilerErrorSeverity.Warn | CompilerErrorNamespace.KeyboardCompiler; -const SevError = CompilerErrorSeverity.Error | CompilerErrorNamespace.KeyboardCompiler; -const SevFatal = CompilerErrorSeverity.Fatal | CompilerErrorNamespace.KeyboardCompiler; +const SevError = CompilerErrorSeverity.Error | CompilerErrorNamespace.LdmlKeyboardCompiler; +const SevFatal = CompilerErrorSeverity.Fatal | CompilerErrorNamespace.LdmlKeyboardCompiler; export class CompilerMessages { static Error_InvalidNormalization = (o:{form: string}) => m(this.ERROR_InvalidNormalization, `Invalid normalization form '${o.form}`); @@ -16,7 +16,7 @@ export class CompilerMessages { static Error_HardwareLayerHasTooManyRows = () => m(this.ERROR_HardwareLayerHasTooManyRows, `'hardware' layer has too many rows`); static ERROR_HardwareLayerHasTooManyRows = SevError | 0x0003; - static Error_RowOnHardwareLayerHasTooManyKeys = (o:{row: number, hardware: string}) => m(this.ERROR_RowOnHardwareLayerHasTooManyKeys, `Row #${o.row} on 'hardware' ${o.hardware} layer has too many keys`); + static Error_RowOnHardwareLayerHasTooManyKeys = (o:{row: number, hardware: string, modifier: string}) => m(this.ERROR_RowOnHardwareLayerHasTooManyKeys, `Row #${o.row} on 'hardware' ${o.hardware} layer for modifier ${o.modifier || 'none'} has too many keys`); static ERROR_RowOnHardwareLayerHasTooManyKeys = SevError | 0x0004; static Error_KeyNotFoundInKeyBag = (o:{keyId: string, col: number, row: number, layer: string, form: string}) => @@ -89,18 +89,5 @@ export class CompilerMessages { static Error_MissingFlicks = (o:{flicks: string, id: string}) => m(this.ERROR_MissingFlicks, `key id=${o.id} refers to missing flicks=${o.flicks}`); static ERROR_MissingFlicks = SevError | 0x0015; - - static severityName(code: number): string { - let severity = code & CompilerErrorSeverity.Severity_Mask; - switch(severity) { - case CompilerErrorSeverity.Info: return 'INFO'; - case CompilerErrorSeverity.Hint: return 'HINT'; - case CompilerErrorSeverity.Warn: return 'WARN'; - case CompilerErrorSeverity.Error: return 'ERROR'; - case CompilerErrorSeverity.Fatal: return 'FATAL'; - /* istanbul ignore next */ - default: return 'UNKNOWN'; - } - } } diff --git a/developer/src/kmc-keyboard/src/compiler/meta.ts b/developer/src/kmc-ldml/src/compiler/meta.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/meta.ts rename to developer/src/kmc-ldml/src/compiler/meta.ts diff --git a/developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts b/developer/src/kmc-ldml/src/compiler/metadata-compiler.ts similarity index 94% rename from developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts rename to developer/src/kmc-ldml/src/compiler/metadata-compiler.ts index f459d5fab96651d93051380bfa8ec6cd5ee8efbc..262b86c8019f19d1fdd733a4c73a4901c9eaa793 100644 --- a/developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/metadata-compiler.ts @@ -1,12 +1,12 @@ import { KMX, KMXPlus } from '@keymanapp/common-types'; -import CompilerOptions from "./compiler-options.js"; +import { CompilerOptions } from "./compiler-options.js"; import KEYMAN_VERSION from "@keymanapp/keyman-version"; import KMXPlusData = KMXPlus.KMXPlusData; import KMXFile = KMX.KMXFile; import KEYBOARD = KMX.KEYBOARD; -export default class KMXPlusMetadataCompiler { +export class KMXPlusMetadataCompiler { /** * Look for metadata fields in the KMXPlus data and copy them * through to the relevant KMX stores diff --git a/developer/src/kmc-keyboard/src/compiler/name.ts b/developer/src/kmc-ldml/src/compiler/name.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/name.ts rename to developer/src/kmc-ldml/src/compiler/name.ts diff --git a/developer/src/kmc-keyboard/src/compiler/ordr.ts b/developer/src/kmc-ldml/src/compiler/ordr.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/ordr.ts rename to developer/src/kmc-ldml/src/compiler/ordr.ts diff --git a/developer/src/kmc-keyboard/src/compiler/section-compiler.ts b/developer/src/kmc-ldml/src/compiler/section-compiler.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/section-compiler.ts rename to developer/src/kmc-ldml/src/compiler/section-compiler.ts diff --git a/developer/src/kmc-keyboard/src/compiler/touch-layout-compiler.ts b/developer/src/kmc-ldml/src/compiler/touch-layout-compiler.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/touch-layout-compiler.ts rename to developer/src/kmc-ldml/src/compiler/touch-layout-compiler.ts diff --git a/developer/src/kmc-keyboard/src/compiler/tran.ts b/developer/src/kmc-ldml/src/compiler/tran.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/tran.ts rename to developer/src/kmc-ldml/src/compiler/tran.ts diff --git a/developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts similarity index 97% rename from developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts rename to developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts index b80b590b9cfcb9b2b26673fec14ae67ae0e85f8e..d951080b9b41467553e235db92cede920f2789c1 100644 --- a/developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts @@ -1,6 +1,6 @@ import { Constants, VisualKeyboard, LDMLKeyboard } from "@keymanapp/common-types"; -export default class VisualKeyboardCompiler { +export class LdmlKeyboardVisualKeyboardCompiler { public compile(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile): VisualKeyboard.VisualKeyboard { let result = new VisualKeyboard.VisualKeyboard(); diff --git a/developer/src/kmc-keyboard/src/compiler/vkey.ts b/developer/src/kmc-ldml/src/compiler/vkey.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/vkey.ts rename to developer/src/kmc-ldml/src/compiler/vkey.ts diff --git a/developer/src/kmc-ldml/src/main.ts b/developer/src/kmc-ldml/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d5d2f09e64947c9bf79baa94f6d9eeaa01e0e6e --- /dev/null +++ b/developer/src/kmc-ldml/src/main.ts @@ -0,0 +1,10 @@ + +export { LdmlKeyboardCompiler } from './compiler/compiler.js'; +export { LdmlKeyboardKeymanWebCompiler } from './compiler/keymanweb-compiler.js'; +export { LdmlKeyboardVisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js'; +export { TouchLayoutCompiler } from './compiler/touch-layout-compiler.js'; +export { CompilerOptions } from './compiler/compiler-options.js'; +export { CompilerMessages } from './compiler/messages.js'; +export { KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js'; + +export { KMXBuilder } from "@keymanapp/common-types"; diff --git a/developer/src/kmc-keyboard/src/util/util.ts b/developer/src/kmc-ldml/src/util/util.ts similarity index 100% rename from developer/src/kmc-keyboard/src/util/util.ts rename to developer/src/kmc-ldml/src/util/util.ts diff --git a/developer/src/kmc-keyboard/test/README.md b/developer/src/kmc-ldml/test/README.md similarity index 100% rename from developer/src/kmc-keyboard/test/README.md rename to developer/src/kmc-ldml/test/README.md diff --git a/developer/src/kmc-keyboard/test/fixtures/basic-kvk.txt b/developer/src/kmc-ldml/test/fixtures/basic-kvk.txt similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic-kvk.txt rename to developer/src/kmc-ldml/test/fixtures/basic-kvk.txt diff --git a/developer/src/kmc-keyboard/test/fixtures/basic-no-debug.js b/developer/src/kmc-ldml/test/fixtures/basic-no-debug.js similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic-no-debug.js rename to developer/src/kmc-ldml/test/fixtures/basic-no-debug.js diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.js b/developer/src/kmc-ldml/test/fixtures/basic.js similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic.js rename to developer/src/kmc-ldml/test/fixtures/basic.js diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-ldml/test/fixtures/basic.txt similarity index 74% rename from developer/src/kmc-keyboard/test/fixtures/basic.txt rename to developer/src/kmc-ldml/test/fixtures/basic.txt index 0b9077e38efb256011c257b6c42994c07dd1727f..195066648dd526b2d48342035e9cf4ff514a4540 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-ldml/test/fixtures/basic.txt @@ -95,8 +95,8 @@ block(sect) # struct COMP_KMXPLUS_SECT { # KMX_DWORD offset; // 0014+ Section offset relative to dpKMXPlus of section block(sectitems) - 62 6b 73 70 - diff(sect,bksp) + # 62 6b 73 70 + # diff(sect,bksp) 64 69 73 70 diff(sect,disp) @@ -104,8 +104,8 @@ block(sectitems) 65 6c 65 6d diff(sect,elem) - 66 69 6e 6c - diff(sect,finl) + # 66 69 6e 6c + # diff(sect,finl) 6b 65 79 73 diff(sect,keys) @@ -125,14 +125,14 @@ block(sectitems) 6e 61 6d 65 diff(sect,name) - 6f 72 64 72 - diff(sect,ordr) + # 6f 72 64 72 + # diff(sect,ordr) 73 74 72 73 diff(sect,strs) - 74 72 61 6e - diff(sect,tran) + # 74 72 61 6e + # diff(sect,tran) 76 6b 65 79 diff(sect,vkey) @@ -143,18 +143,18 @@ block(endsect) # bksp # ---------------------------------------------------------------------------------------------------- -block(bksp) - 62 6b 73 70 - sizeof(bksp) - 01 00 00 00 # KMX_DWORD count; // 0008 number of entries - # }; +# block(bksp) +# 62 6b 73 70 +# sizeof(bksp) +# 01 00 00 00 # KMX_DWORD count; // 0008 number of entries +# # }; - # struct COMP_KMXPLUS_TRAN_ENTRY { - index(elemNull,elemBkspFrom) # KMXPLUS_ELEM from; - 00 00 00 00 # KMXPLUS_STR to; - 00 00 00 00 # KMXPLUS_ELEM before; - 00 00 00 00 # KMX_DWORD flags; - # }; +# # struct COMP_KMXPLUS_TRAN_ENTRY { +# index(elemNull,elemBkspFrom) # KMXPLUS_ELEM from; +# 00 00 00 00 # KMXPLUS_STR to; +# 00 00 00 00 # KMXPLUS_ELEM before; +# 00 00 00 00 # KMX_DWORD flags; +# # }; # ---------------------------------------------------------------------------------------------------- @@ -187,54 +187,54 @@ block(elem) # struct COMP_KMXPLUS_ELEM { 00 00 00 00 # KMX_DWORD offset; // 0010+ offset from this blob sizeof(elemNull,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemFinlFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemFinlFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + # diff(elem,elemFinlFrom) # KMX_DWORD offset; // 0010+ offset from this blob + # sizeof(elemFinlFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemTranFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemTranFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + # diff(elem,elemTranFrom) # KMX_DWORD offset; // 0010+ offset from this blob + # sizeof(elemTranFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemBkspFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemBkspFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + # diff(elem,elemBkspFrom) # KMX_DWORD offset; // 0010+ offset from this blob + # sizeof(elemBkspFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemOrdrFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemOrdrFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + # diff(elem,elemOrdrFrom) # KMX_DWORD offset; // 0010+ offset from this blob + # sizeof(elemOrdrFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemOrdrBefore) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemOrdrBefore,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + # diff(elem,elemOrdrBefore) # KMX_DWORD offset; // 0010+ offset from this blob + # sizeof(elemOrdrBefore,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) block(elemNull) -block(elemFinlFrom) - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - -block(elemTranFrom) - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemTranFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - -block(elemBkspFrom) - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemBkspFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - -block(elemOrdrFrom) - # - index(strNull,strElemOrdrFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 0A 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemOrdrFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 37 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemOrdrFrom3,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 0A 00 # KMX_DWORD flags; // flag and order values - unicodeset - -block(elemOrdrBefore) - index(strNull,strElemOrdrBefore,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset +# block(elemFinlFrom) +# index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset +# index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + +# block(elemTranFrom) +# index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset +# index(strNull,strElemTranFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + +# block(elemBkspFrom) +# index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset +# index(strNull,strElemBkspFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + +# block(elemOrdrFrom) +# # +# index(strNull,strElemOrdrFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 0A 00 # KMX_DWORD flags; // flag and order values - unicodeset +# index(strNull,strElemOrdrFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 37 00 # KMX_DWORD flags; // flag and order values - unicodeset +# index(strNull,strElemOrdrFrom3,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 0A 00 # KMX_DWORD flags; // flag and order values - unicodeset + +# block(elemOrdrBefore) +# index(strNull,strElemOrdrBefore,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint +# 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset block(endelem) @@ -242,18 +242,18 @@ block(endelem) # finl # ---------------------------------------------------------------------------------------------------- -block(finl) - 66 69 6e 6c - sizeof(finl) - 01 00 00 00 # KMX_DWORD count; // 0008 number of entries - # }; - - # struct COMP_KMXPLUS_TRAN_ENTRY { - index(elemNull,elemFinlFrom) # KMXPLUS_ELEM from; - index(strNull,strElemTranFrom1,2) # KMXPLUS_STR to; - 00 00 00 00 # KMXPLUS_ELEM before; - 01 00 00 00 # KMX_DWORD flags; - # }; +# block(finl) +# 66 69 6e 6c +# sizeof(finl) +# 01 00 00 00 # KMX_DWORD count; // 0008 number of entries +# # }; + +# # struct COMP_KMXPLUS_TRAN_ENTRY { +# index(elemNull,elemFinlFrom) # KMXPLUS_ELEM from; +# index(strNull,strElemTranFrom1,2) # KMXPLUS_STR to; +# 00 00 00 00 # KMXPLUS_ELEM before; +# 01 00 00 00 # KMX_DWORD flags; +# # }; # ---------------------------------------------------------------------------------------------------- # keys # ---------------------------------------------------------------------------------------------------- @@ -400,14 +400,14 @@ block(name) # struct COMP_KMXPLUS_META { # ordr # ---------------------------------------------------------------------------------------------------- -block(ordr) - 6f 72 64 72 # KMX_DWORD header.ident; // 0000 Section name - ordr - sizeof(ordr) # KMX_DWORD header.size; // 0004 Section length - 01 00 00 00 # KMX_DWORD count; // 0008 count of str entries - # }; +# block(ordr) +# 6f 72 64 72 # KMX_DWORD header.ident; // 0000 Section name - ordr +# sizeof(ordr) # KMX_DWORD header.size; // 0004 Section length +# 01 00 00 00 # KMX_DWORD count; // 0008 count of str entries +# # }; - index(elemNull,elemOrdrFrom) # KMXPLUS_ELEM elements; - index(elemNull,elemOrdrBefore) # KMXPLUS_ELEM before; +# index(elemNull,elemOrdrFrom) # KMXPLUS_ELEM elements; +# index(elemNull,elemOrdrBefore) # KMXPLUS_ELEM before; # ---------------------------------------------------------------------------------------------------- # strs @@ -437,13 +437,13 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) diff(strs,strThat) sizeof(strThat,2) - diff(strs,strTranTo) sizeof(strTranTo,2) + # diff(strs,strTranTo) sizeof(strTranTo,2) diff(strs,strKey1) sizeof(strKey1,2) diff(strs,strKeys) sizeof(strKeys,2) - diff(strs,strElemOrdrFrom3) sizeof(strElemOrdrFrom3,2) - diff(strs,strElemOrdrFrom1) sizeof(strElemOrdrFrom1,2) - diff(strs,strElemOrdrBefore) sizeof(strElemOrdrBefore,2) - diff(strs,strElemOrdrFrom2) sizeof(strElemOrdrFrom2,2) + # diff(strs,strElemOrdrFrom3) sizeof(strElemOrdrFrom3,2) + # diff(strs,strElemOrdrFrom1) sizeof(strElemOrdrFrom1,2) + # diff(strs,strElemOrdrBefore) sizeof(strElemOrdrBefore,2) + # diff(strs,strElemOrdrFrom2) sizeof(strElemOrdrFrom2,2) diff(strs,strIndicator) sizeof(strIndicator,2) @@ -465,14 +465,14 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' - block(strTranTo) E2 00 block(x) 00 00 # 'â' + # block(strTranTo) E2 00 block(x) 00 00 # 'â' block(strKey1) 27 01 block(x) 00 00 # 'ħ' block(strKeys) 90 17 b6 17 block(x) 00 00 # 'ថា' # - block(strElemOrdrFrom3) 45 1a block(x) 00 00 # 'ᩅ' - block(strElemOrdrFrom1) 60 1a block(x) 00 00 # '᩠' - block(strElemOrdrBefore) 6b 1a block(x) 00 00 # 'ᩫ' - block(strElemOrdrFrom2) 75 1a block(x) 00 00 # '᩵' + # block(strElemOrdrFrom3) 45 1a block(x) 00 00 # 'ᩅ' + # block(strElemOrdrFrom1) 60 1a block(x) 00 00 # '᩠' + # block(strElemOrdrBefore) 6b 1a block(x) 00 00 # 'ᩫ' + # block(strElemOrdrFrom2) 75 1a block(x) 00 00 # '᩵' block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀' @@ -484,18 +484,18 @@ block(endstrs) # end of strs block # tran # ---------------------------------------------------------------------------------------------------- -block(tran) # struct COMP_KMXPLUS_TRAN { - 74 72 61 6e # KMX_DWORD header.ident; // 0000 Section name - tran - sizeof(tran) # KMX_DWORD header.size; // 0004 Section length - 01 00 00 00 # KMX_DWORD count; // 0008 number of entries - # }; - - # struct COMP_KMXPLUS_TRAN_ENTRY { - index(elemNull,elemTranFrom) # KMXPLUS_ELEM from; - index(strNull,strTranTo,2) # KMXPLUS_STR to; - 00 00 00 00 # KMXPLUS_ELEM before; - 00 00 00 00 # KMX_DWORD flags; - # }; +# block(tran) # struct COMP_KMXPLUS_TRAN { +# 74 72 61 6e # KMX_DWORD header.ident; // 0000 Section name - tran +# sizeof(tran) # KMX_DWORD header.size; // 0004 Section length +# 01 00 00 00 # KMX_DWORD count; // 0008 number of entries +# # }; + +# # struct COMP_KMXPLUS_TRAN_ENTRY { +# index(elemNull,elemTranFrom) # KMXPLUS_ELEM from; +# index(strNull,strTranTo,2) # KMXPLUS_STR to; +# 00 00 00 00 # KMXPLUS_ELEM before; +# 00 00 00 00 # KMX_DWORD flags; +# # }; block(vkey) # struct COMP_KMXPLUS_VKEY { 76 6b 65 79 # KMX_DWORD header.ident; // 0000 Section name - vkey diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.xml b/developer/src/kmc-ldml/test/fixtures/basic.xml similarity index 66% rename from developer/src/kmc-keyboard/test/fixtures/basic.xml rename to developer/src/kmc-ldml/test/fixtures/basic.xml index 0703e30c328383064cfbec8006c6d3115d8e7fcb..4f9208cb20674f4846eb579abd4aaa526bffb20f 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.xml +++ b/developer/src/kmc-ldml/test/fixtures/basic.xml @@ -21,7 +21,7 @@ - + @@ -36,21 +36,26 @@ - - - + + + + + - - + + + + + + + + + - + + + - - - - - - diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/bksp/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/bksp/minimal.xml similarity index 80% rename from developer/src/kmc-keyboard/test/fixtures/sections/bksp/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/bksp/minimal.xml index 00b466380d28c99d2bb6e54cd80a9badf81d8218..6de012b33a1c20084a98e8ac6bf8817252b4d3e8 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/bksp/minimal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/bksp/minimal.xml @@ -9,6 +9,8 @@ - + + + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/escaped.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/escaped.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/escaped.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/escaped.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/invalid-dupto.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/invalid-dupto.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/invalid-dupto.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/invalid-dupto.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/options-only.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/options-only.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/options-only.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/options-only.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/typical.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/typical.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/typical.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/typical.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/finl/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/finl/minimal.xml similarity index 73% rename from developer/src/kmc-keyboard/test/fixtures/sections/finl/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/finl/minimal.xml index 80c22bd3feeda8df4317f863bf83550cf1a50e6f..b9d5176bc1245e3a24477e112944a7d6857fcfc1 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/finl/minimal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/finl/minimal.xml @@ -8,7 +8,9 @@ - - + + + + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/escaped.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/escaped.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped2.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/escaped2.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped2.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/escaped2.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/gap-switch.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/gap-switch.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_iso.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_iso.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_iso.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_iso.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_us.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_us.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_us.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_us.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-bad-modifier.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-bad-modifier.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-bad-modifier.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-bad-modifier.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-key-missing-attrs.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-key-missing-attrs.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-missing-flick.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-missing-flick.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-missing-flick.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-missing-flick.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-undefined-key.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-undefined-key.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-undefined-key.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-undefined-key.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-invalid-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-invalid-form.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-invalid-form.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-invalid-form.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer2.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer2.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer2.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer2.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-multi-hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-multi-hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-multi-hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-multi-hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/invalid-locale.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/invalid-locale.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/invalid-locale.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/invalid-locale.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/multiple.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/multiple.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/multiple.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/multiple.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-normalization.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-normalization.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-normalization.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-normalization.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-1.0.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-1.0.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-1.0.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-1.0.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-v1.0.3.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-v1.0.3.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-v1.0.3.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-v1.0.3.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/maximal.xml similarity index 83% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/maximal.xml index 473501457fdb441fe9987a015a782cbc3870b788..f42d892e91ce56decc0d32bdfc9b04d76231ab94 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/meta/maximal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/meta/maximal.xml @@ -10,7 +10,7 @@ - + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/name/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/name/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/name/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/name/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/name/multiple.xml b/developer/src/kmc-ldml/test/fixtures/sections/name/multiple.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/name/multiple.xml rename to developer/src/kmc-ldml/test/fixtures/sections/name/multiple.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/ordr/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/ordr/minimal.xml similarity index 63% rename from developer/src/kmc-keyboard/test/fixtures/sections/ordr/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/ordr/minimal.xml index 6ab49c6a5a2710a566e0db9daa0a366bd7f31e7a..c29f6621cb68af764865251d43180e6ed8e18b25 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/ordr/minimal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/ordr/minimal.xml @@ -8,8 +8,10 @@ - - - - + + + + + + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/tran/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/tran/minimal.xml similarity index 80% rename from developer/src/kmc-keyboard/test/fixtures/sections/tran/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/tran/minimal.xml index b6ff27db6ddbfd4ef69faddaaf768e039af09b27..6504c6920f5b18c4c02d4c614ce66ef570b374fa 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/tran/minimal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/tran/minimal.xml @@ -9,6 +9,8 @@ - + + + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-from-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-from-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-repeated-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-repeated-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-to-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-to-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/redundant.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/redundant.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/same-target.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/same-target.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/test-fr.json b/developer/src/kmc-ldml/test/fixtures/test-fr.json similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/test-fr.json rename to developer/src/kmc-ldml/test/fixtures/test-fr.json diff --git a/developer/src/kmc-keyboard/test/fixtures/test-fr.xml b/developer/src/kmc-ldml/test/fixtures/test-fr.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/test-fr.xml rename to developer/src/kmc-ldml/test/fixtures/test-fr.xml diff --git a/developer/src/kmc-keyboard/test/helpers/index.ts b/developer/src/kmc-ldml/test/helpers/index.ts similarity index 74% rename from developer/src/kmc-keyboard/test/helpers/index.ts rename to developer/src/kmc-ldml/test/helpers/index.ts index b5d86a93b3a20414befc45df0146917377323651..8ba8a28aa84ded8a6549461d6ea8b657302bb431 100644 --- a/developer/src/kmc-keyboard/test/helpers/index.ts +++ b/developer/src/kmc-ldml/test/helpers/index.ts @@ -3,15 +3,15 @@ */ import 'mocha'; import * as path from 'path'; -import * as fs from 'fs'; import { fileURLToPath } from 'url'; import { SectionCompiler } from '../../src/compiler/section-compiler.js'; -import { KMXPlus, LDMLKeyboardXMLSourceFileReader, VisualKeyboard, CompilerEvent, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; -import Compiler from '../../src/compiler/compiler.js'; +import { KMXPlus, LDMLKeyboardXMLSourceFileReader, VisualKeyboard, CompilerEvent, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; +import { LdmlKeyboardCompiler } from '../../src/compiler/compiler.js'; import { assert } from 'chai'; -import KMXPlusMetadataCompiler from '../../src/compiler/metadata-compiler.js'; -import CompilerOptions from '../../src/compiler/compiler-options.js'; -import VisualKeyboardCompiler from '../../src/compiler/visual-keyboard-compiler.js'; +import { KMXPlusMetadataCompiler } from '../../src/compiler/metadata-compiler.js'; +import { CompilerOptions } from '../../src/compiler/compiler-options.js'; +import { LdmlKeyboardVisualKeyboardCompiler } from '../../src/compiler/visual-keyboard-compiler.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; import KMXPlusFile = KMXPlus.KMXPlusFile; import Elem = KMXPlus.Elem; @@ -31,44 +31,6 @@ export function makePathToFixture(...components: string[]): string { return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url)); } -/** - * A CompilerCallbacks implementation for testing - */ -class TestCompilerCallbacks implements CompilerCallbacks { - clear() { - this.messages = []; - } - messages: CompilerEvent[] = []; - loadFile(baseFilename: string, filename: string | URL): Buffer { - // TODO: translate filename based on the baseFilename - try { - return fs.readFileSync(filename); - } catch(e) { - if (e.code === 'ENOENT') { - return null; - } else { - throw e; - } - } - } - reportMessage(event: CompilerEvent): void { - // console.log(event.message); - this.messages.push(event); - } - loadLdmlKeyboardSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'ldml-keyboard.schema.json'), import.meta.url)); - } - loadKvksJsonSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'kvks.schema.json'), import.meta.url)); - } - loadKpjJsonSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'kpj.schema.json'), import.meta.url)); - } - loadLdmlKeyboardTestSchema(): Buffer { - return fs.readFileSync(new URL(path.join('..', '..', 'src', 'ldml-keyboardtest.schema.json'), import.meta.url)); - } -}; - export const compilerTestCallbacks = new TestCompilerCallbacks(); beforeEach(function() { @@ -85,14 +47,14 @@ afterEach(function() { export function loadSectionFixture(compilerClass: typeof SectionCompiler, filename: string, callbacks: TestCompilerCallbacks): Section { callbacks.messages = []; const inputFilename = makePathToFixture(filename); - const data = callbacks.loadFile(inputFilename, inputFilename); + const data = callbacks.loadFile(inputFilename); assert.isNotNull(data); const reader = new LDMLKeyboardXMLSourceFileReader(callbacks); const source = reader.load(data); assert.isNotNull(source); - if (!reader.validate(source, callbacks.loadLdmlKeyboardSchema())) { + if (!reader.validate(source, callbacks.loadSchema('ldml-keyboard'))) { return null; // mimic kmc behavior - bail if validate fails } @@ -114,13 +76,13 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena } export function loadTestdata(inputFilename: string, options: CompilerOptions) : LDMLKeyboardTestDataXMLSourceFile { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.loadTestData(inputFilename); return source; } export function compileKeyboard(inputFilename: string, options: CompilerOptions): KMXPlusFile { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -141,7 +103,7 @@ export function compileKeyboard(inputFilename: string, options: CompilerOptions) } export function compileVisualKeyboard(inputFilename: string, options: CompilerOptions): VisualKeyboard.VisualKeyboard { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -150,9 +112,9 @@ export function compileVisualKeyboard(inputFilename: string, options: CompilerOp checkMessages(); assert.isTrue(valid, 'k.validate should not have failed'); - const vk = (new VisualKeyboardCompiler()).compile(source); + const vk = (new LdmlKeyboardVisualKeyboardCompiler()).compile(source); checkMessages(); - assert.isNotNull(vk, 'VisualKeyboardCompiler.compile should not have returned null'); + assert.isNotNull(vk, 'LdmlKeyboardVisualKeyboardCompiler.compile should not have returned null'); return vk; } diff --git a/developer/src/kmc-keyboard/test/test-bksp.ts b/developer/src/kmc-ldml/test/test-bksp.ts similarity index 58% rename from developer/src/kmc-keyboard/test/test-bksp.ts rename to developer/src/kmc-ldml/test/test-bksp.ts index 0254a4647f7556567a3682ae847a94c3cc727cc1..92a55077b517140b24383409d8de43bf020fa8e5 100644 --- a/developer/src/kmc-keyboard/test/test-bksp.ts +++ b/developer/src/kmc-ldml/test/test-bksp.ts @@ -14,13 +14,15 @@ describe('bksp', function () { let bksp = loadSectionFixture(BkspCompiler, 'sections/bksp/minimal.xml', compilerTestCallbacks) as Bksp; assert.lengthOf(compilerTestCallbacks.messages, 0); - assert.lengthOf(bksp.items, 1); - assert.lengthOf(bksp.items[0].from, 2); - assert.strictEqual(bksp.items[0].from[0].value.value, "្"); - assert.strictEqual(bksp.items[0].from[1].value.value, "ម"); - assert.strictEqual(bksp.items[0].flags, BkspItemFlags.none); - assert.isEmpty(bksp.items[0].before); - assert.strictEqual(bksp.items[0].to.value, ""); + it.skip('TODO-LDML rewriting this #7377', () => { + assert.lengthOf(bksp.items, 1); + assert.lengthOf(bksp.items[0].from, 2); + assert.strictEqual(bksp.items[0].from[0].value.value, "្"); + assert.strictEqual(bksp.items[0].from[1].value.value, "ម"); + assert.strictEqual(bksp.items[0].flags, BkspItemFlags.none); + assert.isEmpty(bksp.items[0].before); + assert.strictEqual(bksp.items[0].to.value, ""); + }); }); }); diff --git a/developer/src/kmc-keyboard/test/test-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-compiler-e2e.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-compiler-e2e.ts rename to developer/src/kmc-ldml/test/test-compiler-e2e.ts diff --git a/developer/src/kmc-keyboard/test/test-disp.ts b/developer/src/kmc-ldml/test/test-disp.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-disp.ts rename to developer/src/kmc-ldml/test/test-disp.ts diff --git a/developer/src/kmc-keyboard/test/test-finl.ts b/developer/src/kmc-ldml/test/test-finl.ts similarity index 59% rename from developer/src/kmc-keyboard/test/test-finl.ts rename to developer/src/kmc-ldml/test/test-finl.ts index 2aa8495bb470e0d530ee8751045c591482ec13fb..89189031e187c6b9bc65aa8e82255ba7407cf377 100644 --- a/developer/src/kmc-keyboard/test/test-finl.ts +++ b/developer/src/kmc-ldml/test/test-finl.ts @@ -14,13 +14,15 @@ describe('finl', function () { let finl = loadSectionFixture(FinlCompiler, 'sections/finl/minimal.xml', compilerTestCallbacks) as Finl; assert.lengthOf(compilerTestCallbacks.messages, 0); - assert.lengthOf(finl.items, 1); - assert.lengthOf(finl.items[0].from, 2); - assert.strictEqual(finl.items[0].from[0].value.value, "x"); - assert.strictEqual(finl.items[0].from[1].value.value, "x"); - assert.strictEqual(finl.items[0].flags, FinlItemFlags.error); - assert.isEmpty(finl.items[0].before); - assert.strictEqual(finl.items[0].to.value, "x"); + it.skip('TODO-LDML rewriting this #7377', () => { + assert.lengthOf(finl.items, 1); + assert.lengthOf(finl.items[0].from, 2); + assert.strictEqual(finl.items[0].from[0].value.value, "x"); + assert.strictEqual(finl.items[0].from[1].value.value, "x"); + assert.strictEqual(finl.items[0].flags, FinlItemFlags.error); + assert.isEmpty(finl.items[0].before); + assert.strictEqual(finl.items[0].to.value, "x"); + }); }); }); diff --git a/developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts similarity index 76% rename from developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts rename to developer/src/kmc-ldml/test/test-keymanweb-compiler.ts index c4449b44c14024f376a17acd081969efe6239a98..fb4f28daeeedf1fef9a6c191df57b55e0c5f2933 100644 --- a/developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts +++ b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts @@ -1,11 +1,11 @@ import 'mocha'; import { assert } from 'chai'; import { checkMessages, compilerTestCallbacks, makePathToFixture } from './helpers/index.js'; -import { KeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js'; -import Compiler from '../src/compiler/compiler.js'; +import { LdmlKeyboardKeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js'; +import { LdmlKeyboardCompiler } from '../src/compiler/compiler.js'; import * as fs from 'fs'; -describe('KeymanWebCompiler', function() { +describe('LdmlKeyboardKeymanWebCompiler', function() { it('should build a .js file', async function() { // Let's build basic.xml @@ -16,7 +16,7 @@ describe('KeymanWebCompiler', function() { // Load input data; we'll use the LDML keyboard compiler loader to save us // effort here - const k = new Compiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false}); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false}); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -27,7 +27,7 @@ describe('KeymanWebCompiler', function() { assert.isTrue(valid, 'k.validate should not have failed'); // Actual test: compile to javascript - const jsCompiler = new KeymanWebCompiler({debug: true}); + const jsCompiler = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: true}); const output = jsCompiler.compile('basic.xml', source); assert.isNotNull(output); @@ -36,7 +36,7 @@ describe('KeymanWebCompiler', function() { assert.strictEqual(output, outputFixture); // Second test: compile to javascript without debug formatting - const jsCompilerNoDebug = new KeymanWebCompiler({debug: false}); + const jsCompilerNoDebug = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: false}); const outputNoDebug = jsCompilerNoDebug.compile('basic.xml', source); assert.isNotNull(outputNoDebug); diff --git a/developer/src/kmc-keyboard/test/test-keys.ts b/developer/src/kmc-ldml/test/test-keys.ts similarity index 99% rename from developer/src/kmc-keyboard/test/test-keys.ts rename to developer/src/kmc-ldml/test/test-keys.ts index c315cd31af5e7ea8d1cd72b5c693e5cccba6a7f6..df978de773cada0195258caf91e07d804effd13b 100644 --- a/developer/src/kmc-keyboard/test/test-keys.ts +++ b/developer/src/kmc-ldml/test/test-keys.ts @@ -230,7 +230,7 @@ describe('keys.kmap', function () { assert.isNull(keys); assert.equal(compilerTestCallbacks.messages.length, 1); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({row: 1, hardware: 'us'})); + assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({row: 1, hardware: 'us', modifier: 'none'})); }); it('should reject layouts with undefined keys', function() { diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-ldml/test/test-layr.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-layr.ts rename to developer/src/kmc-ldml/test/test-layr.ts diff --git a/developer/src/kmc-keyboard/test/test-loca.ts b/developer/src/kmc-ldml/test/test-loca.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-loca.ts rename to developer/src/kmc-ldml/test/test-loca.ts diff --git a/developer/src/kmc-ldml/test/test-messages.ts b/developer/src/kmc-ldml/test/test-messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..0dee53f12112d47b67ca19bc266630a791dd4bf9 --- /dev/null +++ b/developer/src/kmc-ldml/test/test-messages.ts @@ -0,0 +1,9 @@ +import 'mocha'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; + +describe('CompilerMessages', function () { + it('should have a valid CompilerMessages object', function() { + return verifyCompilerMessagesObject(CompilerMessages); + }); +}); diff --git a/developer/src/kmc-keyboard/test/test-meta.ts b/developer/src/kmc-ldml/test/test-meta.ts similarity index 95% rename from developer/src/kmc-keyboard/test/test-meta.ts rename to developer/src/kmc-ldml/test/test-meta.ts index c2e32d68b4dfe9090ea69c19024bd670a4885605..e507222866a6a49d9a9b81f7af2c3bf5d1d5aa0c 100644 --- a/developer/src/kmc-keyboard/test/test-meta.ts +++ b/developer/src/kmc-ldml/test/test-meta.ts @@ -33,7 +33,7 @@ describe('meta', function () { assert.equal(meta.normalization.value, 'NFC'); assert.equal(meta.indicator.value, 'QW'); assert.equal(meta.version.value, "1.2.3"); - assert.equal(meta.settings, KeyboardSettings.fallback | KeyboardSettings.transformFailure | KeyboardSettings.transformPartial); + assert.equal(meta.settings, KeyboardSettings.fallback); }); it('should reject invalid normalization', function() { diff --git a/developer/src/kmc-keyboard/test/test-metadata-compiler.ts b/developer/src/kmc-ldml/test/test-metadata-compiler.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-metadata-compiler.ts rename to developer/src/kmc-ldml/test/test-metadata-compiler.ts diff --git a/developer/src/kmc-keyboard/test/test-name.ts b/developer/src/kmc-ldml/test/test-name.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-name.ts rename to developer/src/kmc-ldml/test/test-name.ts diff --git a/developer/src/kmc-ldml/test/test-ordr.ts b/developer/src/kmc-ldml/test/test-ordr.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f764e7b633ecc7f87c84a42419900b389ea03b8 --- /dev/null +++ b/developer/src/kmc-ldml/test/test-ordr.ts @@ -0,0 +1,31 @@ +import 'mocha'; +import { assert } from 'chai'; +import { OrdrCompiler } from '../src/compiler/ordr.js'; +import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; +import { KMXPlus } from '@keymanapp/common-types'; + +import Ordr = KMXPlus.Ordr; + +describe('ordr', function () { + this.slow(500); // 0.5 sec -- json schema validation takes a while + + it('should compile minimal ordr data', function() { + let ordr = loadSectionFixture(OrdrCompiler, 'sections/ordr/minimal.xml', compilerTestCallbacks) as Ordr; + assert.lengthOf(compilerTestCallbacks.messages, 0); + + it.skip('TODO-LDML rewriting this #7377', () => { + assert.lengthOf(ordr.items, 1); + assert.lengthOf(ordr.items[0].elements, 4); + assert.strictEqual(ordr.items[0].elements[0].value.value, "ខ"); + assert.strictEqual(ordr.items[0].elements[1].value.value, "ែ"); + assert.strictEqual(ordr.items[0].elements[2].value.value, "្"); + assert.strictEqual(ordr.items[0].elements[3].value.value, "ម"); + assert.strictEqual(ordr.items[0].elements[0].order, 1); + assert.strictEqual(ordr.items[0].elements[1].order, 3); + assert.strictEqual(ordr.items[0].elements[2].order, 4); + assert.strictEqual(ordr.items[0].elements[3].order, 2); + assert.isEmpty(ordr.items[0].before); + }); + }); +}); + diff --git a/developer/src/kmc-keyboard/test/test-testdata-e2e.ts b/developer/src/kmc-ldml/test/test-testdata-e2e.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-testdata-e2e.ts rename to developer/src/kmc-ldml/test/test-testdata-e2e.ts diff --git a/developer/src/kmc-keyboard/test/test-tran.ts b/developer/src/kmc-ldml/test/test-tran.ts similarity index 59% rename from developer/src/kmc-keyboard/test/test-tran.ts rename to developer/src/kmc-ldml/test/test-tran.ts index 9a75defe2ea58989b1f01961055e92123e6a165a..0661af62a520179ad85ea61633db5c49d07f2f0c 100644 --- a/developer/src/kmc-keyboard/test/test-tran.ts +++ b/developer/src/kmc-ldml/test/test-tran.ts @@ -14,13 +14,15 @@ describe('tran', function () { let tran = loadSectionFixture(TranCompiler, 'sections/tran/minimal.xml', compilerTestCallbacks) as Tran; assert.lengthOf(compilerTestCallbacks.messages, 0); - assert.lengthOf(tran.items, 1); - assert.lengthOf(tran.items[0].from, 2); - assert.strictEqual(tran.items[0].from[0].value.value, "x"); - assert.strictEqual(tran.items[0].from[1].value.value, "x"); - assert.strictEqual(tran.items[0].flags, TranItemFlags.error); - assert.isEmpty(tran.items[0].before); - assert.strictEqual(tran.items[0].to.value, "x"); + it.skip('TODO-LDML rewriting this #7377', () => { + assert.lengthOf(tran.items, 1); + assert.lengthOf(tran.items[0].from, 2); + assert.strictEqual(tran.items[0].from[0].value.value, "x"); + assert.strictEqual(tran.items[0].from[1].value.value, "x"); + assert.strictEqual(tran.items[0].flags, TranItemFlags.error); + assert.isEmpty(tran.items[0].before); + assert.strictEqual(tran.items[0].to.value, "x"); + }); }); }); diff --git a/developer/src/kmc-keyboard/test/test-utils.ts b/developer/src/kmc-ldml/test/test-utils.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-utils.ts rename to developer/src/kmc-ldml/test/test-utils.ts diff --git a/developer/src/kmc-keyboard/test/test-virtual-key-constants.ts b/developer/src/kmc-ldml/test/test-virtual-key-constants.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-virtual-key-constants.ts rename to developer/src/kmc-ldml/test/test-virtual-key-constants.ts diff --git a/developer/src/kmc-keyboard/test/test-visual-keyboard-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-visual-keyboard-compiler-e2e.ts rename to developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts diff --git a/developer/src/kmc-keyboard/test/test-vkey.ts b/developer/src/kmc-ldml/test/test-vkey.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-vkey.ts rename to developer/src/kmc-ldml/test/test-vkey.ts diff --git a/developer/src/kmc-keyboard/test/tsconfig.json b/developer/src/kmc-ldml/test/tsconfig.json similarity index 85% rename from developer/src/kmc-keyboard/test/tsconfig.json rename to developer/src/kmc-ldml/test/tsconfig.json index 263cdd0d9459a0bfe7fafc40eeeb3196351c54a4..cfc7583fa48620c06c92c20039f6b11dfa724f4d 100644 --- a/developer/src/kmc-keyboard/test/tsconfig.json +++ b/developer/src/kmc-ldml/test/tsconfig.json @@ -10,6 +10,7 @@ "paths": { // "@keymanapp/keyman-version": ["../../../common/web/keyman-version/keyman-version.mts"], "@keymanapp/common-types": ["../../../../common/web/types/src/main"], + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], // "@keymanapp/": ["core/include/ldml/ldml-keyboard-constants"], }, }, @@ -21,6 +22,7 @@ { "path": "../../../../common/web/keyman-version/tsconfig.esm.json" }, { "path": "../../../../common/web/types/" }, { "path": "../../../../common/tools/hextobin/" }, + { "path": "../../common/web/test-helpers/" }, { "path": "../" } ] } \ No newline at end of file diff --git a/developer/src/kmc-keyboard/tsconfig.json b/developer/src/kmc-ldml/tsconfig.json similarity index 100% rename from developer/src/kmc-keyboard/tsconfig.json rename to developer/src/kmc-ldml/tsconfig.json diff --git a/developer/src/kmc-model-info/.eslintrc.cjs b/developer/src/kmc-model-info/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..01dae6b737e69e50f7ece590cc3fa7716d70ed11 --- /dev/null +++ b/developer/src/kmc-model-info/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + overrides: [ + { + files:"src/**/*.ts", + // extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + } + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc-model-info/Makefile b/developer/src/kmc-model-info/Makefile index f3e73efd1239abdb668726af3ac9b51169d942ac..878d77f184f61fd91dd90ee8c403ed7c513c8f8c 100644 --- a/developer/src/kmc-model-info/Makefile +++ b/developer/src/kmc-model-info/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-model-info/build.sh b/developer/src/kmc-model-info/build.sh index da212a417b286691faa0aecd84e441f82a49faf3..7c2119826f5c3dba16e250d92b387e60bb6cfbc1 100755 --- a/developer/src/kmc-model-info/build.sh +++ b/developer/src/kmc-model-info/build.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -# -# Compiles the kmc lexical model model-info compiler. -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" @@ -17,13 +10,16 @@ cd "$THIS_SCRIPT_PATH" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" builder_describe "Build Keyman kmc Lexical Model model-info Compiler module" \ - "@/developer/src/kmc-package" \ + "@/common/web/types" \ + "@/common/models/types" \ + "clean" \ "configure" \ "build" \ - "clean" \ "test" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" + builder_describe_outputs \ configure /node_modules \ build /developer/src/kmc-model-info/build/src/model-info-compiler.js @@ -61,7 +57,11 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/developer/src/kmc-model-info/package.json b/developer/src/kmc-model-info/package.json index 96d660b8dbc1436248aaa6b95381ac13e13fabb8..709f9d2d6fc0b29a0b732361a6463e6b487e42ad 100644 --- a/developer/src/kmc-model-info/package.json +++ b/developer/src/kmc-model-info/package.json @@ -12,9 +12,13 @@ "exports": { ".": "./build/src/model-info-compiler.js" }, + "files": [ + "/build/src/" + ], "scripts": { "build": "tsc -b", - "test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", "prepublishOnly": "npm run build" }, "author": "Marc Durdin (https://github.com/mcdurdin)", @@ -27,8 +31,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/kmc-package": "*", + "@keymanapp/common-types": "*", "@keymanapp/models-types": "*" }, "devDependencies": { diff --git a/developer/src/kmc-model-info/src/model-info-compiler.ts b/developer/src/kmc-model-info/src/model-info-compiler.ts index f4480a1d5b650cc6591647d2bed3c4142f9d90a9..7fd22308d557d9d0bb3ebec7064fbb87ffb1ced1 100644 --- a/developer/src/kmc-model-info/src/model-info-compiler.ts +++ b/developer/src/kmc-model-info/src/model-info-compiler.ts @@ -3,21 +3,18 @@ * compiled files to produce a comprehensive .model_info file. */ -/// -/// - import * as fs from "fs"; import * as path from "path"; import { minKeymanVersion } from "./min-keyman-version.js"; import { ModelInfoFile } from "./model-info-file.js"; -import { type KmpJsonFile } from "@keymanapp/kmc-package"; +import { KmpJsonFile } from "@keymanapp/common-types"; export class ModelInfoOptions { /** The identifier for the model */ model_id: string; /** The data from the .kps file, transformed to kmp.json */ - kmpJsonData: KmpJsonFile; + kmpJsonData: KmpJsonFile.KmpJsonFile; /** The path in the keymanapp/lexical-models repo where this model may be found (optional) */ sourcePath?: string; diff --git a/developer/src/kmc-model-info/tsconfig.json b/developer/src/kmc-model-info/tsconfig.json index 1609623edfc41aa402e86c9a987ca1dbf7db7d1b..1e6ba49b190f09623f7062e6b0abaf000d87662b 100644 --- a/developer/src/kmc-model-info/tsconfig.json +++ b/developer/src/kmc-model-info/tsconfig.json @@ -12,6 +12,7 @@ "src/**/*.ts" ], "references": [ - { "path": "../kmc-package" } // can we eliminate this in future? + { "path": "../../../common/web/types" }, + { "path": "../../../common/models/types" }, ] } diff --git a/developer/src/kmc-model/.eslintrc.cjs b/developer/src/kmc-model/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..bd32773f9ada5d3c4e82ac3ff22bc268d70c0aad --- /dev/null +++ b/developer/src/kmc-model/.eslintrc.cjs @@ -0,0 +1,19 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + ignorePatterns: [ + "test/fixtures/**/*", + "tools/*" /* TODO: linting on tools */, + ], + overrides: [ + { + files: "src/**/*.ts", + // extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + }, + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + "no-var": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc-model/Makefile b/developer/src/kmc-model/Makefile index 353d923bd445a82056020bb49859e617054122c8..b41bbe81844c79856e4479de808232ef618bff98 100644 --- a/developer/src/kmc-model/Makefile +++ b/developer/src/kmc-model/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-model/build.sh b/developer/src/kmc-model/build.sh index 2582c10451e1987644d6c1989d1094573c184c27..ecf58e20e478e40cdb2094d854acb523e483f261 100755 --- a/developer/src/kmc-model/build.sh +++ b/developer/src/kmc-model/build.sh @@ -1,31 +1,28 @@ #!/usr/bin/env bash -# -# Compiles the kmc lexical model compiler. -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE -cd "$THIS_SCRIPT_PATH" - . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + +cd "$THIS_SCRIPT_PATH" # TODO: "@/common/models/types" \ builder_describe "Build Keyman kmc Lexical Model Compiler module" \ "@/common/web/keyman-version" \ + "@/developer/src/common/web/test-helpers" \ + "clean" \ "configure" \ "build" \ - "clean" \ "test" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" + builder_describe_outputs \ configure /node_modules \ build /developer/src/kmc-model/build/src/main.js @@ -34,40 +31,15 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- -if builder_start_action clean; then - rm -rf ./build/ ./tsconfig.tsbuildinfo - builder_finish_action success clean -fi - -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action configure; then - verify_npm_setup - builder_finish_action success configure -fi - -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action build; then - # Note: build-cjs only emits lexical-model-compiler.cjs at this time, as that - # is the only file required by other non-ES modules - # (common/web/input-processor tests) +function do_build() { mkdir -p build/cjs-src npm run build - builder_finish_action success build -fi +} -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action test; then - npm test - builder_finish_action success test -fi - -#------------------------------------------------------------------------------------------------------------------- +builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo +builder_run_action configure verify_npm_setup +builder_run_action build do_build +builder_run_action test npm test +builder_run_action publish builder_publish_to_npm +builder_run_action pack builder_publish_to_pack -if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish - builder_finish_action success publish -fi diff --git a/developer/src/kmc-model/package.json b/developer/src/kmc-model/package.json index 9ea9ebefb6b6c73b7e3b4ce8e0d2e70902635a25..bbeefca05ed595d2787340a1942ab281b3970764 100644 --- a/developer/src/kmc-model/package.json +++ b/developer/src/kmc-model/package.json @@ -12,10 +12,14 @@ "exports": { ".": "./build/src/main.js" }, + "files": [ + "/build/src/" + ], "scripts": { "build": "tsc -b && npm run build-cjs", "build-cjs": "esbuild build/src/lexical-model-compiler.js --bundle --platform=node --external:../../node_modules/* > build/cjs-src/lexical-model-compiler.cjs", - "test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", "prepublishOnly": "npm run build" }, "author": "Marc Durdin (https://github.com/mcdurdin)", @@ -30,12 +34,11 @@ "dependencies": { "@keymanapp/keyman-version": "*", "@keymanapp/models-types": "*", + "@keymanapp/common-types": "*", "typescript": "^4.9.5", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "*", - "@keymanapp/models-wordbreakers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", @@ -45,7 +48,8 @@ "chalk": "^2.4.2", "esbuild": "^0.15.7", "mocha": "^10.0.0", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "@keymanapp/developer-test-helpers": "*" }, "mocha": { "spec": "build/test/**/test-*.js", @@ -53,6 +57,18 @@ "source-map-support/register" ] }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "test/", + "tools/", + "src/lexical-model.ts" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/keymanapp/keyman.git" diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 75dc2db592bf97985c56596d0f01cde9d6ecbed6..beeb80821b6bac61d3cceb84a612e3119d54230e 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -1,5 +1,6 @@ import { readFileSync } from "fs"; -import { log, KeymanCompilerError } from "./model-compiler-errors.js"; +import { ModelCompilerError, ModelCompilerMessageContext, ModelCompilerMessages } from "./model-compiler-errors.js"; +import { callbacks } from "./compiler-callbacks.js"; // Supports LF or CRLF line terminators. const NEWLINE_SEPARATOR = /\u000d?\u000a/; @@ -23,7 +24,7 @@ export type WordList = {[wordform: string]: number}; */ export function createTrieDataStructure(filenames: string[], searchTermToKey?: (wf: string) => string): string { if (typeof searchTermToKey !== "function") { - throw new TypeError("searchTermToKey must be explicitly specified") + throw new ModelCompilerError(ModelCompilerMessages.Error_SearchTermToKeyMustBeExplicitlySpecified()); } // Make one big word list out of all of the filenames provided. let wordlist: WordList = {}; @@ -45,6 +46,7 @@ export function createTrieDataStructure(filenames: string[], searchTermToKey?: ( * @param filename filename of the word list */ export function parseWordListFromFilename(wordlist: WordList, filename: string): void { + ModelCompilerMessageContext.filename = filename; _parseWordList(wordlist, new WordListFromFilename(filename)); } @@ -56,6 +58,7 @@ export function parseWordListFromFilename(wordlist: WordList, filename: string): * @param filename filename of the word list */ export function parseWordListFromContents(wordlist: WordList, contents: string): void { + ModelCompilerMessageContext.filename = undefined; _parseWordList(wordlist, new WordListFromMemory(contents)); } @@ -91,6 +94,8 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { let wordsSeenInThisFile = new Set(); for (let [lineno, line] of source.lines()) { + ModelCompilerMessageContext.line = lineno; + // Remove the byte-order mark (BOM) from the beginning of the string. // Because `contents` can be the concatenation of several files, we have to remove // the BOM from every possible start of file -- i.e., beginning of every line. @@ -109,11 +114,7 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { wordform = wordform.normalize('NFC'); if (original !== wordform) { // Mixed normalization forms are yucky! Warn about it. - log( - KeymanCompilerError.CWARN_MixedNormalizationForms, - `“${wordform}” is not in Unicode NFC. Automatically converting to NFC.`, - {filename: source.name, lineno} - ) + callbacks.reportMessage(ModelCompilerMessages.Warn_MixedNormalizationForms({wordform: wordform})); } wordform = wordform.trim() @@ -131,11 +132,7 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { if (wordsSeenInThisFile.has(wordform)) { // The same word seen across multiple files is fine, // but a word seen multiple times in one file is a problem! - log( - KeymanCompilerError.CWARN_DuplicateWordInSameFile, - `duplicate word “${wordform}” found in same file; summing counts`, - {filename: source.name, lineno} - ) + callbacks.reportMessage(ModelCompilerMessages.Warn_DuplicateWordInSameFile({wordform: wordform})); } wordsSeenInThisFile.add(wordform); @@ -473,7 +470,7 @@ namespace Trie { } if(isNaN(val)) { - console.error("Unexpected NaN has appeared!"); + throw new Error("Unexpected NaN has appeared!"); } return val; } @@ -503,7 +500,7 @@ function detectEncoding(filename: string): 'utf8' | 'utf16le' { } else if (buffer[0] == 0xFE && buffer[1] == 0xFF) { // Big Endian, is NOT supported because Node does not support it (???) // See: https://stackoverflow.com/a/14551669/6626414 - throw new Error('UTF-16BE is unsupported') + throw new ModelCompilerError(ModelCompilerMessages.Error_UTF16BEUnsupported()); } else { // Assume its in UTF-8, with or without a BOM. return 'utf8'; diff --git a/developer/src/kmc-model/src/compiler-callbacks.ts b/developer/src/kmc-model/src/compiler-callbacks.ts new file mode 100644 index 0000000000000000000000000000000000000000..a077035003f9e36b258ac008b1e6d64372e01510 --- /dev/null +++ b/developer/src/kmc-model/src/compiler-callbacks.ts @@ -0,0 +1,7 @@ +import { CompilerCallbacks } from "@keymanapp/common-types"; + +export let callbacks: CompilerCallbacks; + +export function setCompilerCallbacks(c: CompilerCallbacks) { + callbacks = c; +} \ No newline at end of file diff --git a/developer/src/kmc-model/src/lexical-model-compiler.ts b/developer/src/kmc-model/src/lexical-model-compiler.ts index 0c55242c47598883545198643d9c2c346498de0a..0bf8bd8db4c2925645e51cdf999e7b1634025089 100644 --- a/developer/src/kmc-model/src/lexical-model-compiler.ts +++ b/developer/src/kmc-model/src/lexical-model-compiler.ts @@ -12,6 +12,7 @@ import { ModelDefinitions } from "./model-definitions.js"; import {decorateWithJoin} from "./join-word-breaker-decorator.js"; import {decorateWithScriptOverrides} from "./script-overrides-decorator.js"; import { LexicalModelSource, WordBreakerSpec, SimpleWordBreakerSpec } from "./lexical-model.js"; +import { ModelCompilerError, ModelCompilerMessages } from "./model-compiler-errors.js"; export default class LexicalModelCompiler { @@ -43,7 +44,7 @@ export default class LexicalModelCompiler { func += `LMLayerWorker.loadModel(new ${modelSource.rootClass}());\n`; break; case "fst-foma-1.0": - throw new ModelSourceError(`Unimplemented model format: ${modelSource.format}`); + throw new ModelCompilerError(ModelCompilerMessages.Error_UnimplementedModelFormat({format:modelSource.format})); case "trie-1.0": // Convert all relative path names to paths relative to the enclosing // directory. This way, we'll read the files relative to the model.ts @@ -81,7 +82,7 @@ export default class LexicalModelCompiler { func += `}));\n`; break; default: - throw new ModelSourceError(`Unknown model format: ${modelSource.format}`); + throw new ModelCompilerError(ModelCompilerMessages.Error_UnknownModelFormat({format: modelSource.format})); } func += fileSuffix; @@ -99,14 +100,8 @@ export default class LexicalModelCompiler { ); }; - logError(s: string) { - console.error(require('chalk').red(s)); - }; }; -export class ModelSourceError extends Error { -} - /** * Returns a JavaScript expression (as a string) that can serve as a word * breaking function. @@ -170,7 +165,7 @@ function normalizeWordBreakerSpec(wordBreakerSpec: LexicalModelSource["wordBreak } else if (wordBreakerSpec.use) { return wordBreakerSpec; } else { - throw new Error(`Unknown word breaker: ${wordBreakerSpec}`); + throw new ModelCompilerError(ModelCompilerMessages.Error_UnknownWordBreaker({spec: wordBreakerSpec.toString()})); } } diff --git a/developer/src/kmc-model/src/main.ts b/developer/src/kmc-model/src/main.ts index 5b1bb7fe7fb9c50671581416f83c70e8d4ae3f8b..3d808057d5981cef795bd59545735b1ef6cc1e31 100644 --- a/developer/src/kmc-model/src/main.ts +++ b/developer/src/kmc-model/src/main.ts @@ -1,22 +1,36 @@ +import { CompilerCallbacks } from '@keymanapp/common-types'; import * as fs from 'fs'; import * as path from 'path'; import ts from 'typescript'; +import { setCompilerCallbacks } from './compiler-callbacks.js'; import LexicalModelCompiler from './lexical-model-compiler.js'; import { LexicalModelSource } from './lexical-model.js'; +import { ModelCompilerError, ModelCompilerMessageContext, ModelCompilerMessages } from './model-compiler-errors.js'; /** * Compiles a model.ts file, using paths relative to its location. * * @param filename path to model.ts source. - * @return model source code + * @return model source code, or null on error */ -export function compileModel(filename: string): string { - let modelSource = loadFromFilename(filename); - let containingDirectory = path.dirname(filename); +export function compileModel(filename: string, callbacks: CompilerCallbacks): string { + setCompilerCallbacks(callbacks); - return (new LexicalModelCompiler) - .generateLexicalModelCode('', modelSource, containingDirectory); + try { + let modelSource = loadFromFilename(filename, callbacks); + let containingDirectory = path.dirname(filename); + + return (new LexicalModelCompiler) + .generateLexicalModelCode('', modelSource, containingDirectory); + } catch(e) { + callbacks.reportMessage( + e instanceof ModelCompilerError + ? e.event + : ModelCompilerMessages.Fatal_UnexpectedException({e:e}) + ); + } + return null; } /** @@ -33,15 +47,17 @@ interface ES2015Module { * * @param filename path to the model source file. */ -export function loadFromFilename(filename: string): LexicalModelSource { +export function loadFromFilename(filename: string, callbacks: CompilerCallbacks): LexicalModelSource { + setCompilerCallbacks(callbacks); + let sourceCode = fs.readFileSync(filename, 'utf8'); // Compile the module to JavaScript code. // NOTE: transpile module does a very simple TS to JS compilation. // It DOES NOT check for types! let compilationOutput = ts.transpile(sourceCode, { // Our runtime only supports ES3 with Node/CommonJS modules on Android 5.0. - // When we drop Android 5.0 support, we can update this to a `ScriptTarget` - // matrix against target version of Keyman, here and in + // When we drop Android 5.0 support, we can update this to a `ScriptTarget` + // matrix against target version of Keyman, here and in // lexical-model-compiler.ts. target: ts.ScriptTarget.ES3, module: ts.ModuleKind.CommonJS, @@ -55,7 +71,8 @@ export function loadFromFilename(filename: string): LexicalModelSource { module(moduleExports); if (!moduleExports['__esModule'] || !moduleExports['default']) { - throw new Error(`Model source '${filename}' does have a default export. Did you remember to write \`export default source;\`?`); + ModelCompilerMessageContext.filename = filename; + throw new ModelCompilerError(ModelCompilerMessages.Error_NoDefaultExport()); } return moduleExports['default'] as LexicalModelSource; diff --git a/developer/src/kmc-model/src/model-compiler-errors.ts b/developer/src/kmc-model/src/model-compiler-errors.ts index 20aa02d8e802a76e8f831f670f5d99ec8fb8d134..f7062da44e2cd2a9efff487d24a3c681d1503f5d 100644 --- a/developer/src/kmc-model/src/model-compiler-errors.ts +++ b/developer/src/kmc-model/src/model-compiler-errors.ts @@ -1,203 +1,74 @@ -// TODO: merge with kmc keyboard-compiler errors -/** - * Log levels. - * - * Note: Currently, this acts like a bit set, where the upper 4 bits of an - * unsigned 16 bit value are the log level flags. - * - * Warning: even though these look like bitmasks, these flags may not be - * combined. - */ -export enum LogLevel { - CERR_FATAL = 0x8000, - CERR_ERROR = 0x4000, - CERR_WARNING = 0x2000, - CERR_HINT = 0x1000 -}; - -/** - * Error codes. Use these when logging messages. - * - * Extends https://github.com/keymanapp/keyman/blob/99db3c0d2448f448242e6397f9d72e9a7ccee4b9/windows/src/global/inc/Comperr.h - */ -export enum KeymanCompilerError { - CERR_LEXICAL_MODEL_MIN = 0x0800, - CERR_LEXICAL_MODEL_MAX = 0x08FF, - - - CERR_FATAL_LM = LogLevel.CERR_FATAL | CERR_LEXICAL_MODEL_MIN, - /* Place all fatal LM compiler errors here! */ - - CERR_ERROR_LM = LogLevel.CERR_ERROR | CERR_LEXICAL_MODEL_MIN, - /* Place all recoverable LM compiler errors here! */ - - CERR_WARN_LM = LogLevel.CERR_WARNING | CERR_LEXICAL_MODEL_MIN, - /* Place all LM compiler warnings here! */ - CWARN_MixedNormalizationForms = 0x2801, /* CERR_WARN_LM + 1 */ - CWARN_DuplicateWordInSameFile = 0x2802, /* CERR_WARN_LM + 2 */ - - CERR_HINT_LM = LogLevel.CERR_HINT | CERR_LEXICAL_MODEL_MIN, - /* Place all LM compiler hints here! */ - - /* Errors that are not specific to the lexical model compiler, from comperr.h: */ - CWARN_TooManyErrorsOrWarnings = 0x20A7, +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.ModelCompiler; +// const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevHint = CompilerErrorSeverity.Hint | Namespace; +const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +const m = (code: number, message: string) : CompilerEvent => { return { + line: ModelCompilerMessageContext.line, + filename: ModelCompilerMessageContext.filename, + code, + message +} }; + +export class ModelCompilerMessageContext { + // Context added to all messages + static line: number; + static filename: string; } -/** - * Human-readable titles for the various log levels. - * - * Taken from https://github.com/keymanapp/keyman/blob/d83cfffe511ce65b781f919e89e3693146844849/windows/src/developer/TIKE/project/Keyman.Developer.System.Project.ProjectLog.pas#L39-L46 - */ -const LOG_LEVEL_TITLE: {[level in LogLevel]: string} = { - [LogLevel.CERR_HINT]: 'Hint', - [LogLevel.CERR_WARNING]: 'Warning', - [LogLevel.CERR_ERROR]: 'Error', - [LogLevel.CERR_FATAL]: 'Fatal Error', -}; +export class ModelCompilerMessages { -/** - * How many errors or warnings are too many! - */ -export const MAX_MESSAGES = 100; + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, + `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); + static FATAL_UnexpectedException = SevFatal | 0x0001; -/** - * Direct where log messages go. - */ -let _logHandler: (log: LogMessage) => void = printLogs; + static Warn_MixedNormalizationForms = (o:{wordform: string}) => m(this.WARN_MixedNormalizationForms, + `“${o.wordform}” is not in Unicode NFC. Automatically converting to NFC.`); + static WARN_MixedNormalizationForms = SevWarn | 0x0002; -/** - * How many logs or warnings have been witnessed so far. - */ -let _messagesSeen = 0; + static Warn_DuplicateWordInSameFile = (o:{wordform: string}) => m(this.WARN_DuplicateWordInSameFile, + `duplicate word “${o.wordform}” found in same file; summing counts`); + static WARN_DuplicateWordInSameFile = SevWarn | 0x0003; -/** - * Logs compiler messages (warnings, errors, logs). - * - * @param code Error code - * @param message A helpful message! - * @param source [optional] the filename/line number in the source that induced this error - * - * @see https://github.com/keymanapp/keyman/blob/99db3c0d2448f448242e6397f9d72e9a7ccee4b9/windows/src/developer/TIKE/project/Keyman.Developer.System.Project.ProjectLog.pas#L60-L77 - */ -export function log(code: KeymanCompilerError, message: string, source?: FilenameAndLineNo) { - // Ignore the request if there are too many messages - if (_messagesSeen > MAX_MESSAGES) - return; + static Error_UnimplementedModelFormat = (o:{format: string}) => m(this.ERROR_UnimplementedModelFormat, + `Unimplemented model format: ${o.format}`); + static ERROR_UnimplementedModelFormat = SevError | 0x0004; - let logMessage = source - ? new LogMessageFromSource(code, message, source) - : new OrdinaryLogMessage(code, message); + static Error_UnknownModelFormat = (o:{format: string}) => m(this.ERROR_UnknownModelFormat, + `Unimplemented model format: ${o.format}`); + static ERROR_UnknownModelFormat = SevError | 0x0005; - _logHandler(logMessage) - _messagesSeen++; + static Error_NoDefaultExport = () => m(this.ERROR_NoDefaultExport, + `Model source does have a default export. Did you remember to write \`export default source;\`?`); + static ERROR_NoDefaultExport = SevError | 0x0006; - if (_messagesSeen > MAX_MESSAGES) { - _logHandler(new OrdinaryLogMessage( - KeymanCompilerError.CWARN_TooManyErrorsOrWarnings, - `More than ${MAX_MESSAGES} warnings or errors received; suppressing further messages` - )); - } -} + static Error_SearchTermToKeyMustBeExplicitlySpecified = () => m(this.ERROR_SearchTermToKeyMustBeExplicitlySpecified, + "searchTermToKey must be explicitly specified"); + static ERROR_SearchTermToKeyMustBeExplicitlySpecified = SevError | 0x0007; -/** - * Override where log messages go. - * - * @param fn The desired log message handler. - */ -export function redirectLogMessagesTo(fn: (log: LogMessage) => void) { - _logHandler = fn; -} + static Error_UTF16BEUnsupported = () => m(this.ERROR_UTF16BEUnsupported, 'UTF-16BE is unsupported'); + static ERROR_UTF16BEUnsupported = SevError | 0x0008; -/** - * Reset the log message handler to the default. - */ -export function resetLogMessageHandler() { - _logHandler = printLogs; - _messagesSeen = 0; -} + static Error_UnknownWordBreaker = (o:{spec:string}) => m(this.ERROR_UnknownWordBreaker, + `Unknown word breaker: ${o.spec}`); + static ERROR_UnknownWordBreaker = SevError | 0x0009; -/** - * Prints log messages to stdout. The default log action. - */ -export function printLogs(log: LogMessage): void { - console.log(log.format()); -} - -/** - * Duct tapes together a filename and a line number of a log. - */ -interface FilenameAndLineNo { - readonly filename: string; - readonly lineno: number; -} - -/** - * A log message that knows how to format itself. - */ -export interface LogMessage { - readonly code: KeymanCompilerError; - readonly level: LogLevel; - readonly message: string; - - format(): string; -} - -/** - * Concrete implementation of the log message. - */ -class OrdinaryLogMessage implements LogMessage { - readonly code: KeymanCompilerError; - readonly message: string; - - constructor(code: KeymanCompilerError, message: string) { - this.code = code; - this.message = message; - } - - get level(): LogLevel { - return this.code & 0xF000; - } - - determineLogLevelTitle(): string { - return LOG_LEVEL_TITLE[this.level] || ''; - } - - format(): string { - let prefix = this.determineLogLevelTitle(); - if (prefix) - prefix = `${prefix}: `; - - return `${prefix}${h(this.code)} ${this.message}` - } -} + static Error_UnsupportedScriptOverride = (o:{option:string}) => m(this.ERROR_UnsupportedScriptOverride, + `Unsupported script override: ${o.option}`); + static ERROR_UnsupportedScriptOverride = SevError | 0x000A; +}; /** - * A log message with a filename and line number. + * A ModelCompilerError should be thrown when an unrecoverable error occurs that + * would block further compilation. It will be caught in the top-most compiler + * API endpoint and converted into a callback message. */ -class LogMessageFromSource extends OrdinaryLogMessage { - readonly filename: string; - readonly lineno: number; - - constructor(code: KeymanCompilerError, message: string, source: FilenameAndLineNo) { - super(code, message); - this.filename = source.filename; - this.lineno = source.lineno; - } - - format(): string { - let originalMessage = super.format(); - return `${this.filename} (${this.lineno}): ${originalMessage}`; +export class ModelCompilerError extends Error { + constructor(public event: CompilerEvent) { + super(event.message); } } - -/** - * Format a number as a zero-padded 4 digit hexadecimal. - */ -function h(n: number) { - let formatted = n.toString(16).toUpperCase(); - if (formatted.length < 4) { - formatted = '0'.repeat(4 - formatted.length); - } - - return formatted; -} \ No newline at end of file diff --git a/developer/src/kmc-model/src/script-overrides-decorator.ts b/developer/src/kmc-model/src/script-overrides-decorator.ts index 4413a34d261a78597464f3c9f5971d6b0c9600e0..3723a56dae325d69c4a7cf5b5168d4fa55e750f7 100644 --- a/developer/src/kmc-model/src/script-overrides-decorator.ts +++ b/developer/src/kmc-model/src/script-overrides-decorator.ts @@ -1,8 +1,9 @@ import { OverrideScriptDefaults } from "./lexical-model.js"; +import { ModelCompilerError, ModelCompilerMessages } from "./model-compiler-errors.js"; export function decorateWithScriptOverrides(breaker: WordBreakingFunction, option: OverrideScriptDefaults) { if (option !== 'break-words-at-spaces') { - throw new Error(`Unsupported script override: ${option}`) + throw new ModelCompilerError(ModelCompilerMessages.Error_UnsupportedScriptOverride({option})); } /** diff --git a/developer/src/kmc-model/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps b/developer/src/kmc-model/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps index 571e710c9491c2485ea2714c7ef1fd6a01a833aa..5354ba7980af9c9f2efc5cb328d60520f42d8ac2 100644 --- a/developer/src/kmc-model/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps +++ b/developer/src/kmc-model/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps @@ -4,9 +4,6 @@ 12.0.1500.0 12.0 - - - SENĆOŦEN (Saanich Dialect) Lexical Model © 2019 National Research Council Canada @@ -25,7 +22,6 @@ SENĆOŦEN dictionary example.qaa.sencoten - 1.0.3 North Straits Salish SENĆOŦEN diff --git a/developer/src/kmc-model/test/helpers/index.ts b/developer/src/kmc-model/test/helpers/index.ts index cc6affa69309b8154b8b152011f1fd696c0556d7..e7d274f571290cceb3b9048df53d0ca82c718ac8 100644 --- a/developer/src/kmc-model/test/helpers/index.ts +++ b/developer/src/kmc-model/test/helpers/index.ts @@ -6,7 +6,6 @@ import * as path from 'path'; import { fileURLToPath } from 'url'; import {assert} from 'chai'; -import {LogMessage, KeymanCompilerError, redirectLogMessagesTo, resetLogMessageHandler, LogLevel} from '../../src/model-compiler-errors.js'; export interface CompilationResult { hasSyntaxError: boolean; @@ -121,64 +120,6 @@ export function compileModelSourceCode(code: string) { }; } -/** - * Enables one to query log messages after they have been logged. - */ -export class LogHoarder { - private messages: LogMessage[] = []; - - /** - * Get rid of all log messages seen. - */ - clear() { - this.messages = []; - } - - /** - * Hoards a log message for later perusal. - */ - handleLog(log: LogMessage) { - this.messages.push(log); - } - - /** - * Has an error message with this code been witnessed? - */ - hasSeenCode(code: KeymanCompilerError): boolean { - return Boolean(this.messages.find(log => log.code === code)); - } - - /** - * Have any warnings been logged? - */ - hasSeenWarnings(): boolean { - return this.messages - .filter(log => log.level === LogLevel.CERR_WARNING) - .length > 0; - } - - /** - * Overrides the global log handler, allowing one to browse log messages - * later. - * - * Remember to uninstall the log handler afterwards! - */ - install(): this { - redirectLogMessagesTo(this.handleLog.bind(this)); - return this; - } - - /** - * Return the log message handler to its default. - * - * Note: You MUST uninstall the hoarder after use! - * It's recommended you put this in an afterEach() callback. - */ - uninstall() { - resetLogMessageHandler() - } -} - type ModuleType = (a: LMLayerWorker, b: ModelsNamespace, c: WordBreakersNamespace) => any; interface LMLayerWorker { loadModel(model: object): void; diff --git a/developer/src/kmc-model/test/test-compile-model.ts b/developer/src/kmc-model/test/test-compile-model.ts index d98f0e866801b84af2139bddcd7698eac521a458..c24a857e71a422e3a2a1070ca745bca29e932b3a 100644 --- a/developer/src/kmc-model/test/test-compile-model.ts +++ b/developer/src/kmc-model/test/test-compile-model.ts @@ -3,8 +3,11 @@ import {assert} from 'chai'; import {compileModel} from '../src/main.js'; import {makePathToFixture, compileModelSourceCode, CompilationResult} from './helpers/index.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; describe('compileModel', function () { + let callbacks = new TestCompilerCallbacks(); + // Try to compile ALL of the correct models. const MODELS = [ 'example.qaa.sencoten', @@ -19,7 +22,7 @@ describe('compileModel', function () { let modelPath = makePathToFixture(modelID, `${modelID}.model.ts`); it(`should compile ${modelID}`, function () { - let code = compileModel(modelPath); + let code = compileModel(modelPath, callbacks); let r: unknown; assert.doesNotThrow(() => { r = compileModelSourceCode(code); diff --git a/developer/src/kmc-model/test/test-compile-trie.ts b/developer/src/kmc-model/test/test-compile-trie.ts index d02b24eddf070c74af6966a60059a605a8db4697..9a2c744a1987b6b1ff1a51a29a9005e2de6969e7 100644 --- a/developer/src/kmc-model/test/test-compile-trie.ts +++ b/developer/src/kmc-model/test/test-compile-trie.ts @@ -4,6 +4,7 @@ import 'mocha'; import {makePathToFixture, compileModelSourceCode} from './helpers/index.js'; import { createTrieDataStructure } from '../src/build-trie.js'; +import { ModelCompilerError } from '../src/model-compiler-errors.js'; describe('LexicalModelCompiler', function () { describe('#generateLexicalModelCode', function () { @@ -131,7 +132,7 @@ describe('createTrieDataStructure()', function () { it('must be given an explicit searchTermToKey function', function () { assert.throws(function () { createTrieDataStructure([WORDLIST_FILENAME]); - }, TypeError) + }, ModelCompilerError) }); it('uses the provided searchTermToKey function', function () { diff --git a/developer/src/kmc-model/test/test-error-logger.ts b/developer/src/kmc-model/test/test-error-logger.ts deleted file mode 100644 index fa57d3df57874d2aec4b0a4f00d80792ac0397d8..0000000000000000000000000000000000000000 --- a/developer/src/kmc-model/test/test-error-logger.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { MAX_MESSAGES, KeymanCompilerError, log } from "../src/model-compiler-errors.js"; -import { LogHoarder } from "./helpers/index.js"; -import { assert } from "chai"; - -describe('error logger', function () { - beforeEach(function () { - this.logHoarder = (new LogHoarder).install() - }) - - afterEach(function () { - this.logHoarder.uninstall(); - delete this.logHoarder; - }) - - it('should stop logging messages **after** a maximum', function () { - for (let i = 0; i < MAX_MESSAGES; i++) { - log(KeymanCompilerError.CWARN_DuplicateWordInSameFile, "fake error"); - } - - // We've logged *just enough messages. This error should not be found: - assert.isFalse(this.logHoarder.hasSeenCode( - KeymanCompilerError.CWARN_TooManyErrorsOrWarnings - )); - - // Log just one too many: - log(KeymanCompilerError.CWARN_DuplicateWordInSameFile, "fake error"); - - assert.isTrue(this.logHoarder.hasSeenCode( - KeymanCompilerError.CWARN_TooManyErrorsOrWarnings - )); - - // Log a DIFFERENT error -- it should not appear in the log - log(KeymanCompilerError.CWARN_MixedNormalizationForms, "fake error"); - assert.isFalse(this.logHoarder.hasSeenCode( - KeymanCompilerError.CWARN_MixedNormalizationForms - )); - }) -}) \ No newline at end of file diff --git a/developer/src/kmc-model/test/test-messages.ts b/developer/src/kmc-model/test/test-messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..51394313040cd07c0bfff2777d5ee8a9671ba5f6 --- /dev/null +++ b/developer/src/kmc-model/test/test-messages.ts @@ -0,0 +1,9 @@ +import 'mocha'; +import { ModelCompilerMessages } from '../src/model-compiler-errors.js'; +import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; + +describe('ModelCompilerMessages', function () { + it('should have a valid ModelCompilerMessages object', function() { + return verifyCompilerMessagesObject(ModelCompilerMessages); + }); +}); diff --git a/developer/src/kmc-model/test/test-parse-wordlist.ts b/developer/src/kmc-model/test/test-parse-wordlist.ts index 6f5be70ca7ec33ca54fc4e09abebf02b1ec28afe..ab610f5e802c832ef9b8a606c6aef8c500ff09a0 100644 --- a/developer/src/kmc-model/test/test-parse-wordlist.ts +++ b/developer/src/kmc-model/test/test-parse-wordlist.ts @@ -1,8 +1,10 @@ import {parseWordListFromContents, parseWordListFromFilename, WordList} from '../src/build-trie.js'; import {assert} from 'chai'; import 'mocha'; -import { makePathToFixture, LogHoarder } from './helpers/index.js'; -import { KeymanCompilerError } from '../src/model-compiler-errors.js'; +import { makePathToFixture } from './helpers/index.js'; +import { ModelCompilerMessages } from '../src/model-compiler-errors.js'; +import { setCompilerCallbacks } from '../src/compiler-callbacks.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; const BOM = '\ufeff'; const SENCOTEN_WORDLIST = { @@ -18,15 +20,18 @@ const SENCOTEN_WORDLIST = { 'I': 1884 }; + describe('parsing a word list', function () { + let testCallbacks = new TestCompilerCallbacks(); + beforeEach(function () { - this.logHoarder = (new LogHoarder).install() - }) + testCallbacks.clear(); + setCompilerCallbacks(testCallbacks); + }); afterEach(function () { - this.logHoarder.uninstall(); - delete this.logHoarder; - }) + setCompilerCallbacks(null); + }); it('should remove the UTF-8 byte order mark from files', function () { let word = 'hello'; @@ -38,12 +43,12 @@ describe('parsing a word list', function () { let withoutBOM: WordList = {}; parseWordListFromContents(withoutBOM, file); assert.deepEqual(withoutBOM, expected, "expected regular file to parse properly"); - assert.isFalse(this.logHoarder.hasSeenWarnings()); + assert.isEmpty(testCallbacks.messages); let withBOM: WordList = {}; parseWordListFromContents(withBOM, `${BOM}${file}`) assert.deepEqual(withBOM, expected, "expected BOM to be ignored"); - assert.isFalse(this.logHoarder.hasSeenWarnings()); + assert.isEmpty(testCallbacks.messages); }); it('should read word lists in UTF-8', function () { @@ -53,7 +58,7 @@ describe('parsing a word list', function () { parseWordListFromFilename(wordlist, filename); assert.deepEqual(wordlist, SENCOTEN_WORDLIST); - assert.isFalse(this.logHoarder.hasSeenWarnings()); + assert.isEmpty(testCallbacks.messages); }); it('should read word lists in UTF-16 little-endian (with BOM)', function () { @@ -64,7 +69,7 @@ describe('parsing a word list', function () { parseWordListFromFilename(wordlist, filename); assert.deepEqual(wordlist, SENCOTEN_WORDLIST); - assert.isFalse(this.logHoarder.hasSeenWarnings()); + assert.isEmpty(testCallbacks.messages); }); it('should NOT read word lists in UTF-16 big-endian (with BOM)', function () { @@ -101,22 +106,22 @@ describe('parsing a word list', function () { assert.deepEqual(repeatedWords, expected); - assert.isTrue(this.logHoarder.hasSeenWarnings()); + assert.lengthOf(testCallbacks.messages, 4); // hello has been seen multiple times: - assert.isTrue(this.logHoarder.hasSeenCode(KeymanCompilerError.CWARN_DuplicateWordInSameFile)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_DuplicateWordInSameFile)); // helló and hello + U+0301 have both been seen: - assert.isTrue(this.logHoarder.hasSeenCode(KeymanCompilerError.CWARN_MixedNormalizationForms)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_MixedNormalizationForms)); // Let's parse another file: - this.logHoarder.clear(); + testCallbacks.clear(); // Now, parse a DIFFERENT file, but with an NFD entry. parseWordListFromContents(repeatedWords, "hello\u0301\t5\n"); - assert.isTrue(this.logHoarder.hasSeenWarnings()) + assert.lengthOf(testCallbacks.messages, 1); // hello + U+0301 (NFD) has been seen, but... - assert.isTrue(this.logHoarder.hasSeenCode(KeymanCompilerError.CWARN_MixedNormalizationForms)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_MixedNormalizationForms)); // BUT! We have not seen a duplicate **within the same file** - assert.isFalse(this.logHoarder.hasSeenCode(KeymanCompilerError.CWARN_DuplicateWordInSameFile)); + assert.isFalse(testCallbacks.hasMessage(ModelCompilerMessages.WARN_DuplicateWordInSameFile)); assert.deepEqual(repeatedWords, { hello: expected['hello'], diff --git a/developer/src/kmc-model/test/tsconfig.json b/developer/src/kmc-model/test/tsconfig.json index fd7f672037a6b84733fdd01a4ca2ea6ebeb365cc..3902bed57f0fe4fa4219b67f663284aef74b76ea 100644 --- a/developer/src/kmc-model/test/tsconfig.json +++ b/developer/src/kmc-model/test/tsconfig.json @@ -9,6 +9,9 @@ "moduleResolution": "node16", "allowSyntheticDefaultImports": true, "baseUrl": ".", + "paths": { + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], + }, }, "include": [ "**/test-*.ts", @@ -18,5 +21,6 @@ ], "references": [ { "path": "../" }, + { "path": "../../common/web/test-helpers/" }, ] } \ No newline at end of file diff --git a/developer/src/kmc-model/tsconfig.json b/developer/src/kmc-model/tsconfig.json index e48ec745f9dca9dcd2b591e375f9fcc701190b82..8ad34413fa273854677e75ad7d41264a6434e234 100644 --- a/developer/src/kmc-model/tsconfig.json +++ b/developer/src/kmc-model/tsconfig.json @@ -13,5 +13,6 @@ "references": [ { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, { "path": "../../../common/models/types" }, + { "path": "../../../common/web/types" }, ] } diff --git a/developer/src/kmc-package/.eslintrc.cjs b/developer/src/kmc-package/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..09038ae9291d0ce786656be735b38cff1049976d --- /dev/null +++ b/developer/src/kmc-package/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + ignorePatterns: ["test/fixtures/**/*"], + overrides: [ + { + files:"src/**/*.ts", + extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + } + ], + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc-package/Makefile b/developer/src/kmc-package/Makefile index 02bf07dc025634e44b69d0784c7259050b461b7c..0a5ba6a1c10107a854a1170ce9ca317758f449b8 100644 --- a/developer/src/kmc-package/Makefile +++ b/developer/src/kmc-package/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-package/build.sh b/developer/src/kmc-package/build.sh index c17363729cb5b9ecc12c0463341f8bd447bb5b8e..3206f60fda34cef55f689d2206c3229743836772 100755 --- a/developer/src/kmc-package/build.sh +++ b/developer/src/kmc-package/build.sh @@ -18,15 +18,17 @@ cd "$THIS_SCRIPT_PATH" builder_describe "Build Keyman kmc Package Compiler module" \ "@/common/web/keyman-version" \ + "@/developer/src/common/web/test-helpers" \ "configure" \ "build" \ "clean" \ "test" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ configure /node_modules \ - build /developer/src/kmc-package/build/src/kmp-compiler.js + build /developer/src/kmc-package/build/src/main.js builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- @@ -60,7 +62,11 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/developer/src/kmc-package/package.json b/developer/src/kmc-package/package.json index 8a164f021f1b09bbb8d2df5b53f6b98bf41a580a..1595dc21ecc47d6842584e31657a22c775f7514d 100644 --- a/developer/src/kmc-package/package.json +++ b/developer/src/kmc-package/package.json @@ -8,12 +8,15 @@ ], "type": "module", "exports": { - ".": "./build/src/kmp-compiler.js", - "./kmp-json-file": "./build/src/kmp-json-file.js" + ".": "./build/src/main.js" }, + "files": [ + "/build/src/" + ], "scripts": { "build": "tsc -b", - "test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "lint": "eslint .", + "test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", "coverage": "npm test", "prepublishOnly": "npm run build" }, @@ -27,7 +30,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "dependencies": { - "jszip": "^3.7.0" + "jszip": "^3.7.0", + "@keymanapp/common-types": "*" }, "devDependencies": { "@types/chai": "^4.1.7", @@ -38,7 +42,8 @@ "chalk": "^2.4.2", "mocha": "^8.4.0", "ts-node": "^9.1.1", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "@keymanapp/developer-test-helpers": "*" }, "mocha": { "spec": "build/test/**/test-*.js", @@ -46,6 +51,18 @@ "source-map-support/register" ] }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "test/", + "src/kmp-json-file.ts", + "src/kps-file.ts" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/keymanapp/keyman.git" diff --git a/developer/src/kmc-package/src/compiler/keyman-targets.ts b/developer/src/kmc-package/src/compiler/keyman-targets.ts new file mode 100644 index 0000000000000000000000000000000000000000..14fc6572c4931446c8753bbbe981d04db31ca10e --- /dev/null +++ b/developer/src/kmc-package/src/compiler/keyman-targets.ts @@ -0,0 +1,53 @@ + +// +// This mirrors UKeymanTargets.pas +// + +export enum + KeymanTarget { + any = 'any', + windows = 'windows', macosx = 'macosx', linux = 'linux', + web = 'web', iphone = 'iphone', ipad = 'ipad', androidphone = 'androidphone', androidtablet = 'androidtablet', + mobile = 'mobile', desktop = 'desktop', tablet = 'tablet' + }; + +export const + // Note: if these change, update the copied values in KeyboardParser.pas accordingly + AllKeymanTargets: KeymanTarget[] = [ + KeymanTarget.any, + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, + KeymanTarget.web, KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.desktop, KeymanTarget.tablet + ], + + TouchKeymanTargets: KeymanTarget[] = [ + KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.tablet + ], + + // Compile to .kmx + KMXKeymanTargets: KeymanTarget[] = [ + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, KeymanTarget.desktop + ], + + // Compile to .js + KMWKeymanTargets: KeymanTarget[] = [ + KeymanTarget.web, KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.tablet + ], + + // Supports .kvks + KeymanTargetsUsingKVK: KeymanTarget[] = [ + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, KeymanTarget.desktop, KeymanTarget.web + ], + + // Friendly names for targets, e.g. for documentation + + SKeymanTargetNames: { [index: string]: string } = { + [KeymanTarget.any]: 'All', + [KeymanTarget.windows]: 'Windows', [KeymanTarget.macosx]: 'macOS', [KeymanTarget.linux]: 'Linux', + [KeymanTarget.web]: 'Web', [KeymanTarget.iphone]: 'iPhone', [KeymanTarget.ipad]: 'iPad', + [KeymanTarget.androidphone]: 'Android phone', [KeymanTarget.androidtablet]: 'Android tablet', + [KeymanTarget.mobile]: 'Mobile devices', [KeymanTarget.desktop]: 'Desktop devices', + [KeymanTarget.tablet]: 'Tablet devices' + }; diff --git a/developer/src/kmc-package/src/kmp-compiler.ts b/developer/src/kmc-package/src/compiler/kmp-compiler.ts similarity index 53% rename from developer/src/kmc-package/src/kmp-compiler.ts rename to developer/src/kmc-package/src/compiler/kmp-compiler.ts index 718403d18b9bc40d2af2942b9ed6614208a7425c..b660641df9c564972064f4ed644433dd793c3f65 100644 --- a/developer/src/kmc-package/src/kmp-compiler.ts +++ b/developer/src/kmc-package/src/compiler/kmp-compiler.ts @@ -1,33 +1,34 @@ -import * as fs from 'fs'; -import * as path from 'path'; import * as xml2js from 'xml2js'; import JSZip from 'jszip'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; -import type { KpsFile, KpsFileContentFile, KpsFileInfo, KpsFileKeyboard, KpsFileLanguage, KpsFileLexicalModel, KpsFileOptions, KpsPackage } from './kps-file.js'; -import type { KmpJsonFile, KmpJsonFileInfo, KmpJsonFileLanguage, KmpJsonFileOptions } from './kmp-json-file.js'; - -export { type KmpJsonFile } from './kmp-json-file.js'; +import { CompilerCallbacks, KvkFile } from '@keymanapp/common-types'; +import { CompilerMessages } from './messages.js'; +import { KmpJsonFile, KpsFile } from '@keymanapp/common-types'; +import { PackageVersionValidation } from './package-version-validation.js'; const FILEVERSION_KMP_JSON = '12.0'; -export default class KmpCompiler { +export class KmpCompiler { - public transformKpsToKmpObject(kpsString: string, kpsPath: string): KmpJsonFile { + constructor(private callbacks: CompilerCallbacks) { + } + public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile.KmpJsonFile { // Load the KPS data from XML as JS structured data. + const data = this.callbacks.fs.readFileSync(kpsFilename, 'utf-8'); - let kpsPackage = (() => { - let a: KpsPackage; + const kpsPackage = (() => { + let a: KpsFile.KpsPackage; let parser = new xml2js.Parser({ tagNameProcessors: [xml2js.processors.firstCharLowerCase], explicitArray: false }); - parser.parseString(kpsString, (e: unknown, r: unknown) => { a = r as KpsPackage }); + parser.parseString(data, (e: unknown, r: unknown) => { a = r as KpsFile.KpsPackage }); return a; })(); - let kps: KpsFile = kpsPackage.package; + let kps: KpsFile.KpsFile = kpsPackage.package; // // To convert to kmp.json, we need to: @@ -42,7 +43,7 @@ export default class KmpCompiler { // Start to construct the kmp.json file from the .kps file - let kmp: KmpJsonFile = { + let kmp: KmpJsonFile.KmpJsonFile = { system: { fileVersion: FILEVERSION_KMP_JSON, keymanDeveloperVersion: KEYMAN_VERSION.VERSION @@ -54,16 +55,18 @@ export default class KmpCompiler { // Fill in additional fields // - let keys: [keyof KpsFileOptions, keyof KmpJsonFileOptions][] = [ + let keys: [keyof KpsFile.KpsFileOptions, keyof KmpJsonFile.KmpJsonFileOptions][] = [ ['executeProgram','executeProgram'], ['graphicFile', 'graphicFile'], ['msiFileName','msiFilename'], ['msiOptions', 'msiOptions'], ['readMeFile', 'readmeFile'] ]; - for (let [src,dst] of keys) { - if (kps.options[src]) { - kmp.options[dst] = kps.options[src]; + if(kps.options) { + for (let [src,dst] of keys) { + if (kps.options[src]) { + kmp.options[dst] = kps.options[src]; + } } } @@ -75,20 +78,12 @@ export default class KmpCompiler { kmp.info = this.kpsInfoToKmpInfo(kps.info); } - // FollowKeyboardVersion support - - if(kps.options?.followKeyboardVersion !== undefined) { - kmp.info.version = { - description: this.extractKeyboardVersionFromKmx(kpsPath, kps) - }; - } - // // Add file metadata // if(kps.files && kps.files.file) { - kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFileContentFile) => { + kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFile.KpsFileContentFile) => { return { name: file.name, description: file.description, @@ -110,16 +105,16 @@ export default class KmpCompiler { // if(kps.keyboards && kps.keyboards.keyboard) { - kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFileKeyboard) => { - return { - displayFont: keyboard.displayFont ? path.basename(keyboard.displayFont.replaceAll('\\', '/')) : undefined, - oskFont: keyboard.oSKFont ? path.basename(keyboard.oSKFont.replaceAll('\\', '/')) : undefined, - name:keyboard.name, - id:keyboard.iD, - version:keyboard.version, - languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFileLanguage[]) - }; - }); + kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFile.KpsFileKeyboard) => ({ + displayFont: keyboard.displayFont ? this.callbacks.path.basename(keyboard.displayFont) : undefined, + oskFont: keyboard.oSKFont ? this.callbacks.path.basename(keyboard.oSKFont) : undefined, + name:keyboard.name, + id:keyboard.iD, + version:keyboard.version, + languages: keyboard.languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[]) : + [] + })); } // @@ -127,9 +122,22 @@ export default class KmpCompiler { // if(kps.lexicalModels && kps.lexicalModels.lexicalModel) { - kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFileLexicalModel) => { - return { name:model.name, id:model.iD, languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFileLanguage[]) } - }); + kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFile.KpsFileLexicalModel) => ({ + name:model.name, + id:model.iD, + languages: model.languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFile.KpsFileLanguage[]) : [] + })); + } + + // + // Verify version metadata; doing this in the transform + // while we have access to the .kps metadata, and keeping the + // + + const versionValidator = new PackageVersionValidation(this.callbacks); + if(!versionValidator.validateAndUpdateVersions(kpsFilename, kps, kmp)) { + return null; } // @@ -151,17 +159,17 @@ export default class KmpCompiler { kmp.strings = this.arrayWrap(kps.strings.string); } - kmp = this.stripUndefined(kmp) as KmpJsonFile; + kmp = this.stripUndefined(kmp) as KmpJsonFile.KmpJsonFile; return kmp; } // Helper functions - private kpsInfoToKmpInfo(info: KpsFileInfo): KmpJsonFileInfo { - let ni: KmpJsonFileInfo = {}; + private kpsInfoToKmpInfo(info: KpsFile.KpsFileInfo): KmpJsonFile.KmpJsonFileInfo { + let ni: KmpJsonFile.KmpJsonFileInfo = {}; - const keys: [(keyof KpsFileInfo), (keyof KmpJsonFileInfo)][] = [ + const keys: [(keyof KpsFile.KpsFileInfo), (keyof KmpJsonFile.KmpJsonFileInfo)][] = [ ['author','author'], ['copyright','copyright'], ['name','name'], @@ -186,23 +194,11 @@ export default class KmpCompiler { return [a]; }; - private kpsLanguagesToKmpLanguages(language: KpsFileLanguage[]): KmpJsonFileLanguage[] { + private kpsLanguagesToKmpLanguages(language: KpsFile.KpsFileLanguage[]): KmpJsonFile.KmpJsonFileLanguage[] { return language.map((element) => { return { name: element._, id: element.$.ID } }); }; - private extractKeyboardVersionFromKmx(kpsPath: string, kps: KpsFile) { - // TODO-LDML: #7340 this is incomplete; we need to read from first .kmx - // When we do this, we'll need to update fixtures to have the correct - // version string also - if(!kps.keyboards || !kps.keyboards.keyboard) { - // We don't read from the kps metadata because we want the keyboard - // version data here; - // TODO: currently model files don't support follow-version? - return '0.0.0'; - } - let k: KpsFileKeyboard[] = this.arrayWrap(kps.keyboards.keyboard); - return k?.[0]?.version ?? '0.0.0'; - } + private stripUndefined(o: any) { for(const key in o) { @@ -221,20 +217,19 @@ export default class KmpCompiler { * @param kpsFilename - Filename of the kps, not read, used only for calculating relative paths * @param kmpJsonData - The kmp.json Object */ - public buildKmpFile(kpsFilename: string, kmpJsonData: KmpJsonFile): Promise { + public buildKmpFile(kpsFilename: string, kmpJsonData: KmpJsonFile.KmpJsonFile): Promise { const zip = JSZip(); const kmpJsonFileName = 'kmp.json'; - const basePath = path.dirname(kpsFilename); - // Make a copy of kmpJsonData, as we mutate paths for writing - const data: KmpJsonFile = JSON.parse(JSON.stringify(kmpJsonData)); + const data: KmpJsonFile.KmpJsonFile = JSON.parse(JSON.stringify(kmpJsonData)); if(!data.files) { data.files = []; } - data.files.forEach(function(value) { + let failed = false; + data.files.forEach((value) => { // Get the path of the file let filename = value.name; @@ -243,31 +238,56 @@ export default class KmpCompiler { return; } - if(path.isAbsolute(value.name)) { - // absolute paths are not very cross-platform compatible -- we are going to have trouble - // with path separators and roots - // TODO: emit a warning - } else { - // Transform separators to platform separators -- kps files may use - // either / or \, although older kps files were always \. - if(path.sep == '/') { - filename = filename.replace(/\\/g, '/'); - } else { - filename = filename.replace(/\//g, '\\'); - } - filename = path.resolve(basePath, filename); + if(this.callbacks.path.isAbsolute(filename)) { + // absolute paths are not portable to other computers + this.callbacks.reportMessage(CompilerMessages.Warn_AbsolutePath({filename: filename})); + } + + filename = this.callbacks.resolveFilename(kpsFilename, filename); + const basename = this.callbacks.path.basename(filename); + + if(!this.callbacks.fs.existsSync(filename)) { + this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename: filename})); + failed = true; + return; + } + + let memberFileData; + try { + memberFileData = this.callbacks.loadFile(filename); + } catch(e) { + this.callbacks.reportMessage(CompilerMessages.Error_FileCouldNotBeRead({filename: filename, e: e})); + failed = true; + return; } - const basename = path.basename(filename); - let data = fs.readFileSync(filename); - zip.file(basename, data); + + this.warnIfKvkFileIsNotBinary(filename, memberFileData); + + zip.file(basename, memberFileData); // Remove path data from files before JSON save value.name = basename; }); + if(failed) { + return null; + } + zip.file(kmpJsonFileName, JSON.stringify(data, null, 2)); // Generate kmp file return zip.generateAsync({type: 'binarystring', compression:'DEFLATE'}); } + + /** + * Legacy .kmp compiler would transform xml-format .kvk files into a binary .kvk file; now + * we want that to remain the responsibility of the keyboard compiler, so we'll warn the + * few users who are still doing this + */ + private warnIfKvkFileIsNotBinary(filename: string, data: Buffer) { + // TODO: Buffer is not available on web + if(filename.match(/\.kvk$/) && data.compare(Buffer.from(KvkFile.KVK_HEADER_IDENTIFIER_BYTES), 0, 3, 0, 3) != 0) { + this.callbacks.reportMessage(CompilerMessages.Warn_FileIsNotABinaryKvkFile({filename: filename})); + } + } } diff --git a/developer/src/kmc-package/src/compiler/messages.ts b/developer/src/kmc-package/src/compiler/messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..56727fea9bf61927ca7b2007df1a62103164c25b --- /dev/null +++ b/developer/src/kmc-package/src/compiler/messages.ts @@ -0,0 +1,118 @@ +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.PackageCompiler; +const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevHint = CompilerErrorSeverity.Hint | Namespace; +const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +export class CompilerMessages { + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, + `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); + static FATAL_UnexpectedException = SevFatal | 0x0001; + + static Warn_AbsolutePath = (o:{filename: string}) => m(this.WARN_AbsolutePath, `File ${o.filename} has an absolute path, which is not portable.`); + static WARN_AbsolutePath = SevWarn | 0x0002; + + static Error_FileDoesNotExist = (o:{filename: string}) => m(this.ERROR_FileDoesNotExist, `File ${o.filename} does not exist.`); + static ERROR_FileDoesNotExist = SevError | 0x0003; + + static Error_FileCouldNotBeRead = (o:{filename: string; e: any}) => m(this.ERROR_FileCouldNotBeRead, + `File ${o.filename} could not be read: ${(o.e ?? 'unknown error').toString()}.`); + static ERROR_FileCouldNotBeRead = SevError | 0x0004; + + static Warn_FileIsNotABinaryKvkFile = (o:{filename: string}) => m(this.WARN_FileIsNotABinaryKvkFile, + `File ${o.filename} does not appear to be a valid binary .kvk file; this may be an old package that includes an xml-format .kvk file. `+ + `You must update the package to include the compiled .kvk file in the package.`); + static WARN_FileIsNotABinaryKvkFile = SevWarn | 0x0005; + + static Error_FollowKeyboardVersionNotAllowedForModelPackages = () => m(this.ERROR_FollowKeyboardVersionNotAllowedForModelPackages, + `FollowKeyboardVersion is not allowed in model packages`); + static ERROR_FollowKeyboardVersionNotAllowedForModelPackages = SevError | 0x0006; + + static Error_FollowKeyboardVersionButNoKeyboards = () => m(this.ERROR_FollowKeyboardVersionButNoKeyboards, + `FollowKeyboardVersion is set, but the package contains no keyboards`); + static ERROR_FollowKeyboardVersionButNoKeyboards = SevError | 0x0007; + + static Error_KeyboardContentFileNotFound = (o:{id:string}) => m(this.ERROR_KeyboardContentFileNotFound, + `Keyboard ${o.id} was listed in but a corresponding .kmx file was not found in `); + static ERROR_KeyboardContentFileNotFound = SevError | 0x0008; + + static Error_KeyboardFileNotValid = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotValid, + `Keyboard file ${o.filename} is not a valid .kmx file`); + static ERROR_KeyboardFileNotValid = SevError | 0x0009; + + static Info_KeyboardFileHasNoKeyboardVersion = (o:{filename:string}) => m(this.INFO_KeyboardFileHasNoKeyboardVersion, + `Keyboard file ${o.filename} has no &KeyboardVersion store, using default '0.0'`); + static INFO_KeyboardFileHasNoKeyboardVersion = SevInfo | 0x000A; + + static Error_PackageCannotContainBothModelsAndKeyboards = () => m(this.ERROR_PackageCannotContainBothModelsAndKeyboards, + `The package contains both lexical models and keyboards, which is not permitted.`); + static ERROR_PackageCannotContainBothModelsAndKeyboards = SevError | 0x000B; + + static Warn_PackageShouldNotRepeatLanguages = (o:{resourceType: string, id: string, minimalTag: string, firstTag: string, secondTag: string}) => m(this.WARN_PackageShouldNotRepeatLanguages, + `Two language tags in ${o.resourceType} ${o.id}, '${o.firstTag}' and '${o.secondTag}', reduce to the same minimal tag '${o.minimalTag}'.`); + static WARN_PackageShouldNotRepeatLanguages = SevWarn | 0x000C; + + static Warn_PackageNameDoesNotFollowLexicalModelConventions = (o:{filename: string}) => m(this.WARN_PackageNameDoesNotFollowLexicalModelConventions, + `The package file ${o.filename} does not follow the recommended model filename conventions. The name should be all lower case, `+ + `include only alphanumeric characters and underscore (_), not start with a digit, and should have the structure `+ + `...model.kps.`); + static WARN_PackageNameDoesNotFollowLexicalModelConventions = SevWarn | 0x000D; + + static Warn_PackageNameDoesNotFollowKeyboardConventions = (o:{filename: string}) => m(this.WARN_PackageNameDoesNotFollowKeyboardConventions, + `The package file ${o.filename} does not follow the recommended keyboard filename conventions. The name should be all lower case, `+ + `include only alphanumeric characters and underscore (_), and not start with a digit.`); + static WARN_PackageNameDoesNotFollowKeyboardConventions = SevWarn | 0x000E; + + static Warn_FileInPackageDoesNotFollowFilenameConventions = (o:{filename: string}) => m(this.WARN_FileInPackageDoesNotFollowFilenameConventions, + `The file ${o.filename} does not follow the recommended filename conventions. The extension should be all lower case, `+ + `and the filename should include only alphanumeric characters, -, _, + and .`); + static WARN_FileInPackageDoesNotFollowFilenameConventions = SevWarn | 0x000F; + + static Error_PackageNameCannotBeBlank = () => m(this.ERROR_PackageNameCannotBeBlank, + `Package name cannot be an empty string.`); + static ERROR_PackageNameCannotBeBlank = SevError | 0x0010; + + static Error_KeyboardFileNotFound = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotFound, + `Keyboard file ${o.filename} was not found. Has it been compiled?`); + static ERROR_KeyboardFileNotFound = SevError | 0x0011; + + static Warn_KeyboardVersionsDoNotMatch = (o: {keyboard:string, version:string, firstKeyboard:string, firstVersion:string}) => m(this.WARN_KeyboardVersionsDoNotMatch, + `Keyboard ${o.keyboard} version ${o.version} does not match keyboard ${o.firstKeyboard} version ${o.firstVersion}.`); + static WARN_KeyboardVersionsDoNotMatch = SevWarn | 0x0012; + + static Warn_KeyboardVersionsDoNotMatchPackageVersion = (o: {keyboard:string, keyboardVersion: string, packageVersion: string}) => m(this.WARN_KeyboardVersionsDoNotMatchPackageVersion, + `Keyboard ${o.keyboard} version ${o.keyboardVersion} does not match package version ${o.packageVersion}.`); + static WARN_KeyboardVersionsDoNotMatchPackageVersion = SevWarn | 0x0013; + + static Error_LanguageTagIsNotValid = (o: {resourceType: string, id:string, lang:string, e:any}) => m(this.ERROR_LanguageTagIsNotValid, + `Language tag '${o.lang}' in ${o.resourceType} ${o.id} is invalid.`); + static ERROR_LanguageTagIsNotValid = SevError | 0x0014; + + static Warn_LanguageTagIsNotMinimal = (o: {resourceType: string, id:string, actual:string, expected:string}) => m(this.WARN_LanguageTagIsNotMinimal, + `Language tag '${o.actual}' in ${o.resourceType} ${o.id} is not minimal, and should be '${o.expected}'.`); + static WARN_LanguageTagIsNotMinimal = SevWarn | 0x0015; + + static Error_MustHaveAtLeastOneLanguage = (o:{resourceType:string, id:string}) => m(this.ERROR_MustHaveAtLeastOneLanguage, + `The ${o.resourceType} ${o.id} must have at least one language specified.`); + static ERROR_MustHaveAtLeastOneLanguage = SevError | 0x0016; + + static Warn_RedistFileShouldNotBeInPackage = (o:{filename:string}) => m(this.WARN_RedistFileShouldNotBeInPackage, + `The Keyman system file '${o.filename}' should not be compiled into the package.`); + static WARN_RedistFileShouldNotBeInPackage = SevWarn | 0x0017; + + static Warn_DocFileDangerous = (o:{filename:string}) => m(this.WARN_DocFileDangerous, + `Microsoft Word .doc or .docx files ('${o.filename}') are not portable. You should instead use HTML or PDF format.`); + static WARN_DocFileDangerous = SevWarn | 0x0018; + + static Error_PackageMustContainAModelOrAKeyboard = () => m(this.ERROR_PackageMustContainAModelOrAKeyboard, + `Package must contain a lexical model or a keyboard.`); + static ERROR_PackageMustContainAModelOrAKeyboard = SevError | 0x0019; + + static Warn_JsKeyboardFileIsMissing = (o:{id: string}) => m(this.WARN_JsKeyboardFileIsMissing, + `Keyboard ${o.id} targets touch devices but corresponding ${o.id}.js file is not in the package.`); + static WARN_JsKeyboardFileIsMissing = SevWarn | 0x001A; +} + diff --git a/developer/src/kmc-package/src/compiler/package-validation.ts b/developer/src/kmc-package/src/compiler/package-validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4317a3da012fdbf8838149daf785022552d6e35 --- /dev/null +++ b/developer/src/kmc-package/src/compiler/package-validation.ts @@ -0,0 +1,186 @@ +import { KmpJsonFile, CompilerCallbacks } from '@keymanapp/common-types'; +import { CompilerMessages } from './messages.js'; +import { keymanEngineForWindowsFiles, keymanForWindowsInstallerFiles, keymanForWindowsRedistFiles } from './redist-files.js'; + +// const SLexicalModelExtension = '.model.js'; + +// The keyboard ID SHOULD adhere to this pattern: +const KEYBOARD_ID_PATTERN_PACKAGE = /^[a-z_][a-z0-9_]*\.(kps|kmp)$/; + +// The model ID SHOULD adhere to this pattern: +// author .bcp47 .uniq +const MODEL_ID_PATTERN_PACKAGE = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.(kps|kmp)$/; +// const MODEL_ID_PATTERN_JS = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.js$/; +// const MODEL_ID_PATTERN_TS = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.ts$/; +// const MODEL_ID_PATTERN_PROJECT = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.kpj$/; + +// "Content files" within the package should adhere to these pattern: +const CONTENT_FILE_BASENAME_PATTERN = /^[a-z0-9_+.-]+$/; +const CONTENT_FILE_EXTENSION_PATTERN = /^\.[a-z0-9_]+$/; + +export class PackageValidation { + + constructor(private callbacks: CompilerCallbacks) { + } + + public validate(filename: string, kmpJson: KmpJsonFile.KmpJsonFile) { + if(!this.checkForModelsAndKeyboardsInSamePackage(kmpJson)) { + return false; + } + if(!this.checkKeyboards(filename, kmpJson)) { + return false; + } + if(!this.checkLexicalModels(filename, kmpJson)) { + return false; + } + if(!this.checkContentFiles(kmpJson)) { + return false; + } + if(!this.checkPackageInfo(kmpJson)) { + return false; + } + return true; + } + + private checkForDuplicatedOrNonMinimalLanguages(resourceType: 'keyboard'|'model', id: string, languages: KmpJsonFile.KmpJsonFileLanguage[]): boolean { + let minimalTags: {[tag: string]: string} = {}; + + if(languages.length == 0) { + this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtLeastOneLanguage({resourceType, id})); + return false; + } + + for(let lang of languages) { + let locale; + try { + locale = new Intl.Locale(lang.id); + } catch(e: any) { + this.callbacks.reportMessage(CompilerMessages.Error_LanguageTagIsNotValid({resourceType, id, lang: lang.id, e})); + return false; + } + + const minimalTag = locale.minimize().toString(); + + if(minimalTag.toLowerCase() !== lang.id.toLowerCase()) { + this.callbacks.reportMessage(CompilerMessages.Warn_LanguageTagIsNotMinimal({resourceType, id, actual: lang.id, expected: minimalTag})); + } + + if(minimalTags[minimalTag]) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageShouldNotRepeatLanguages({resourceType, id, minimalTag, firstTag: lang.id, secondTag: minimalTags[minimalTag]})); + } + else { + minimalTags[minimalTag] = lang.id; + } + } + + return true; + } + + private checkForModelsAndKeyboardsInSamePackage(kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(kmpJson.lexicalModels?.length > 0 && kmpJson.keyboards?.length > 0) { + this.callbacks.reportMessage(CompilerMessages.Error_PackageCannotContainBothModelsAndKeyboards()); + return false; + } + + if(!kmpJson.lexicalModels?.length && !kmpJson.keyboards?.length) { + // Note: we require at least 1 keyboard or model in the package. This may + // change in the future if we start to use packages to distribute, e.g. + // localizations or themes. + this.callbacks.reportMessage(CompilerMessages.Error_PackageMustContainAModelOrAKeyboard()); + return false; + } + + + + return true; + } + + private checkLexicalModels(filename: string, kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(!kmpJson.lexicalModels || kmpJson.lexicalModels.length == 0) { + return true; + } + + filename = this.callbacks.path.basename(filename); + + if(!MODEL_ID_PATTERN_PACKAGE.test(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageNameDoesNotFollowLexicalModelConventions({filename})); + } + + for(let model of kmpJson.lexicalModels) { + if(!this.checkForDuplicatedOrNonMinimalLanguages('model', model.id, model.languages)) { + return false; + } + } + + return true; + } + + private checkKeyboards(filename: string, kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(!kmpJson.keyboards || kmpJson.keyboards.length == 0) { + return true; + } + + filename = this.callbacks.path.basename(filename); + + if(!KEYBOARD_ID_PATTERN_PACKAGE.test(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageNameDoesNotFollowKeyboardConventions({filename})); + } + + for(let keyboard of kmpJson.keyboards) { + if(!this.checkForDuplicatedOrNonMinimalLanguages('keyboard', keyboard.id, keyboard.languages)) { + return false; + } + // Note: package-version-validation verifies that there is a corresponding + // content file for each keyboard + } + + return true; + } + + private checkContentFiles(kmpJson: KmpJsonFile.KmpJsonFile): boolean { + for(let file of kmpJson.files) { + if(!this.checkContentFile(file)) { + return false; + } + } + + return true; + } + + private checkContentFile(file: KmpJsonFile.KmpJsonFileContentFile): boolean { + const filename = this.callbacks.path.basename(file.name); + const ext = this.callbacks.path.extname(filename); + const base = filename.substring(0, filename.length-ext.length); + if(!CONTENT_FILE_BASENAME_PATTERN.test(base) || !CONTENT_FILE_EXTENSION_PATTERN.test(ext)) { + this.callbacks.reportMessage(CompilerMessages.Warn_FileInPackageDoesNotFollowFilenameConventions({filename})); + } + + if(!this.checkIfContentFileIsDangerous(file)) { + return false; + } + + return true; + } + + private checkIfContentFileIsDangerous(file: KmpJsonFile.KmpJsonFileContentFile): boolean { + let filename = this.callbacks.path.basename(file.name).toLowerCase(); + if(keymanForWindowsInstallerFiles.includes(filename) || + keymanForWindowsRedistFiles.includes(filename) || + keymanEngineForWindowsFiles.includes(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_RedistFileShouldNotBeInPackage({filename})); + } + if(filename.match(/\.doc(x?)$/)) { + this.callbacks.reportMessage(CompilerMessages.Warn_DocFileDangerous({filename})); + } + return true; + } + + private checkPackageInfo(file: KmpJsonFile.KmpJsonFile) { + if(!file.info || !file.info.name || !file.info.name.description.trim()) { + this.callbacks.reportMessage(CompilerMessages.Error_PackageNameCannotBeBlank()); + return false; + } + + return true; + } +} diff --git a/developer/src/kmc-package/src/compiler/package-version-validation.ts b/developer/src/kmc-package/src/compiler/package-version-validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f8ad4a41c1d3b232ffe3ee244f76f7b347ec96e --- /dev/null +++ b/developer/src/kmc-package/src/compiler/package-version-validation.ts @@ -0,0 +1,185 @@ +import { KmpJsonFile, CompilerCallbacks, KpsFile, KmxFileReader, KMX } from '@keymanapp/common-types'; +import { KeymanTarget, TouchKeymanTargets } from './keyman-targets.js'; +import { CompilerMessages } from './messages.js'; + +// The DEFAULT_VERSION used to be '1.0', but we now use '0.0' to allow +// pre-release 0.x keyboards to be considered later than a keyboard without +// any version metadata at all. +// TODO: move to a better location +const DEFAULT_VERSION = '0.0'; + +export class PackageVersionValidation { + + constructor(private callbacks: CompilerCallbacks) {} + + /** + * Verifies version information in corresponding keyboards and updates kmpJson + * metadata as the version information can be out of sync in the .kps file + * after updating a contained keyboard. + * @param kpsFilename + * @param kps + * @param kmp + * @returns + */ + public validateAndUpdateVersions(kpsFilename: string, kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) { + const followKeyboardVersion = kps.options?.followKeyboardVersion !== undefined; + + if(followKeyboardVersion) { + if(!this.checkFollowKeyboardVersion(kps, kmp)) { + return false; + } + } + + if(!kmp.keyboards) { + // Lexical models don't have version metadata; only their packages. + return true; + } + + let result = true; + + // We now know we have at least one keyboard in the package + + for(let keyboard of kmp.keyboards) { + const kmx = this.getKmxData(kpsFilename, kmp, keyboard); + if(!kmx) { + // Warnings or errors will have been raised by getKmxData + result = false; + continue; + } + + // get the targets from the .kmx + this.verifyTargets(keyboard, kmx, kmp); + + // Note: there is often version metadata in the .kps element, but + // we don't read from the metadata because we want to ensure we have the + // most up-to-date keyboard version data here, from the compiled keyboard. + let keyboardVersion = this.getKeyboardVersionFromKmx(kmx); + if(followKeyboardVersion && keyboardVersion === null) { + this.callbacks.reportMessage(CompilerMessages.Info_KeyboardFileHasNoKeyboardVersion({filename: keyboard.id})); + } + keyboard.version = keyboardVersion ?? DEFAULT_VERSION; + + if(result) { + if(kmp.keyboards[0].version !== keyboard.version) { + this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatch({ + keyboard: keyboard.id, + version: keyboard.version, + firstKeyboard: kmp.keyboards[0].id, + firstVersion: kmp.keyboards[0].version + })); + } + } + } + + if(result) { + if(followKeyboardVersion) { + kmp.info.version.description = kmp.keyboards[0].version; + } + else if(kmp.info.version?.description != kmp.keyboards[0].version) { + // Only need to compare against first keyboard as we compare keyboards above + this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatchPackageVersion({ + keyboard: kmp.keyboards[0].id, + keyboardVersion: kmp.keyboards[0].version, + packageVersion: kmp.info.version?.description + })); + } + } + + return result; + } + + private checkFollowKeyboardVersion(kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) { + // Lexical model packages do not allow FollowKeyboardVersion + if(kmp.lexicalModels && kmp.lexicalModels.length) { + this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionNotAllowedForModelPackages()); + return false; + } + + if(!kmp.keyboards || !kmp.keyboards.length) { + this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionButNoKeyboards()); + return false; + } + + return true; + } + + private getKmxData( + kpsFilename: string, + kmp: KmpJsonFile.KmpJsonFile, + keyboard: KmpJsonFile.KmpJsonFileKeyboard + ): KMX.KEYBOARD { + + const file = kmp.files.find(file => this.callbacks.path.basename(file.name, '.kmx') == keyboard.id); + if(!file) { + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardContentFileNotFound({id:keyboard.id})); + return null; + } + + const filename = this.callbacks.resolveFilename(kpsFilename, file.name); + if(!this.callbacks.fs.existsSync(filename)) { + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotFound({filename})); + return null; + } + + // + // load the .kmx and extract the version number + // + let kmxFileData; + try { + kmxFileData = this.callbacks.loadFile(filename); + } catch(e) { + this.callbacks.reportMessage(CompilerMessages.Error_FileCouldNotBeRead({filename, e})); + return null; + } + const kmxReader: KmxFileReader = new KmxFileReader(); + const kmx: KMX.KEYBOARD = kmxReader.read(kmxFileData); + if(!kmx) { + // The file couldn't be read, it might not be a .kmx file + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotValid({filename})); + return null; + } + + return kmx; + } + + private getKeyboardVersionFromKmx( + kmx: KMX.KEYBOARD + ): string { + const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION); + return store ? store.dpString : null; + } + + /** + * Verify that the package contains a .js if the keyboard targets touch + * devices + */ + private verifyTargets( + keyboard: KmpJsonFile.KmpJsonFileKeyboard, + kmx: KMX.KEYBOARD, + kmp: KmpJsonFile.KmpJsonFile + ): void { + // TODO: this function doesn't really belong in version validation, but we + // don't want to read .kmx twice so we'll leave it here for now; a future + // refactor could move .kmx loading into another class + + const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_TARGETS); + if(!store) { + // If there is no &TARGETS store, then there is nothing to check, + // because default is windows-only + return; + } + + // Note, if we have gotten this far, we've already located and loaded a + // .kmx, so no need to verify that the package includes a .kmx + + // If at least one target is a touch target, we need to check that the + // package also includes the .js + const targets = store.dpString.split(' '); + if(targets.some(target => ['any', ...TouchKeymanTargets].includes(target))) { + if(!kmp.files.find(file => this.callbacks.path.basename(file.name, '.js') == keyboard.id)) { + // .js version of the keyboard is not found, warn + this.callbacks.reportMessage(CompilerMessages.Warn_JsKeyboardFileIsMissing({id: keyboard.id})); + } + } + } +} diff --git a/developer/src/kmc-package/src/compiler/redist-files.ts b/developer/src/kmc-package/src/compiler/redist-files.ts new file mode 100644 index 0000000000000000000000000000000000000000..9fccb32944a9aaa8d6d38dde47d70c7b0681c704 --- /dev/null +++ b/developer/src/kmc-package/src/compiler/redist-files.ts @@ -0,0 +1,64 @@ + +/** + * This is a set of known redistributable files for Keyman for Windows that + * should not be included in packages. It is not critical that this list matches + * the current deployment; it is just for warning against accidental inclusion + * of these files by package authors. Some redistributable files have been + * intentionally excluded because they could legitimately be a different file + * with the same name. + * + * This matches behaviour from the legacy package compiler; we may want to + * reconsider how this is done in the future. + * + * These lists have been constructed from 17.0.109 alpha build. Filenames + * intentionally in lower case. + */ + +export const + keymanForWindowsInstallerFiles: string[] = [ + 'keymandesktop.msi', + 'keymanengine.msm' + ]; + + +export const + keymanEngineForWindowsFiles: string[] = [ + 'base.xslt', + 'crashpad_handler.exe', + 'keyman-debug-etw.man', + 'keyman.exe', + 'keyman32.dll', + 'keyman64.dll', + 'keymanmc.dll', + 'keymanx64.exe', + 'kmcomapi.dll', + 'kmcomapi.x64.dll', + 'kmrefresh.x64.exe', + 'kmrefresh.x86.exe', + 'kmtip.dll', + 'kmtip64.dll', + 'mcompile.exe', + 'sentry.dll', + 'sentry.x64.dll', + 'si_browsers.xslt', + 'si_fonts.xslt', + 'si_hookdlls.xslt', + 'si_keyman.xslt', + 'si_language.xslt', + 'si_office.xslt', + 'si_overview.xslt', + 'si_processes.xslt', + 'si_processes_x64.xslt', + 'si_startup.xslt', + 'tsysinfo.exe', + ]; + +export const + keymanForWindowsRedistFiles: string[] = [ + 'desktop_resources.dll', + 'keymandesktop.chm', + 'kmbrowserhost.exe', + 'kmconfig.exe', + 'kmshell.exe', + 'unicodedata.mdb', + ]; diff --git a/developer/src/kmc-package/src/main.ts b/developer/src/kmc-package/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..5903a59de3055ef20bf5ac4b086c43f72d840ba7 --- /dev/null +++ b/developer/src/kmc-package/src/main.ts @@ -0,0 +1,2 @@ +export { KmpCompiler } from "./compiler/kmp-compiler.js"; +export { PackageValidation } from "./compiler/package-validation.js"; \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps b/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps new file mode 100644 index 0000000000000000000000000000000000000000..e7c4a8b8512ddf02f8d0e47094638a66cba3679a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps @@ -0,0 +1,29 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + + + + + Absolute Path + + + + \build\absolute_path.kmx + File absolute_path.kmx + 0 + .kmx + + + diff --git a/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps new file mode 100644 index 0000000000000000000000000000000000000000..de17212a8451980f4c24b2e8cd46c723eef86677 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps @@ -0,0 +1,46 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + + + + + Binary KVK File + 1.0 + + + + binary_kvk_file.kvk + File binary_kvk_file.kvk + 0 + .kvk + + + ../../invalid/basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kvk b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kvk new file mode 100644 index 0000000000000000000000000000000000000000..c64b3a5a544dc64ab3dcfd44a6875ea5f339b878 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kvk differ diff --git a/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.intermediate.json b/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.intermediate.json index 97ef6967b7300c37643b6a89e90088db5d1efcb6..3a683626fa31daa440d76e212d1ce538a4e6e3c1 100644 --- a/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.intermediate.json +++ b/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.intermediate.json @@ -16,7 +16,7 @@ "description": "SENĆOŦEN (Saanich Dialect) Lexical Model" }, "version": { - "description": "0.0.0" + "description": "1.0.3" } }, "files": [ diff --git a/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps b/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps index e93266d48c9cf8ca97cabdcf19f71d4733a24203..fd5faa8476ee1c3b9616d638dad40384e9b221ee 100644 --- a/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps +++ b/developer/src/kmc-package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps @@ -4,9 +4,6 @@ 12.0.1500.0 12.0 - - - SENĆOŦEN (Saanich Dialect) Lexical Model © 2019 National Research Council Canada @@ -25,7 +22,6 @@ SENĆOŦEN dictionary example.qaa.sencoten - 1.0.3 North Straits Salish SENĆOŦEN diff --git a/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..70f58d779b399f349a59a1d32d6a5e1c00502c2c --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps @@ -0,0 +1,32 @@ + + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..674ad85a2f055e9cf87e944bc9d4198469b77f4c --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps @@ -0,0 +1,32 @@ + + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + SENĆOŦEN + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/basic.kmn b/developer/src/kmc-package/test/fixtures/invalid/basic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..48705f6b9939ae0229a0892fe352502d1a31ffa0 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/basic.kmn @@ -0,0 +1,6 @@ +store(&name) 'basic desktop-only kmx' +store(&keyboardversion) '1.0' + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/basic.kmx b/developer/src/kmc-package/test/fixtures/invalid/basic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/basic.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps b/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps new file mode 100644 index 0000000000000000000000000000000000000000..06d95266512afa80722fc23b1506d5350cf559de --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + English (Australian script) as spoken in Latin + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps new file mode 100644 index 0000000000000000000000000000000000000000..6483fe1c10c2ae5964920eb0d992446e5b3f667f --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps @@ -0,0 +1,49 @@ + + + + 15.0.266.0 + 7.0 + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + 1.0 + https://keyman.com/keyboards/khmer_angkor + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + SENĆOŦEN + + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps new file mode 100644 index 0000000000000000000000000000000000000000..335201a6e5b0d47456237139691a32e24175bddd --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps @@ -0,0 +1,21 @@ + + + + 15.0.266.0 + 7.0 + + + error_package_must_contain_a_model_or_a_keyboard + 1.3 + + + + + khmer_angkor.kmn + Keyboard Khmer Angkor + 0 + .kmn + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps new file mode 100644 index 0000000000000000000000000000000000000000..5b7d6d3d46a07fe429c515b5431b3a2977ff49e8 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + + + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps new file mode 100644 index 0000000000000000000000000000000000000000..f9beefebb986641a4b95ba5a7916c68be15053df --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps @@ -0,0 +1,31 @@ + + + + 15.0.266.0 + 7.0 + + + + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.empty.kps b/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.empty.kps new file mode 100644 index 0000000000000000000000000000000000000000..955cf8a05351e801f229c37f88c197d4b7518d73 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.empty.kps @@ -0,0 +1,24 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + + + + + + FollowKeyboardVersion with no keyboards + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.qaa.sencoten.model.kps b/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.qaa.sencoten.model.kps new file mode 100644 index 0000000000000000000000000000000000000000..f6dfae7119de8ebba69c8592b5cdf3d31c9336aa --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/followkeyboardversion.qaa.sencoten.model.kps @@ -0,0 +1,35 @@ + + + + 12.0.1500.0 + 12.0 + + + + + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + SENĆOŦEN + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps b/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps new file mode 100644 index 0000000000000000000000000000000000000000..cfcd52789453f18921025aa19b9f773280ee8ac8 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps @@ -0,0 +1,128 @@ + + + + 15.0.266.0 + 7.0 + + + + readme.htm + splash.gif + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + + ..\build\khmer_angkor.js + File khmer_angkor.js + 0 + .js + + + ..\build\khmer_angkor.kvk + File khmer_angkor.kvk + 0 + .kvk + + + welcome\keyboard_layout.png + File keyboard_layout.png + 0 + .png + + + welcome\welcome.htm + File welcome.htm + 0 + .htm + + + ..\shared\fonts\khmer\mondulkiri\FONTLOG.txt + File FONTLOG.txt + 0 + .txt + + + ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf + Font Khmer Mondulkiri + 0 + .ttf + + + ..\shared\fonts\khmer\mondulkiri\OFL.txt + File OFL.txt + 0 + .txt + + + ..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt + File OFL-FAQ.txt + 0 + .txt + + + welcome\KAK_Documentation_EN.pdf + File KAK_Documentation_EN.pdf + 0 + .pdf + + + welcome\KAK_Documentation_KH.pdf + File KAK_Documentation_KH.pdf + 0 + .pdf + + + readme.htm + File readme.htm + 0 + .htm + + + welcome\image002.png + File image002.png + 0 + .png + + + ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf + Font KhmerBusraKbd + 0 + .ttf + + + splash.gif + File splash.gif + 0 + .gif + + + + + Khmer Angkor + khmer_angkor + 1.3 + ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf + ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps new file mode 100644 index 0000000000000000000000000000000000000000..cf6e49053a507a1e98ee8125e4b94c23ec08d924 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps @@ -0,0 +1,41 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + + keyboardfilenotfound.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + + + Khmer Angkor + keyboardfilenotfound + 1.3 + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kmx b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kmx new file mode 100644 index 0000000000000000000000000000000000000000..93b44e9d16e3ec69bf9ed5e6d41d5d1af3d44989 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kps b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kps new file mode 100644 index 0000000000000000000000000000000000000000..37dbc27a664169ade8feb3817cc7866750a1fd7e --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotvalid.kps @@ -0,0 +1,41 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + + keyboardfilenotvalid.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + + + Khmer Angkor + keyboardfilenotvalid + 1.3 + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps new file mode 100644 index 0000000000000000000000000000000000000000..b79330f06f1ec885b6e5a55f6d2901c7086a39f2 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps @@ -0,0 +1,28 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps new file mode 100644 index 0000000000000000000000000000000000000000..0fdb270321410e75782c9498017cebc42496902a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps @@ -0,0 +1,33 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + + Salish Again + SENĆOŦEN + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx b/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c3d0289f4863c7c4a998c348eb1d425caadca79d Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx b/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx b/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmn b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmn new file mode 100644 index 0000000000000000000000000000000000000000..4fdd8a62f32d26814daf16079813b6010729ee07 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmn @@ -0,0 +1,3 @@ +store(&NAME) "nokeyboardversion" +store(&VERSION) "10.0" +c no &keyboardversion store! \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmx b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0cf77b0cbbceed2664457d5f2590ad54b410a207 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kps b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kps new file mode 100644 index 0000000000000000000000000000000000000000..56cd7e3e58939ea06528de4f42e36a2f0837593d --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/nokeyboardversion.kps @@ -0,0 +1,40 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + nokeyboardversion.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + + + Khmer Angkor + nokeyboardversion + 1.3 + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn new file mode 100644 index 0000000000000000000000000000000000000000..cb2cce5816df350bfcc819ec8b902ab7c09aa01e --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn @@ -0,0 +1,6 @@ +store(&name) 'version 4' +store(&keyboardversion) '4.0' + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7180460e961b26b2bbb372c70302c0d8f77934f8 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps new file mode 100644 index 0000000000000000000000000000000000000000..54fc8df0ebe9e61d6ac41e5113087e6bb5314d1a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps @@ -0,0 +1,38 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + khmer_angkor.docx + Documentation + 0 + .docx + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..9a8c6d588677f2399ef9baf8272bc463f731f55a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + + my special keyboard.kmx + Keyboard my special keyboard + 0 + .kmx + + + + + Khmer Angkor + my special keyboard + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps new file mode 100644 index 0000000000000000000000000000000000000000..426592c2715940c8ba1ad34a1e0ae713b8b7c4e6 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps @@ -0,0 +1,39 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + + my_file.PDF + My Documentation + 0 + .PDF + + + + basic.kmx + Keyboard my special keyboard + 0 + .kmx + + + + + Khmer Angkor + basic + 1.3 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn new file mode 100644 index 0000000000000000000000000000000000000000..fe4f8457c498dd295aaa0de432e826b19d2daff5 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn @@ -0,0 +1,8 @@ +store(&NAME) "warn_js_keyboard_file_is_missing" +store(&VERSION) "10.0" +store(&keyboardversion) "1.0" +store(&targets) "any" + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ca697e872c2dadb3afd3a9f659bb38539d19607d Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps new file mode 100644 index 0000000000000000000000000000000000000000..82227ffea62b9323dc80aadcec469e0255a446db --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps @@ -0,0 +1,40 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + warn_js_keyboard_file_is_missing.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + + + Khmer Angkor + warn_js_keyboard_file_is_missing + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps new file mode 100644 index 0000000000000000000000000000000000000000..d6a8aaf20182cc63481a3e41700dd0e3b6538445 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps @@ -0,0 +1,45 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + version_four.kmx + Keyboard Version Four + 0 + .kmx + + + + + Basic + basic + 1.0 + + Khmer + + + + Version 4 + version_four + 4.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps new file mode 100644 index 0000000000000000000000000000000000000000..fe0276104bd72f7cfbec0a5698e11efd38274d7d --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps @@ -0,0 +1,31 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.3 + + + + version_four.kmx + Keyboard Version Four + 0 + .kmx + + + + + Version 4 + version_four + 4.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps new file mode 100644 index 0000000000000000000000000000000000000000..cc757d6431cc46887d755d7ad2cf84a8cc14089a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps new file mode 100644 index 0000000000000000000000000000000000000000..ef8fcfb89df78010987478c92fc0d73c82b5f749 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps @@ -0,0 +1,33 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + Central Khmer (Khmer, Cambodia) + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps new file mode 100644 index 0000000000000000000000000000000000000000..10d6212d67701e2dd340510b806e337d69056a57 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps @@ -0,0 +1,38 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + keyman.exe + Keyman Program + 0 + .exe + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.kps b/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.kps index 8e8010a0aa07e5697a65e65f1bcdbe52b94b53c2..960502b6c6c458965f66059beaaebc404dea26eb 100644 --- a/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.kps +++ b/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.kps @@ -4,9 +4,6 @@ 12.0.1500.0 12.0 - - - SENĆOŦEN (Saanich Dialect) Lexical Model © 2019 National Research Council Canada @@ -31,7 +28,6 @@ SENĆOŦEN dictionary withfolders.qaa.sencoten - 1.0.3 North Straits Salish SENĆOŦEN diff --git a/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.intermediate.json b/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.intermediate.json index 046a2f3f39a48fa358a9ae447cbad948542f5b93..e02186e8d9b2baedd9aa3d0daa70f60cc4c1bd72 100644 --- a/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.intermediate.json +++ b/developer/src/kmc-package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.intermediate.json @@ -16,7 +16,7 @@ "description": "SENĆOŦEN (Saanich Dialect) Lexical Model" }, "version": { - "description": "0.0.0" + "description": "1.0.3" } }, "files": [ diff --git a/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps new file mode 100644 index 0000000000000000000000000000000000000000..816a66d633883252ca3f51daee0bf4ac5e50f70f --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps @@ -0,0 +1,46 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + + + + + XML KVK File + 1.0 + + + + xml_kvk_file.kvk + File xml_kvk_file.kvk + 0 + .kvk + + + ../../invalid/basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kvk b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kvk new file mode 100644 index 0000000000000000000000000000000000000000..32f49e3b3363c90f6a0006f0a67ea5b75418d3ed --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kvk @@ -0,0 +1,15 @@ + + +
+ 10.0 + xml_kvk_file + + + +
+ + + + + +
diff --git a/developer/src/kmc-package/test/test-messages.ts b/developer/src/kmc-package/test/test-messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..5944ce956a636b4a06eda6cc76d4e6dbe76755ee --- /dev/null +++ b/developer/src/kmc-package/test/test-messages.ts @@ -0,0 +1,200 @@ +import 'mocha'; +import { assert } from 'chai'; +import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { makePathToFixture } from './helpers/index.js'; +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { PackageValidation } from '../src/compiler/package-validation.js'; + +const debug = false; +const callbacks = new TestCompilerCallbacks(); + +describe('CompilerMessages', function () { + it('should have a valid CompilerMessages object', function() { + return verifyCompilerMessagesObject(CompilerMessages); + }); + + + // + // Message tests + // + + function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) { + context.timeout(10000); + + callbacks.clear(); + + const kpsPath = makePathToFixture(...fixture); + const kmpCompiler = new KmpCompiler(callbacks); + + let kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); + if(kmpJson && callbacks.messages.length == 0) { + const validator = new PackageValidation(callbacks); + validator.validate(kpsPath, kmpJson); // we'll ignore return value and rely on the messages + } + + if(kmpJson && callbacks.messages.length == 0) { + // We'll try building the package if we have not yet received any messages + kmpCompiler.buildKmpFile(kpsPath, kmpJson) + } + + if(debug) callbacks.printMessages(); + + if(messageId) { + assert.lengthOf(callbacks.messages, 1); + assert.isTrue(callbacks.hasMessage(messageId)); + } else { + assert.lengthOf(callbacks.messages, 0); + } + } + + // WARN_FileIsNotABinaryKvkFile + + it('should generate WARN_FileIsNotABinaryKvkFile if a non-binary kvk file is included', async function() { + testForMessage(this, ['xml_kvk_file', 'source', 'xml_kvk_file.kps'], CompilerMessages.WARN_FileIsNotABinaryKvkFile); + }); + + it('should not warn if a binary kvk file is included', async function() { + testForMessage(this, ['binary_kvk_file', 'source', 'binary_kvk_file.kps']); + }); + + // ERROR_FollowKeyboardVersionNotAllowedForModelPackages + + it('should generate ERROR_FollowKeyboardVersionNotAllowedForModelPackages if is set for model packages', async function() { + testForMessage(this, ['invalid', 'followkeyboardversion.qaa.sencoten.model.kps'], CompilerMessages.ERROR_FollowKeyboardVersionNotAllowedForModelPackages); + }); + + // ERROR_FollowKeyboardVersionButNoKeyboards + + it('should generate ERROR_FollowKeyboardVersionButNoKeyboards if is set for a package with no keyboards', async function() { + testForMessage(this, ['invalid', 'followkeyboardversion.empty.kps'], CompilerMessages.ERROR_FollowKeyboardVersionButNoKeyboards); + }); + + // ERROR_KeyboardContentFileNotFound + + it('should generate ERROR_KeyboardContentFileNotFound if a is listed in a package but not found in ', async function() { + testForMessage(this, ['invalid', 'keyboardcontentfilenotfound.kps'], CompilerMessages.ERROR_KeyboardContentFileNotFound); + }); + + // ERROR_KeyboardFileNotValid + + it('should generate ERROR_KeyboardFileNotValid if a .kmx is not valid in ', async function() { + testForMessage(this, ['invalid', 'keyboardfilenotvalid.kps'], CompilerMessages.ERROR_KeyboardFileNotValid); + }); + + // INFO_KeyboardFileHasNoKeyboardVersion + + it('should generate INFO_KeyboardFileHasNoKeyboardVersion if is set but keyboard has no version', async function() { + testForMessage(this, ['invalid', 'nokeyboardversion.kps'], CompilerMessages.INFO_KeyboardFileHasNoKeyboardVersion); + }); + + // ERROR_PackageCannotContainBothModelsAndKeyboards + + it('should generate ERROR_PackageCannotContainBothModelsAndKeyboards if package has both keyboards and models', async function() { + testForMessage(this, ['invalid', 'error_package_cannot_contain_both_models_and_keyboards.kps'], CompilerMessages.ERROR_PackageCannotContainBothModelsAndKeyboards); + }); + + // WARN_PackageShouldNotRepeatLanguages (models) + + it('should generate WARN_PackageShouldNotRepeatLanguages if model has same language repeated', async function() { + testForMessage(this, ['invalid', 'keyman.en.warn_package_should_not_repeat_languages.model.kps'], CompilerMessages.WARN_PackageShouldNotRepeatLanguages); + }); + + // WARN_PackageShouldNotRepeatLanguages (keyboards) + + it('should generate WARN_PackageShouldNotRepeatLanguages if keyboard has same language repeated', async function() { + testForMessage(this, ['invalid', 'warn_package_should_not_repeat_languages.kps'], CompilerMessages.WARN_PackageShouldNotRepeatLanguages); + }); + + // WARN_PackageNameDoesNotFollowLexicalModelConventions + + it('should generate WARN_PackageNameDoesNotFollowLexicalModelConventions if filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'WARN_PackageNameDoesNotFollowLexicalModelConventions.kps'], CompilerMessages.WARN_PackageNameDoesNotFollowLexicalModelConventions); + }); + + // WARN_PackageNameDoesNotFollowKeyboardConventions + + it('should generate WARN_PackageNameDoesNotFollowKeyboardConventions if filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'WARN_PackageNameDoesNotFollowKeyboardConventions.kps'], CompilerMessages.WARN_PackageNameDoesNotFollowKeyboardConventions); + }); + + // WARN_FileInPackageDoesNotFollowFilenameConventions + + it('should generate WARN_FileInPackageDoesNotFollowFilenameConventions if content filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions.kps'], CompilerMessages.WARN_FileInPackageDoesNotFollowFilenameConventions); + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions_2.kps'], CompilerMessages.WARN_FileInPackageDoesNotFollowFilenameConventions); + }); + + // ERROR_PackageNameCannotBeBlank + + it('should generate ERROR_PackageNameCannotBeBlank if package info has empty name', async function() { + testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // blank field + testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank_2.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // missing field + }); + + // ERROR_KeyboardFileNotFound + + it('should generate ERROR_KeyboardFileNotFound if a is listed in a package but not found in ', async function() { + testForMessage(this, ['invalid', 'keyboardfilenotfound.kps'], CompilerMessages.ERROR_KeyboardFileNotFound); + }); + + // WARN_KeyboardVersionsDoNotMatch + + it('should generate WARN_KeyboardVersionsDoNotMatch if two have different versions', async function() { + testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatch); + }); + + // WARN_KeyboardVersionsDoNotMatchPackageVersion + + it('should generate WARN_KeyboardVersionsDoNotMatchPackageVersion if version does not match package version', async function() { + testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match_package_version.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatchPackageVersion); + }); + + // ERROR_LanguageTagIsNotValid + + it('should generate ERROR_LanguageTagIsNotValid if keyboard has an invalid language tag', async function() { + testForMessage(this, ['invalid', 'error_language_tag_is_not_valid.kps'], CompilerMessages.ERROR_LanguageTagIsNotValid); + }); + + // WARN_LanguageTagIsNotMinimal + + it('should generate WARN_LanguageTagIsNotMinimal if keyboard has a non-minimal language tag', async function() { + testForMessage(this, ['invalid', 'warn_language_tag_is_not_minimal.kps'], CompilerMessages.WARN_LanguageTagIsNotMinimal); + }); + + // ERROR_MustHaveAtLeastOneLanguage + + it('should generate ERROR_MustHaveAtLeastOneLanguage if model or keyboard has zero language tags', async function() { + testForMessage(this, ['invalid', 'keyman.en.error_must_have_at_least_one_language.model.kps'], + CompilerMessages.ERROR_MustHaveAtLeastOneLanguage); + }); + + // WARN_RedistFileShouldNotBeInPackage + + it('should generate WARN_RedistFileShouldNotBeInPackage if package contains a redist file', async function() { + testForMessage(this, ['invalid', 'warn_redist_file_should_not_be_in_package.kps'], + CompilerMessages.WARN_RedistFileShouldNotBeInPackage); + }); + + // WARN_DocFileDangerous + + it('should generate WARN_DocFileDangerous if package contains a .doc file', async function() { + testForMessage(this, ['invalid', 'warn_doc_file_dangerous.kps'], + CompilerMessages.WARN_DocFileDangerous); + }); + + // ERROR_PackageMustContainAPackageOrAKeyboard + + it('should generate ERROR_PackageMustContainAModelOrAKeyboard if package contains a .doc file', async function() { + testForMessage(this, ['invalid', 'error_package_must_contain_a_model_or_a_keyboard.kps'], + CompilerMessages.ERROR_PackageMustContainAModelOrAKeyboard); + }); + + // WARN_JsKeyboardFileIsMissing + + it('should generate WARN_JsKeyboardFileIsMissing if package is missing corresponding .js for a touch .kmx', async function() { + testForMessage(this, ['invalid', 'warn_js_keyboard_file_is_missing.kps'], + CompilerMessages.WARN_JsKeyboardFileIsMissing); + }); + +}); diff --git a/developer/src/kmc-package/test/test-package-compiler.ts b/developer/src/kmc-package/test/test-package-compiler.ts index a2850e7fc2542f986491e02b13389cfd0efb37e2..59d2acb55a32cd477c1f7f3f88794cd97dbafdfe 100644 --- a/developer/src/kmc-package/test/test-package-compiler.ts +++ b/developer/src/kmc-package/test/test-package-compiler.ts @@ -1,12 +1,18 @@ import 'mocha'; import * as fs from 'fs'; -import {assert} from 'chai'; - -import KmpCompiler from '../src/kmp-compiler.js'; -import {makePathToFixture} from './helpers/index.js'; +import { assert } from 'chai'; import JSZip from 'jszip'; + import KEYMAN_VERSION from "@keymanapp/keyman-version"; -import { type KmpJsonFile } from '../src/kmp-json-file.js'; +import { KmpJsonFile } from '@keymanapp/common-types'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; + +import { makePathToFixture } from './helpers/index.js'; + +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { CompilerMessages } from '../src/compiler/messages.js'; + +const debug = false; describe('KmpCompiler', function () { const MODELS : string[] = [ @@ -14,7 +20,8 @@ describe('KmpCompiler', function () { 'withfolders.qaa.sencoten', ]; - let kmpCompiler = new KmpCompiler(); + const callbacks = new TestCompilerCallbacks(); + let kmpCompiler = new KmpCompiler(callbacks); for (let modelID of MODELS) { const kpsPath = modelID.includes('withfolders') ? @@ -33,11 +40,10 @@ describe('KmpCompiler', function () { // Test just the transform from kps to kmp.json // it(`should transform ${modelID}.model.kps to kmp.json`, function () { - let source = fs.readFileSync(kpsPath, 'utf-8'); - let kmpJson: KmpJsonFile; + let kmpJson: KmpJsonFile.KmpJsonFile; assert.doesNotThrow(() => { - kmpJson = kmpCompiler.transformKpsToKmpObject(source, kpsPath); + kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); }); // Test that the kmp.json data is identical @@ -51,10 +57,9 @@ describe('KmpCompiler', function () { //fs.writeFileSync(kmpJsonPath, JSON.stringify(kmpJson), 'utf-8'); }); it(`should build a full .kmp for ${modelID}`, async function() { - const source = fs.readFileSync(kpsPath, 'utf-8'); const zip = JSZip(); // Build kmp.json in memory - const kmpJson: KmpJsonFile = kmpCompiler.transformKpsToKmpObject(source, kpsPath); + const kmpJson: KmpJsonFile.KmpJsonFile = kmpCompiler.transformKpsToKmpObject(kpsPath); // Build file.kmp in memory const promise = kmpCompiler.buildKmpFile(kpsPath, kmpJson); promise.then(data => { @@ -78,20 +83,20 @@ describe('KmpCompiler', function () { } it('should generates a valid .kmp (zip) file', async function() { - // const kmpPath = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.kmp'); + this.timeout(10000); // building a zip file can sometimes be slow + const kpsPath = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps'); const kmpJsonRefPath = makePathToFixture('khmer_angkor', 'ref', 'kmp.json'); - const kmpCompiler = new KmpCompiler(); - const source = fs.readFileSync(kpsPath, 'utf-8'); - const kmpJsonFixture: KmpJsonFile = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8')); + const kmpCompiler = new KmpCompiler(callbacks); + const kmpJsonFixture: KmpJsonFile.KmpJsonFile = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8')); // We override the fixture version so that we can compare with the compiler output kmpJsonFixture.system.keymanDeveloperVersion = KEYMAN_VERSION.VERSION; let kmpJson = null; assert.doesNotThrow(() => { - kmpJson = kmpCompiler.transformKpsToKmpObject(source, kpsPath); + kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); }); const kmpData = await kmpCompiler.buildKmpFile(kpsPath, kmpJson); @@ -117,4 +122,30 @@ describe('KmpCompiler', function () { assert.deepEqual(kmpJsonData, kmpJsonFixture); }); + /* + * Testing Warnings and Errors + */ + + it('should warn on absolute paths', async function() { + this.timeout(10000); // building a zip file can sometimes be slow + + callbacks.clear(); + + const kpsPath = makePathToFixture('absolute_path', 'source', 'absolute_path.kps'); + const kmpCompiler = new KmpCompiler(callbacks); + + let kmpJson: KmpJsonFile.KmpJsonFile = null; + + assert.doesNotThrow(() => { + kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); + }); + + await assert.isNull(kmpCompiler.buildKmpFile(kpsPath, kmpJson)); + + if(debug) callbacks.printMessages(); + + assert.lengthOf(callbacks.messages, 2); + assert.deepEqual(callbacks.messages[0].code, CompilerMessages.WARN_AbsolutePath); + assert.deepEqual(callbacks.messages[1].code, CompilerMessages.ERROR_FileDoesNotExist); + }); }); diff --git a/developer/src/kmc-package/test/tsconfig.json b/developer/src/kmc-package/test/tsconfig.json index d785ebc0195f575015a900cee3664f9e5329d1c9..9700969d6220af9e2837776691cc084cda01cf31 100644 --- a/developer/src/kmc-package/test/tsconfig.json +++ b/developer/src/kmc-package/test/tsconfig.json @@ -7,6 +7,9 @@ "outDir": "../build/test", "baseUrl": ".", "allowSyntheticDefaultImports": true, // for jszip + "paths": { + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], + }, }, "include": [ "**/test-*.ts", @@ -14,6 +17,8 @@ ], "references": [ { "path": "../" }, + { "path": "../../../../common/web/types" }, { "path": "../../../../common/web/keyman-version/tsconfig.esm.json" }, + { "path": "../../common/web/test-helpers/" }, ] } \ No newline at end of file diff --git a/developer/src/kmc-package/tsconfig.json b/developer/src/kmc-package/tsconfig.json index b7044ed6ee5d0d3df90aad0afdef54e06491c228..8913057047eadc303181a6e070dbb16889d459ed 100644 --- a/developer/src/kmc-package/tsconfig.json +++ b/developer/src/kmc-package/tsconfig.json @@ -7,9 +7,10 @@ "allowSyntheticDefaultImports": true, // for jszip }, "include": [ - "src/**/*.ts" + "src/**/*.ts", ], "references": [ { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, + { "path": "../../../common/web/types" }, ] } diff --git a/developer/src/kmc/.eslintrc.cjs b/developer/src/kmc/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..f348275f8a6aed22101ec03bfbfe9218998c4b8a --- /dev/null +++ b/developer/src/kmc/.eslintrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + // extends: ["../../../common/web/eslint/eslintNoNodeImports.js"], + parserOptions: { + project: ["./tsconfig.json", "./test/tsconfig.json"], + }, + rules: { + "prefer-const": "off", // TODO: enable this once infrastructure is in place and cleanup the problem cases + }, +}; diff --git a/developer/src/kmc/.npmignore b/developer/src/kmc/.npmignore deleted file mode 100644 index 8a5101350b1c8be6ae1a9baa8cdac9436adece21..0000000000000000000000000000000000000000 --- a/developer/src/kmc/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -# Ignore files required only in development. -build.sh -bundle.inc.sh -Makefile -src/* -test/* -tools/* -tsconfig.json -tsconfig.kmc-base.json -tsconfig.tsbuildinfo -dist-tests/* -tests/* -# Ignore other unneeded build artifacts -.nyc_output/ diff --git a/developer/src/kmc/Makefile b/developer/src/kmc/Makefile index 8e8dc7d5f3187501bf08b8b5bf22e9cc94f1f87f..1078ab1a67f1d7a5fe1436d00bf44ba240547309 100644 --- a/developer/src/kmc/Makefile +++ b/developer/src/kmc/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already, +# We don't depend on configure here because kmc-ldml does that already, # called from parent folder Makefile build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc/build.sh b/developer/src/kmc/build.sh index fd14e00e4cc74b9c23eac287762227c0657e9112..61ccd3bdca7ae0e20829f1cf28dc7c3dff00dac6 100755 --- a/developer/src/kmc/build.sh +++ b/developer/src/kmc/build.sh @@ -17,9 +17,11 @@ cd "$THIS_SCRIPT_PATH" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" builder_describe "Build Keyman Keyboard Compiler kmc" \ + "@/common/include" \ "@/common/web/keyman-version" \ "@/common/web/types" \ - "@/developer/src/kmc-keyboard" \ + "@/developer/src/kmc-kmn" \ + "@/developer/src/kmc-ldml" \ "@/developer/src/kmc-model" \ "@/developer/src/kmc-model-info" \ "@/developer/src/kmc-package" \ @@ -28,7 +30,8 @@ builder_describe "Build Keyman Keyboard Compiler kmc" \ "clean cleans build/ folder" \ "bundle creates a bundled version of kmc" \ "test run automated tests for kmc" \ - "publish publish to npm" \ + "pack build a local .tgz pack for testing (note: all npm modules in the repo will be packed by this script)" \ + "publish publish to npm (note: all npm modules in the repo will be published by this script)" \ "--build-path=BUILD_PATH build directory for bundle" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ @@ -37,24 +40,32 @@ builder_describe_outputs \ builder_parse "$@" +if builder_has_option --dry-run; then + DRY_RUN=--dry-run +else + DRY_RUN= +fi + #------------------------------------------------------------------------------------------------------------------- if builder_start_action clean; then rm -rf ./build/ ./tsconfig.tsbuildinfo builder_finish_action success clean -else +fi + +function copy_schemas() { # We need the schema file at runtime and bundled, so always copy it for all actions except `clean` mkdir -p "$THIS_SCRIPT_PATH/build/src/util/" cp "$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard.schema.json" "$THIS_SCRIPT_PATH/build/src/util/" cp "$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboardtest.schema.json" "$THIS_SCRIPT_PATH/build/src/util/" cp "$KEYMAN_ROOT/common/schemas/kvks/kvks.schema.json" "$THIS_SCRIPT_PATH/build/src/util/" cp "$KEYMAN_ROOT/common/schemas/kpj/kpj.schema.json" "$THIS_SCRIPT_PATH/build/src/util/" -fi - +} #------------------------------------------------------------------------------------------------------------------- if builder_start_action configure; then + copy_schemas verify_npm_setup builder_finish_action success configure fi @@ -62,6 +73,7 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action build; then + copy_schemas npm run build builder_finish_action success build fi @@ -69,13 +81,18 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action test; then - # npm test -- no tests as yet + copy_schemas + npm test + # TODO: enable c8 (disabled because no coverage at present) + # && c8 --reporter=lcov --reporter=text mocha builder_finish_action success test fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action bundle; then + copy_schemas + if ! builder_has_option --build-path; then builder_finish_action "Parameter --build-path is required" bundle exit 64 @@ -90,15 +107,39 @@ fi #------------------------------------------------------------------------------------------------------------------- +readonly PACKAGES=( + common/web/keyman-version + common/web/types + common/models/types + core/include/ldml + developer/src/kmc-kmn + developer/src/kmc-ldml + developer/src/kmc-model + developer/src/kmc-model-info + developer/src/kmc-package +) + if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + + # For now, kmc will have responsibility for publishing keyman-version and + # common-types, as well as all the other dependent modules. In the future, we + # should probably have a top-level npm publish script that publishes all + # modules for a given release version From: #7595 + for package in "${PACKAGES[@]}"; do + "$KEYMAN_ROOT/$package/build.sh" publish $DRY_RUN + done + + # Finally, publish kmc + builder_publish_to_npm + builder_finish_action success publish +elif builder_start_action pack; then + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" - # For now, kmc will have responsibility for publishing keyman-version. In - # the future, we should probably have a top-level npm publish script that - # publishes all modules for a given release version - # From: #7595 - "$KEYMAN_ROOT/common/web/keyman-version/build.sh" publish $DRY_RUN + for package in "${PACKAGES[@]}"; do + "$KEYMAN_ROOT/$package/build.sh" pack $DRY_RUN + done - builder_finish_action success publish + builder_publish_to_pack + builder_finish_action success pack fi diff --git a/developer/src/kmc/package.json b/developer/src/kmc/package.json index 67ba3c38ba6054dc705089261c029124104d02c0..695ebc96e350528972b4b10e0dd72a40b11c4f24 100644 --- a/developer/src/kmc/package.json +++ b/developer/src/kmc/package.json @@ -16,7 +16,7 @@ "bundle-kmlmc": "esbuild build/src/kmlmc.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmc.cjs", "bundle-kmlmi": "esbuild build/src/kmlmi.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmi.cjs", "bundle-kmlmp": "esbuild build/src/kmlmp.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmp.cjs", - "test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha", + "test": "eslint . && cd test && tsc -b && cd .. && mocha", "prepublishOnly": "npm run build" }, "type": "module", @@ -39,13 +39,17 @@ "dependencies": { "@keymanapp/keyman-version": "*", "@keymanapp/common-types": "*", - "@keymanapp/kmc-keyboard": "*", + "@keymanapp/kmc-kmn": "*", + "@keymanapp/kmc-ldml": "*", "@keymanapp/kmc-model": "*", "@keymanapp/kmc-model-info": "*", "@keymanapp/kmc-package": "*", "@keymanapp/models-types": "*", "commander": "^10.0.0" }, + "files": [ + "build/src/" + ], "devDependencies": { "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", @@ -65,6 +69,16 @@ "source-map-support/register" ] }, + "c8": { + "all": true, + "src": [ + "src/" + ], + "exclude-after-remap": true, + "exclude": [ + "test/" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/keymanapp/keyman.git" diff --git a/developer/src/kmc/src/activities/buildKmnKeyboard.ts b/developer/src/kmc/src/activities/buildKmnKeyboard.ts deleted file mode 100644 index 421c59c6138f5c7ceaa4a57a77cbcabfe5c65cea..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/activities/buildKmnKeyboard.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { spawnSync } from 'child_process'; -import * as path from 'path'; -import { BuildCommandOptions } from '../commands/build.js'; -import { getDeveloperBinPath } from '../util/getDeveloperBinPath.js'; - -export async function buildKmnKeyboard(infile: string, options: BuildCommandOptions): Promise { - // We'll call out to kmcomp.exe to build a .kmn keyboard into a .kmx Note: - // kmcomp.exe will also build a .js if it is required, and may not actually - // generate a .kmx if the output target defined in the .kmn is mobile/web - // only. kmcomp.exe will also build the .kvk file. - const binRoot = getDeveloperBinPath(); - if(binRoot == null) { - console.error('Could not locate Keyman Developer bin path'); - return false; - } - - let args = ['-nologo']; - if(options.debug) { - args.push('-d'); - } - - args.push(path.win32.normalize(infile)); - - // .* target file name option allows us to specify .kmx output file and the - // .js output file will also be generated as required, in the same way as when - // we build a project. Note: this is a stop gap as we work to deprecate - // kmcomp.exe and replace it with kmcmp (cross-platform .kmx compiler in C++) - // + kmc-kmw (KMX->KMW transpiler in TypeScript) + kmc-kvk (KVK compiler in - // TypeScript). - let outfile = (options.outFile ?? infile).replace(/\\/g, '/'); - // For now, we normalize to posix, and then renormalize to win32 for launching - // kmcomp, until we have a cross-platform alternative to use - outfile = path.win32.normalize(options.outFile).replace(/\.km.$/i, '.*'); - args.push(outfile); - - const kmcomp = path.join(binRoot, 'kmcomp.exe'); - - // We won't attempt to make this call cross-platform, yet. - let child = spawnSync(kmcomp, args, { - encoding: 'utf8' - }); - - if(child.error) { - console.error(child.error); - return false; - } - - console.log(child.stdout); - if(child.stderr) { - console.error(child.stderr); - } - - return child.status === 0; -} \ No newline at end of file diff --git a/developer/src/kmc/src/activities/buildLdmlKeyboard.ts b/developer/src/kmc/src/activities/buildLdmlKeyboard.ts deleted file mode 100644 index 9fa7814afdbe651f8182bd5739727fc1b434db41..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/activities/buildLdmlKeyboard.ts +++ /dev/null @@ -1,84 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import * as kmc from '@keymanapp/kmc-keyboard'; -import { KvkFileWriter, CompilerCallbacks } from '@keymanapp/common-types'; -import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js'; -import { BuildCommandOptions } from '../commands/build.js'; - -export async function buildLdmlKeyboard(infile: string, options: BuildCommandOptions): Promise { - // TODO-LDML: consider hardware vs touch -- touch-only layout will not have a .kvk - // Compile: - let [kmx,kvk,kmw] = buildLdmlKeyboardToMemory(infile, options); - // Output: - - const fileBaseName = options.outFile ?? infile; - const outFileBase = path.basename(fileBaseName, path.extname(fileBaseName)); - const outFileDir = path.dirname(fileBaseName); - - if(kmx && kvk) { - const outFileKmx = path.join(outFileDir, outFileBase + '.kmx'); - console.log(`Writing compiled keyboard to ${outFileKmx}`); - fs.writeFileSync(outFileKmx, kmx); - - const outFileKvk = path.join(outFileDir, outFileBase + '.kvk'); - console.log(`Writing compiled visual keyboard to ${outFileKvk}`); - fs.writeFileSync(outFileKvk, kvk); - } else { - console.error(`An error occurred compiling ${infile}`); - return false; - } - - if(kmw) { - const outFileKmw = path.join(outFileDir, outFileBase + '.js'); - console.log(`Writing compiled js keyboard to ${outFileKmw}`); - fs.writeFileSync(outFileKmw, kmw); - } - - return true; -} - -function buildLdmlKeyboardToMemory(inputFilename: string, options: BuildCommandOptions): [Uint8Array, Uint8Array, Uint8Array] { - let compilerOptions: kmc.CompilerOptions = { - debug: options.debug ?? false, - addCompilerVersion: options.compilerVersion ?? true - } - - const c: CompilerCallbacks = new NodeCompilerCallbacks(); - const k = new kmc.Compiler(c, options); - let source = k.load(inputFilename); - if (!source) { - return [null, null, null]; - } - if (!k.validate(source)) { - return [null, null, null]; - } - let kmx = k.compile(source); - if (!kmx) { - return [null, null, null]; - } - - // In order for the KMX file to be loaded by non-KMXPlus components, it is helpful - // to duplicate some of the metadata - kmc.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions); - - // Use the builder to generate the binary output file - const builder = new kmc.KMXBuilder(kmx, options.debug); - const kmx_binary = builder.compile(); - - const vkcompiler = new kmc.VisualKeyboardCompiler(); - const vk = vkcompiler.compile(source); - const writer = new KvkFileWriter(); - const kvk_binary = writer.write(vk); - - // Note: we could have a step of generating source files here - // KvksFileWriter()... - // const tlcompiler = new kmc.TouchLayoutCompiler(); - // const tl = tlcompiler.compile(source); - // const tlwriter = new TouchLayoutFileWriter(); - const kmwcompiler = new kmc.KeymanWebCompiler(compilerOptions); - const kmw_string = kmwcompiler.compile(inputFilename, source); - const encoder = new TextEncoder(); - const kmw_binary = encoder.encode(kmw_string); - - return [kmx_binary, kvk_binary, kmw_binary]; -} diff --git a/developer/src/kmc/src/activities/buildModel.ts b/developer/src/kmc/src/activities/buildModel.ts deleted file mode 100644 index dc0ddf6e9bfc242323a55a6e54d3fc1c6b181204..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/activities/buildModel.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as fs from 'fs'; -import { BuildCommandOptions } from '../commands/build.js'; -import { compileModel } from '@keymanapp/kmc-model'; - -export async function buildModel(infile: string, options: BuildCommandOptions): Promise { - - let outputFilename: string = options.outFile ? options.outFile : infile.replace(/\.ts$/i, ".js"); - - let code = null; - - // Compile: - try { - code = compileModel(infile); - } catch(e) { - console.error(e); - return false; - } - - if(!code) { - console.error('Compilation failed.') - return false; - } - - // Output: - fs.writeFileSync(outputFilename, code, 'utf8'); - - return true; -} \ No newline at end of file diff --git a/developer/src/kmc/src/activities/buildPackage.ts b/developer/src/kmc/src/activities/buildPackage.ts deleted file mode 100644 index 7138c29edaf35e874facaea336937fafac06a46d..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/activities/buildPackage.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as fs from 'fs'; -import { BuildCommandOptions } from '../commands/build.js'; -import KmpCompiler from '@keymanapp/kmc-package'; - -export async function buildPackage(infile: string, options: BuildCommandOptions): Promise { - - let outputFilename: string = options.outFile ? options.outFile : infile.replace(/\.kps$/i, ".kmp"); - - // - // Load .kps source data - // - - let kpsString: string = fs.readFileSync(infile, 'utf8'); - let kmpCompiler = new KmpCompiler(); - let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsString, infile); - - // - // Build the .kmp package file - // - - let data = await kmpCompiler.buildKmpFile(infile, kmpJsonData); - if(data) { - fs.writeFileSync(outputFilename, data, 'binary'); - } else { - // TODO error logging - return false; - } - - return true; -} \ No newline at end of file diff --git a/developer/src/kmc/src/activities/buildProject.ts b/developer/src/kmc/src/activities/buildProject.ts deleted file mode 100644 index 78acdbef3c64847020d261ffe8582857c9304455..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/activities/buildProject.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { CompilerCallbacks, KeymanDeveloperProject, KPJFileReader } from '@keymanapp/common-types'; -import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js'; -import { KeymanDeveloperProjectFile } from '../../../../../common/web/types/src/kpj/keyman-developer-project.js'; -import { BuildCommandOptions } from '../commands/build.js'; -import { buildKmnKeyboard } from './buildKmnKeyboard.js'; -import * as path from 'path'; -import * as fs from 'fs'; -import { buildLdmlKeyboard } from './buildLdmlKeyboard.js'; -import { buildModel } from './buildModel.js'; -import { buildPackage } from './buildPackage.js'; - -export async function buildProject(infile: string, options: BuildCommandOptions): Promise { - let builder = new ProjectBuilder(infile, options); - return builder.run(); -} - -class ProjectBuilder { - callbacks: CompilerCallbacks = new NodeCompilerCallbacks(); - infile: string; - options: BuildCommandOptions; - project: KeymanDeveloperProject; - - constructor(infile: string, options: BuildCommandOptions) { - this.infile = path.resolve(infile); - this.options = options; - } - - async run(): Promise { - if(this.options.outFile) { - // TODO: callbacks.reportMessage - console.error('--out-file should not be specified for project builds'); - return false; - } - - this.project = this.loadProject(); - if(!this.project) { - return false; - } - - // Build all Keyman keyboards in the project - if(!await this.buildProjectTargets(buildKmnKeyboard, '.kmn', '.kmx')) { - return false; - } - - // Build all LDML keyboards in the project - if(!await this.buildProjectTargets(buildLdmlKeyboard, '.xml', '.kmx')) { - return false; - } - - // Build all models in the project - if(!await this.buildProjectTargets(buildModel, '.model.ts', '.model.js')) { - return false; - } - - // Build all packages in the project - if(!await this.buildProjectTargets(buildPackage, '.kps', '.kmp')) { - return false; - } - - // TODO: generate .keyboard_info from .kps + etc (and support merge of - // $PROJECTPATH/.keyboard_info for version 1.0 projects) - - return true; - } - - loadProject(): KeymanDeveloperProject { - // TODO: callbacks.reportMessage on exceptions - // TODO: version 2.0 projects are folder-based and scan source/ folder for valid - // files. .kpj need not exist, but if it does, is used just for options. - - this.infile = path.resolve(this.infile.replace(/\\/g, '/')); - - if(fs.statSync(this.infile).isDirectory()) { - // This is a project folder, look for folder-name.kpj - this.infile = path.join(this.infile, path.basename(this.infile) + '.kpj'); - } - - const project = fs.existsSync(this.infile) ? - this.loadProjectFromFile() : - this.loadDefaultProjectFromFolder(); - - return project; - } - - loadDefaultProjectFromFolder() { - // The folder does not contain a .kpj, so construct a default 2.0 .kpj - const project = new KeymanDeveloperProject('2.0'); - project.populateFiles(this.infile); - return project; - } - - loadProjectFromFile(): KeymanDeveloperProject { - const kpjData = this.callbacks.loadFile(null, this.infile); - const reader = new KPJFileReader(); - const kpj = reader.read(kpjData); - const schema = this.callbacks.loadKpjJsonSchema(); - try { - reader.validate(kpj, schema); - } catch(e) { - // TODO: callbacks.reportMessage - console.error(e); - return null; - } - const project = reader.transform(this.infile, kpj); - return project; - } - - async buildProjectTargets( - buildTarget: (infile: string, options: BuildCommandOptions) => Promise, - fileType: '.xml'|'.kmn'|'.model.ts'|'.kps', - outputFileType: '.kmx'|'.model.js'|'.kmp' - ): Promise { - let result = true; - for(let file of this.project.files) { - if(file.fileType.toLowerCase() == fileType) { - result = await this.buildTarget(file, buildTarget, fileType, outputFileType) && result; - } - } - return result; - } - - async buildTarget( - file: KeymanDeveloperProjectFile, - buildTarget: (infile: string, options: BuildCommandOptions) => Promise, - fileType: '.xml'|'.kmn'|'.model.ts'|'.kps', - outputFileType: '.kmx'|'.model.js'|'.kmp' - ): Promise { - const options = {...this.options}; - options.outFile = this.project.resolveOutputFilePath(this.infile, file, fileType, outputFileType); - const infile = this.project.resolveInputFilePath(this.infile, file); - // TODO: callbacks.reportMessage, improve logging and make consistent - console.log(`Building ${infile}\n Output ${options.outFile}`); - - fs.mkdirSync(path.dirname(options.outFile), {recursive:true}); - - let result = await buildTarget(infile, options); - if(result) { - console.log(`${path.basename(infile )} built successfully.`); - } else { - console.log(`${path.basename(infile)} failed to build.`); - } - return result; - } - -} \ No newline at end of file diff --git a/developer/src/kmc/src/commands/build.ts b/developer/src/kmc/src/commands/build.ts index 857ba4b73791bc3734d67c2a0bfcb02188a68354..f21356577c2d953b3ba74eedae59c567dd9d5434 100644 --- a/developer/src/kmc/src/commands/build.ts +++ b/developer/src/kmc/src/commands/build.ts @@ -1,64 +1,76 @@ import * as fs from 'fs'; import { Command } from 'commander'; -import { buildPackage } from '../activities/buildPackage.js'; -import { buildKmnKeyboard } from '../activities/buildKmnKeyboard.js'; -import { buildLdmlKeyboard } from '../activities/buildLdmlKeyboard.js'; -import { buildModel } from '../activities/buildModel.js'; -import { buildProject } from '../activities/buildProject.js'; - -export interface BuildCommandOptions { - debug?: boolean; - outFile?: string; - compilerVersion?: boolean; -}; +import { BuildActivityOptions } from './build/BuildActivity.js'; +import { buildActivities } from './build/buildActivities.js'; +import { BuildProject, PROJECT_EXTENSION } from './build/BuildProject.js'; +import { NodeCompilerCallbacks } from '../messages/NodeCompilerCallbacks.js'; +import { InfrastructureMessages } from '../messages/messages.js'; export function declareBuild(program: Command) { program .command('build [infile...]') .description('Build a source file into a final file') .option('-d, --debug', 'Include debug information in output') - .option('-o, --out-file ', 'where to save the resulting .kmx file') + .option('-o, --out-file ', 'Override the default path and filename for the output file') .option('--no-compiler-version', 'Exclude compiler version metadata from output') - .action((infiles: string[], options: any) => { - let p = []; - if(!infiles.length) { - console.debug('Assuming infile == .'); - p.push(build('.', options)); + .option('-w, --compiler-warnings-as-errors', 'Causes warnings to fail the build') + .option('--no-warn-deprecated-code', 'Turn off warnings for deprecated code styles') + .action(async (filenames: string[], options: any) => { + if(!filenames.length) { + // If there are no filenames provided, then we are building the current + // folder ('.') as a project-style build + filenames.push('.'); } - for(let infile of infiles) { - p.push(build(infile, options)); + + for(let filename of filenames) { + if(!await build(filename, options)) { + // Once a file fails to build, we bail on subsequent builds + // TODO: is this the most appropriate semantics? + process.exit(1); + } } - return Promise.all(p).then(); }); } -async function build(infile: string, options: BuildCommandOptions): Promise { - console.log(`Building ${infile}`); +async function build(filename: string, options: BuildActivityOptions): Promise { + let callbacks = new NodeCompilerCallbacks(); - if(infile.endsWith('.xml')) { - return buildLdmlKeyboard(infile, options); - } + try { + callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename})); - if(infile.endsWith('.kmn')) { - return buildKmnKeyboard(infile, options); - } + if(!fs.existsSync(filename)) { + callbacks.reportMessage(InfrastructureMessages.Error_FileDoesNotExist({filename})); + return false; + } - if(infile.endsWith('.kps')) { - return buildPackage(infile, options); - } + let builder = null; - if(infile.endsWith('.model.ts')) { - return buildModel(infile, options); - } + // If infile is a directory, then we treat that as a project and build it + if(fs.statSync(filename).isDirectory() || filename.toLowerCase().endsWith(PROJECT_EXTENSION)) { + builder = new BuildProject(); + } else { + // Otherwise, if it's one of our known file extensions, we build it + let extensions: string[] = []; + builder = buildActivities.find(build => { + extensions.push(build.sourceExtension); + return filename.toLowerCase().endsWith(build.sourceExtension); + }); + if(!builder) { + callbacks.reportMessage(InfrastructureMessages.Error_FileTypeNotRecognized({filename, extensions: extensions.join(', ')})); + return false; + } + } - if(infile.endsWith('.kpj')) { - return buildProject(infile, options); - } + let result = await builder.build(filename, callbacks, options); + if(result) { + callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename})); + } else { + callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename})); + } - if(fs.statSync(infile).isDirectory()) { - return buildProject(infile, options); + return result; + } catch(e) { + callbacks.reportMessage(InfrastructureMessages.Fatal_UnexpectedException({e})); + return false; } - - console.error(`Unrecognised input file ${infile}, expecting .xml, .kmn, .kps, .model.ts, .kpj, or project folder`); - process.exit(2); } diff --git a/developer/src/kmc/src/commands/build/BuildActivity.ts b/developer/src/kmc/src/commands/build/BuildActivity.ts new file mode 100644 index 0000000000000000000000000000000000000000..83f5aa4aba3a1ff04db184425445da099918475d --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildActivity.ts @@ -0,0 +1,23 @@ +import { CompilerCallbacks } from "@keymanapp/common-types"; +import { escapeRegExp } from "../../util/escapeRegExp.js"; + +export interface BuildActivityOptions { + debug?: boolean; + outFile?: string; + compilerVersion?: boolean; + warnDeprecatedCode?: boolean; + compilerWarningsAsErrors?: boolean; +}; + +export abstract class BuildActivity { + public abstract get name(): string; + public abstract get sourceExtension(): string; + public abstract get compiledExtension(): string; + public abstract get description(): string; + public abstract build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise; + protected getOutputFilename(infile: string, options: BuildActivityOptions): string { + return options.outFile ? + options.outFile : + infile.replace(new RegExp(escapeRegExp(this.sourceExtension), "g"), this.compiledExtension); + } +}; \ No newline at end of file diff --git a/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts b/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts new file mode 100644 index 0000000000000000000000000000000000000000..9055e2862b7b1c81e52eea12bf3d552e9b1e2e0a --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts @@ -0,0 +1,52 @@ +import * as path from 'path'; +import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; +import { KmnCompiler } from '@keymanapp/kmc-kmn'; +import { platform } from 'os'; +import { CompilerCallbacks } from '@keymanapp/common-types'; + +export class BuildKmnKeyboard extends BuildActivity { + public get name(): string { return 'Keyman keyboard'; } + public get sourceExtension(): string { return '.kmn'; } + public get compiledExtension(): string { return '.kmx'; } + public get description(): string { return 'Build a Keyman keyboard'; } + public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { + let compiler = new KmnCompiler(); + if(!await compiler.init(callbacks)) { + return false; + } + + // We need to resolve paths to absolute paths before calling kmc-kmn + let outfile = this.getOutputFilename(infile, options); + + infile = getPosixAbsolutePath(infile); + outfile = getPosixAbsolutePath(outfile); + + // TODO: Currently this only builds .kmn->.kmx, and targeting .js is as-yet unsupported + // TODO: Support additional options compilerWarningsAsErrors, warnDeprecatedCode + return compiler.run(infile, outfile, + { + saveDebug: options.debug, + shouldAddCompilerVersion: options.compilerVersion, + warnDeprecatedCode: options.warnDeprecatedCode, + compilerWarningsAsErrors: options.compilerWarningsAsErrors, + }); + } +} + +function getPosixAbsolutePath(filename: string): string { + if(platform() == 'win32') { + // On Win32, we need to use backslashes for path.resolve to work + filename = filename.replace(/\//g, '\\'); + } + + // Resolve to a fully qualified absolute path + filename = path.resolve(filename); + + if(platform() == 'win32') { + // Ensure that we convert the result back to posix-style paths which is what + // kmc-kmn expects. On posix platforms, we assume paths have forward slashes + // already + filename = filename.replace(/\\/g, '/'); + } + return filename; +} diff --git a/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts new file mode 100644 index 0000000000000000000000000000000000000000..024f7f534002f6231322b561c7b36c0315debe04 --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts @@ -0,0 +1,87 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import * as kmcLdml from '@keymanapp/kmc-ldml'; +import { KvkFileWriter, CompilerCallbacks } from '@keymanapp/common-types'; +import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; + +export class BuildLdmlKeyboard extends BuildActivity { + public get name(): string { return 'LDML keyboard'; } + public get sourceExtension(): string { return '.xml'; } + public get compiledExtension(): string { return '.kmx'; } + public get description(): string { return 'Build a LDML keyboard'; } + public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { + // TODO-LDML: consider hardware vs touch -- touch-only layout will not have a .kvk + // Compile: + let [kmx,kvk,kmw] = buildLdmlKeyboardToMemory(infile, callbacks, options); + // Output: + + const fileBaseName = options.outFile ?? infile; + const outFileBase = path.basename(fileBaseName, path.extname(fileBaseName)); + const outFileDir = path.dirname(fileBaseName); + + if(kmx && kvk) { + const outFileKmx = path.join(outFileDir, outFileBase + '.kmx'); + console.log(`Writing compiled keyboard to ${outFileKmx}`); + fs.writeFileSync(outFileKmx, kmx); + + const outFileKvk = path.join(outFileDir, outFileBase + '.kvk'); + console.log(`Writing compiled visual keyboard to ${outFileKvk}`); + fs.writeFileSync(outFileKvk, kvk); + } else { + console.error(`An error occurred compiling ${infile}`); + return false; + } + + if(kmw) { + const outFileKmw = path.join(outFileDir, outFileBase + '.js'); + console.log(`Writing compiled js keyboard to ${outFileKmw}`); + fs.writeFileSync(outFileKmw, kmw); + } + + return true; + } +} + +function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): [Uint8Array, Uint8Array, Uint8Array] { + let compilerOptions: kmcLdml.CompilerOptions = { + debug: options.debug ?? false, + addCompilerVersion: options.compilerVersion ?? true, + // TODO: warnDeprecatedCode: options.warnDeprecatedCode, + // TODO: treatWarningsAsErrors: options.treatWarningsAsErrors, + } + + const k = new kmcLdml.LdmlKeyboardCompiler(callbacks, options); + let source = k.load(inputFilename); + if (!source) { + return [null, null, null]; + } + let kmx = k.compile(source); + if (!kmx) { + return [null, null, null]; + } + + // In order for the KMX file to be loaded by non-KMXPlus components, it is helpful + // to duplicate some of the metadata + kmcLdml.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions); + + // Use the builder to generate the binary output file + const builder = new kmcLdml.KMXBuilder(kmx, options.debug); + const kmx_binary = builder.compile(); + + const vkcompiler = new kmcLdml.LdmlKeyboardVisualKeyboardCompiler(); + const vk = vkcompiler.compile(source); + const writer = new KvkFileWriter(); + const kvk_binary = writer.write(vk); + + // Note: we could have a step of generating source files here + // KvksFileWriter()... + // const tlcompiler = new kmc.TouchLayoutCompiler(); + // const tl = tlcompiler.compile(source); + // const tlwriter = new TouchLayoutFileWriter(); + const kmwcompiler = new kmcLdml.LdmlKeyboardKeymanWebCompiler(callbacks, compilerOptions); + const kmw_string = kmwcompiler.compile(inputFilename, source); + const encoder = new TextEncoder(); + const kmw_binary = encoder.encode(kmw_string); + + return [kmx_binary, kvk_binary, kmw_binary]; +} diff --git a/developer/src/kmc/src/commands/build/BuildModel.ts b/developer/src/kmc/src/commands/build/BuildModel.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6819aa07b023c0c0ba0420c1589b68a968dc190 --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildModel.ts @@ -0,0 +1,33 @@ +import * as fs from 'fs'; +import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; +import { compileModel } from '@keymanapp/kmc-model'; +import { CompilerCallbacks } from '@keymanapp/common-types'; + +export class BuildModel extends BuildActivity { + public get name(): string { return 'Lexical model'; } + public get sourceExtension(): string { return '.model.ts'; } + public get compiledExtension(): string { return '.model.js'; } + public get description(): string { return 'Build a lexical model'; } + public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { + let outputFilename: string = this.getOutputFilename(infile, options); + let code = null; + + // Compile: + try { + code = compileModel(infile, callbacks); + } catch(e) { + console.error(e); + return false; + } + + if(!code) { + console.error('Compilation failed.') + return false; + } + + // Output: + fs.writeFileSync(outputFilename, code, 'utf8'); + + return true; + } +} \ No newline at end of file diff --git a/developer/src/kmc/src/commands/build/BuildPackage.ts b/developer/src/kmc/src/commands/build/BuildPackage.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c7c81f6a8bd2ba1358786e09203a7477764a53f --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildPackage.ts @@ -0,0 +1,46 @@ +import * as fs from 'fs'; +import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; +import { CompilerCallbacks } from '@keymanapp/common-types'; +import { KmpCompiler, PackageValidation } from '@keymanapp/kmc-package'; + +export class BuildPackage extends BuildActivity { + public get name(): string { return 'Package'; } + public get sourceExtension(): string { return '.kps'; } + public get compiledExtension(): string { return '.kmp'; } + public get description(): string { return 'Build a Keyman package'; } + public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { + + const outfile = this.getOutputFilename(infile, options); + + // + // Load .kps source data + // + + const kmpCompiler = new KmpCompiler(callbacks); + const kmpJsonData = kmpCompiler.transformKpsToKmpObject(infile); + if(!kmpJsonData) { + return false; + } + + // + // Validate the package file + // + + const validation = new PackageValidation(callbacks); + if(!validation.validate(infile, kmpJsonData)) { + return false; + } + + // + // Build the .kmp package file + // + + const data = await kmpCompiler.buildKmpFile(infile, kmpJsonData); + if(!data) { + return false; + } + + fs.writeFileSync(outfile, data, 'binary'); + return true; + } +} diff --git a/developer/src/kmc/src/commands/build/BuildProject.ts b/developer/src/kmc/src/commands/build/BuildProject.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6c7b249683546de38c58522d36be979b0d94dfd --- /dev/null +++ b/developer/src/kmc/src/commands/build/BuildProject.ts @@ -0,0 +1,131 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import { CompilerCallbacks, KeymanDeveloperProject, KPJFileReader } from '@keymanapp/common-types'; +import { KeymanDeveloperProjectFile } from '../../../../../../common/web/types/src/kpj/keyman-developer-project.js'; +import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; +import { buildActivities } from './buildActivities.js'; +import { InfrastructureMessages } from '../../messages/messages.js'; + +export const PROJECT_EXTENSION = '.kpj'; + +export class BuildProject extends BuildActivity { + public get name(): string { return 'Project'; } + public get sourceExtension(): string { return PROJECT_EXTENSION; } + public get compiledExtension(): string { return null; } + public get description(): string { return 'Build a keyboard or lexical model project'; } + public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { + let builder = new ProjectBuilder(infile, callbacks, options); + return builder.run(); + } +} + +class ProjectBuilder { + callbacks: CompilerCallbacks; + infile: string; + options: BuildActivityOptions; + project: KeymanDeveloperProject; + + constructor(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions) { + this.infile = path.resolve(infile); + this.callbacks = callbacks; + this.options = options; + } + + async run(): Promise { + if(this.options.outFile) { + this.callbacks.reportMessage(InfrastructureMessages.Error_OutFileNotValidForProjects()); + return false; + } + + this.project = this.loadProject(); + if(!this.project) { + return false; + } + + // Go through the various file types and build them + for(let builder of buildActivities) { + if(builder.sourceExtension == PROJECT_EXTENSION) { + // We don't support nested projects + continue; + } + + if(!await this.buildProjectTargets(builder)) { + return false; + } + } + + // TODO: generate .keyboard_info from .kps + etc (and support merge of + // $PROJECTPATH/.keyboard_info for version 1.0 projects) + + return true; + } + + loadProject(): KeymanDeveloperProject { + // TODO: callbacks.reportMessage on exceptions + // TODO: version 2.0 projects are folder-based and scan source/ folder for valid + // files. .kpj need not exist, but if it does, is used just for options. + + this.infile = path.resolve(this.infile.replace(/\\/g, '/')); + + if(fs.statSync(this.infile).isDirectory()) { + // This is a project folder, look for folder-name.kpj + this.infile = path.join(this.infile, path.basename(this.infile) + '.kpj'); + } + + const project = fs.existsSync(this.infile) ? + this.loadProjectFromFile() : + this.loadDefaultProjectFromFolder(); + + return project; + } + + loadDefaultProjectFromFolder() { + // The folder does not contain a .kpj, so construct a default 2.0 .kpj + const project = new KeymanDeveloperProject(this.infile, '2.0', this.callbacks); + project.populateFiles(); + return project; + } + + loadProjectFromFile(): KeymanDeveloperProject { + const kpjData = this.callbacks.loadFile(this.infile); + const reader = new KPJFileReader(this.callbacks); + const kpj = reader.read(kpjData); + const schema = this.callbacks.loadSchema('kpj'); + try { + reader.validate(kpj, schema); + } catch(e) { + this.callbacks.reportMessage(InfrastructureMessages.Error_InvalidProjectFile({message: (e??'').toString()})); + return null; + } + const project = reader.transform(this.infile, kpj); + return project; + } + + async buildProjectTargets(activity: BuildActivity): Promise { + let result = true; + for(let file of this.project.files) { + if(file.fileType.toLowerCase() == activity.sourceExtension) { + result = await this.buildTarget(file, activity) && result; + } + } + return result; + } + + async buildTarget(file: KeymanDeveloperProjectFile, activity: BuildActivity): Promise { + const options = {...this.options}; + options.outFile = this.project.resolveOutputFilePath(file, activity.sourceExtension, activity.compiledExtension); + const infile = this.project.resolveInputFilePath(file); + this.callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile})); + + fs.mkdirSync(path.dirname(options.outFile), {recursive:true}); + + let result = await activity.build(infile, this.callbacks, options); + if(result) { + this.callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: path.basename(infile)})); + } else { + this.callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: path.basename(infile)})); + } + return result; + } + +} \ No newline at end of file diff --git a/developer/src/kmc/src/commands/build/buildActivities.ts b/developer/src/kmc/src/commands/build/buildActivities.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e87f55e6cb9d70cea2fb701681d92db90a59da5 --- /dev/null +++ b/developer/src/kmc/src/commands/build/buildActivities.ts @@ -0,0 +1,18 @@ +import { BuildActivity } from './BuildActivity.js'; +import { BuildKmnKeyboard } from './BuildKmnKeyboard.js'; +import { BuildLdmlKeyboard } from './BuildLdmlKeyboard.js'; +import { BuildModel } from './BuildModel.js'; +import { BuildPackage } from './BuildPackage.js'; + +// These builders are listed in the order that files need to be built in +// projects. Packages depend on .kmn, .xml and .model.ts file types +export const buildActivities: BuildActivity[] = [ + new BuildKmnKeyboard(), + new BuildLdmlKeyboard(), + new BuildModel(), + new BuildPackage() +]; + +// Note: BuildProject is not listed here to avoid circular references, +// because it depends on the other activities here. This means that +// BuildProject must be separately checked. diff --git a/developer/src/kmc/src/commands/buildTestData.ts b/developer/src/kmc/src/commands/buildTestData.ts index 1e087214afeda71a0749a14b90520309d869308c..3878eedeee2e0cf540084c5ad2eb012a6100d26d 100644 --- a/developer/src/kmc/src/commands/buildTestData.ts +++ b/developer/src/kmc/src/commands/buildTestData.ts @@ -1,7 +1,7 @@ import { Command } from 'commander'; -import { buildTestData } from '../activities/buildTestData.js'; +import { buildTestData } from './buildTestData/index.js'; export function declareBuildTestData(program: Command) { program diff --git a/developer/src/kmc/src/activities/buildTestData.ts b/developer/src/kmc/src/commands/buildTestData/index.ts similarity index 70% rename from developer/src/kmc/src/activities/buildTestData.ts rename to developer/src/kmc/src/commands/buildTestData/index.ts index d0e2fcfaefeb82e17fd3df2aafb8fd4fe3695db1..0990b91edb55de750acdb544ffa0a6e1105cbd09 100644 --- a/developer/src/kmc/src/activities/buildTestData.ts +++ b/developer/src/kmc/src/commands/buildTestData/index.ts @@ -1,11 +1,14 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as kmc from '@keymanapp/kmc-keyboard'; +import * as kmc from '@keymanapp/kmc-ldml'; import { CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; -import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js'; -import { BuildCommandOptions } from '../commands/build.js'; +import { NodeCompilerCallbacks } from '../../messages/NodeCompilerCallbacks.js'; -export function buildTestData(infile: string, options: BuildCommandOptions) { +export interface BuildTestDataOptions { + outFile?: string; +}; + +export function buildTestData(infile: string, options: BuildTestDataOptions) { let compilerOptions: kmc.CompilerOptions = { debug: false, addCompilerVersion: false @@ -21,12 +24,12 @@ export function buildTestData(infile: string, options: BuildCommandOptions) { const outFileDir = path.dirname(fileBaseName); const outFileJson = path.join(outFileDir, outFileBase + '.json'); console.log(`Writing JSON test data to ${outFileJson}`); - fs - .writeFileSync(outFileJson, JSON.stringify(testData, null, ' ')); + fs.writeFileSync(outFileJson, JSON.stringify(testData, null, ' ')); } + function loadTestData(inputFilename: string, options: kmc.CompilerOptions): LDMLKeyboardTestDataXMLSourceFile { const c: CompilerCallbacks = new NodeCompilerCallbacks(); - const k = new kmc.Compiler(c, options); + const k = new kmc.LdmlKeyboardCompiler(c, options); let source = k.loadTestData(inputFilename); if (!source) { return null; diff --git a/developer/src/kmc/src/kmlmc.ts b/developer/src/kmc/src/kmlmc.ts index 8e17d76e8496588ffcea2e21d8e315cb1bf6dea0..ad607fcb473e8403b186608d06087b95b9cbdf96 100644 --- a/developer/src/kmc/src/kmlmc.ts +++ b/developer/src/kmc/src/kmlmc.ts @@ -8,6 +8,7 @@ import { Command } from 'commander'; import { compileModel } from '@keymanapp/kmc-model'; import { SysExits } from './util/sysexits.js'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { NodeCompilerCallbacks } from './messages/NodeCompilerCallbacks.js'; let inputFilename: string; const program = new Command(); @@ -27,10 +28,12 @@ if (!inputFilename) { exitDueToUsageError('Must provide a lexical model source file.'); } +const callbacks = new NodeCompilerCallbacks(); + let code = null; // Compile: try { - code = compileModel(inputFilename); + code = compileModel(inputFilename, callbacks); } catch(e) { console.error(e); process.exit(SysExits.EX_DATAERR); diff --git a/developer/src/kmc/src/kmlmi.ts b/developer/src/kmc/src/kmlmi.ts index b3549f4271e5c1bf8a58c2ed816692ffbf4db6f7..0868e0ccfe442c21351aa11485b421a7b88a2ff3 100644 --- a/developer/src/kmc/src/kmlmi.ts +++ b/developer/src/kmc/src/kmlmi.ts @@ -3,13 +3,13 @@ * kmlmi - Keyman Lexical Model model_info Compiler */ -import * as fs from 'fs'; import * as path from 'path'; import { Command } from 'commander'; -import KmpCompiler from '@keymanapp/kmc-package'; -import { ModelInfoOptions as ModelInfoOptions, writeMergedModelMetadataFile } from '@keymanapp/kmc-model-info'; +import { KmpCompiler, PackageValidation } from '@keymanapp/kmc-package'; +import { ModelInfoOptions, writeMergedModelMetadataFile } from '@keymanapp/kmc-model-info'; import { SysExits } from './util/sysexits.js'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { NodeCompilerCallbacks } from './messages/NodeCompilerCallbacks.js'; let inputFilename: string; const program = new Command(); @@ -45,9 +45,21 @@ let jsFilename = program.opts().jsFilename ? program.opts().jsFilename : path.jo // Load .kps source data // -let kpsString: string = fs.readFileSync(kpsFilename, 'utf8'); -let kmpCompiler = new KmpCompiler(); -let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsString, kpsFilename); +const callbacks = new NodeCompilerCallbacks(); +let kmpCompiler = new KmpCompiler(callbacks); +let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFilename); +if(!kmpJsonData) { + process.exit(1); +} + +// +// Validate the package file +// + +const validation = new PackageValidation(callbacks); +if(!validation.validate(kpsFilename, kmpJsonData)) { + process.exit(1); +} // // Write out the merged .model_info file diff --git a/developer/src/kmc/src/kmlmp.ts b/developer/src/kmc/src/kmlmp.ts index 6d51b4f58b930650a2833948e60605a8166ba1d7..5a54a57d71bd015e75337aee9617b104780a7900 100644 --- a/developer/src/kmc/src/kmlmp.ts +++ b/developer/src/kmc/src/kmlmp.ts @@ -3,18 +3,21 @@ * kmlmp - Keyman Lexical Model Package Compiler */ +// Note: this is a deprecated package and will be removed in Keyman 18.0 + import * as fs from 'fs'; import { Command } from 'commander'; -import KmpCompiler from '@keymanapp/kmc-package'; +import { PackageValidation, KmpCompiler } from '@keymanapp/kmc-package'; import { SysExits } from './util/sysexits.js'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { NodeCompilerCallbacks } from './messages/NodeCompilerCallbacks.js'; let inputFilename: string; const program = new Command(); /* Arguments */ program - .description('Compiles Keyman lexical model packages') + .description('Compiles Keyman lexical model packages\nDeprecated: use instead; will be removed in v18') .version(KEYMAN_VERSION.VERSION_WITH_TAG) .arguments('') .action(infile => inputFilename = infile) @@ -34,9 +37,21 @@ let outputFilename: string = program.opts().outFile ? program.opts().outFile : i // Load .kps source data // -let kpsString: string = fs.readFileSync(inputFilename, 'utf8'); -let kmpCompiler = new KmpCompiler(); -let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsString, inputFilename); +const callbacks = new NodeCompilerCallbacks(); +let kmpCompiler = new KmpCompiler(callbacks); +let kmpJsonData = kmpCompiler.transformKpsToKmpObject(inputFilename); +if(!kmpJsonData) { + process.exit(1); +} + +// +// Validate the package file +// + +const validation = new PackageValidation(callbacks); +if(!validation.validate(inputFilename, kmpJsonData)) { + process.exit(1); +} // // Build the .kmp package file diff --git a/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts new file mode 100644 index 0000000000000000000000000000000000000000..ceae18c4c59e7f8165a39460fab40558621fd211 --- /dev/null +++ b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts @@ -0,0 +1,69 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { CompilerCallbacks, CompilerSchema, CompilerEvent, compilerErrorSeverityName, CompilerPathCallbacks, CompilerFileSystemCallbacks } from '@keymanapp/common-types'; + +/** + * Concrete implementation for CLI use + */ + +export class NodeCompilerCallbacks implements CompilerCallbacks { + // TODO: REMOVE! + loadFile(filename: string | URL): Buffer { + try { + return fs.readFileSync(filename); + } catch (e) { + if (e.code === 'ENOENT') { + return null; + } else { + throw e; + } + } + } + + get path(): CompilerPathCallbacks { + return path; + } + + get fs(): CompilerFileSystemCallbacks { + return fs; + } + + reportMessage(event: CompilerEvent): void { + const code = event.code.toString(16); + if(event.line) { + console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`); + } else { + console.log(`${compilerErrorSeverityName(event.code)} ${code}: ${event.message}`); + } + } + + debug(msg: string) { + console.debug(msg); + } + + loadSchema(schema: CompilerSchema) { + let schemaPath = new URL('../util/' + schema + '.schema.json', import.meta.url); + return fs.readFileSync(schemaPath); + } + + fileExists(filename: string) { + return fs.existsSync(filename); + } + + resolveFilename(baseFilename: string, filename: string) { + const basePath = path.dirname(baseFilename); + // Transform separators to platform separators -- we are agnostic + // in our use here but path prefers files may use + // either / or \, although older kps files were always \. + if(path.sep == '/') { + filename = filename.replace(/\\/g, '/'); + } else { + filename = filename.replace(/\//g, '\\'); + } + if(!path.isAbsolute(filename)) { + filename = path.resolve(basePath, filename); + } + return filename; + } + +} diff --git a/developer/src/kmc/src/messages/messages.ts b/developer/src/kmc/src/messages/messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f1761e715d6f70f523d95d074b2eebab1fe1500 --- /dev/null +++ b/developer/src/kmc/src/messages/messages.ts @@ -0,0 +1,43 @@ +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.Infrastructure; +const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevHint = CompilerErrorSeverity.Hint | Namespace; +// const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +export class InfrastructureMessages { + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, + `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); + static FATAL_UnexpectedException = SevFatal | 0x0001; + + static Info_BuildingFile = (o:{filename:string}) => m(this.INFO_BuildingFile, + `Building ${o.filename}`); + static INFO_BuildingFile = SevInfo | 0x0002; + + static Error_FileDoesNotExist = (o:{filename:string}) => m(this.ERROR_FileDoesNotExist, + `File ${o.filename} does not exist`); + static ERROR_FileDoesNotExist = SevError | 0x0003; + + static Error_FileTypeNotRecognized = (o:{filename: string, extensions: string}) => m(this.ERROR_FileTypeNotRecognized, + `Unrecognised input file ${o.filename}, expecting ${o.extensions}, or project folder`); + static ERROR_FileTypeNotRecognized = SevError | 0x0004; + + static Error_OutFileNotValidForProjects = () => m(this.ERROR_OutFileNotValidForProjects, + `--out-file should not be specified for project builds`); + static ERROR_OutFileNotValidForProjects = SevError | 0x0005; + + static Info_FileBuiltSuccessfully = (o:{filename:string}) => m(this.INFO_FileBuiltSuccessfully, + `${o.filename} built successfully.`); + static INFO_FileBuiltSuccessfully = SevInfo | 0x0006; + + static Info_FileNotBuiltSuccessfully = (o:{filename:string}) => m(this.INFO_FileNotBuiltSuccessfully, + `${o.filename} failed to build.`); + static INFO_FileNotBuiltSuccessfully = SevInfo | 0x0007; + + static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile, + `Project file is not valid: ${o.message}`); + static ERROR_InvalidProjectFile = SevError | 0x0008; +} + diff --git a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts b/developer/src/kmc/src/util/NodeCompilerCallbacks.ts deleted file mode 100644 index a777b0a7d3c7ddbe87418b2d6be1849c6c7eda38..0000000000000000000000000000000000000000 --- a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as fs from 'fs'; -import * as kmc from '@keymanapp/kmc-keyboard'; -import { CompilerCallbacks, CompilerEvent } from '@keymanapp/common-types'; - -/** - * Concrete implementation for CLI use - */ -export class NodeCompilerCallbacks implements CompilerCallbacks { - loadFile(baseFilename: string, filename: string | URL): Buffer { - // TODO: translate filename based on the baseFilename - try { - return fs.readFileSync(filename); - } catch (e) { - if (e.code === 'ENOENT') { - return null; - } else { - throw e; - } - } - } - reportMessage(event: CompilerEvent): void { - console.log(kmc.CompilerMessages.severityName(event.code) + ' ' + event.code.toString(16) + ': ' + event.message); - } - loadLdmlKeyboardSchema(): Buffer { - let schemaPath = new URL('ldml-keyboard.schema.json', import.meta.url); - return fs.readFileSync(schemaPath); - } - loadLdmlKeyboardTestSchema(): Buffer { - let schemaPath = new URL('ldml-keyboardtest.schema.json', import.meta.url); - return fs.readFileSync(schemaPath); - } - loadKvksJsonSchema(): Buffer { - let schemaPath = new URL('kvks.schema.json', import.meta.url); - return fs.readFileSync(schemaPath); - } - loadKpjJsonSchema(): Buffer { - let schemaPath = new URL('kpj.schema.json', import.meta.url); - return fs.readFileSync(schemaPath); - } -} diff --git a/developer/src/kmc/src/util/escapeRegExp.ts b/developer/src/kmc/src/util/escapeRegExp.ts new file mode 100644 index 0000000000000000000000000000000000000000..8972b6bfedee501009c9857425eaf2741fe4d0d1 --- /dev/null +++ b/developer/src/kmc/src/util/escapeRegExp.ts @@ -0,0 +1,7 @@ + +const escapedRegexp = /[.*+?^${}()|[\]\\]/g; + +// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping +export function escapeRegExp(s: string) { + return s.replace(escapedRegexp, "\\$&"); // $& means the whole matched string +} diff --git a/developer/src/kmc/test/fixtures/.gitignore b/developer/src/kmc/test/fixtures/.gitignore index 13bb51d0488fc60ddd605ee5b133dae3663b0036..de01b143c30ef62192ed3231e735c7b34ca3c889 100644 --- a/developer/src/kmc/test/fixtures/.gitignore +++ b/developer/src/kmc/test/fixtures/.gitignore @@ -1 +1,3 @@ -*/*/build \ No newline at end of file +*/*/build +relative_paths/*.kmp +relative_paths/*.kmx diff --git a/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kmn b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b1304c20c8c89d7423199e27582b212fccdeb51d --- /dev/null +++ b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kmn @@ -0,0 +1,11 @@ +store(&NAME) '000 - null keyboard' +c Description: Tests null keyboard +c keys: [K_A][RALT K_B][SHIFT K_C] +c expected: aC +c context: + +store(&version) '6.0' + +begin Unicode > use(Main) + +group(Main) using keys diff --git a/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kpj b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kpj new file mode 100644 index 0000000000000000000000000000000000000000..0fd69d214c09db24a953a4d89467310747a9bbd2 --- /dev/null +++ b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kpj @@ -0,0 +1,34 @@ + + + + $PROJECTPATH + False + False + False + keyboard + + + + id_k_000___null_keyboard_1 + k_000___null_keyboard.kmn + k_000___null_keyboard.kmn + 1.0 + .kmn +
+ k_000___null_keyboard + Copyright (C) Keyman Team +
+
+ + id_k_000___null_keyboard_2 + k_000___null_keyboard.kps + k_000___null_keyboard.kps + + .kps +
+ k_000___null_keyboard + Copyright (C) Keyman Team +
+
+
+
diff --git a/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps new file mode 100644 index 0000000000000000000000000000000000000000..af65dd9269b0c054be8ca465e3e7058c2d581dec --- /dev/null +++ b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps @@ -0,0 +1,42 @@ + + + + 15.0 + 7.0 + + + + + + + + + + + + + k_000___null_keyboard + Copyright (C) Keyman Team + Keyman Team + 0.0 + + + + k_000___null_keyboard.kmx + + 0 + .kmx + + + + + k_000___null_keyboard + k_000___null_keyboard + 1.0 + + English + + + + + diff --git a/developer/src/kmc/test/helpers/index.ts b/developer/src/kmc/test/helpers/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8250dfd101a0a07fd2966cf3e5537ac3386a92f5 --- /dev/null +++ b/developer/src/kmc/test/helpers/index.ts @@ -0,0 +1,17 @@ +/** + * Helpers and utilities for the Mocha tests. + */ +import * as path from 'path'; +import { fileURLToPath } from 'url'; + +/** + * Builds a path to the fixture with the given path components. + * + * e.g., makePathToFixture('example.qaa.trivial') + * e.g., makePathToFixture('example.qaa.trivial', 'model.ts') + * + * @param components One or more path components. + */ + export function makePathToFixture(...components: string[]): string { + return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url)); +} diff --git a/developer/src/kmc/test/test-messages.ts b/developer/src/kmc/test/test-messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b591ad72de7a2e3dec348e55f52155c77030cbb --- /dev/null +++ b/developer/src/kmc/test/test-messages.ts @@ -0,0 +1,9 @@ +import 'mocha'; +import { InfrastructureMessages } from '../src/messages/messages.js'; +import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; + +describe('InfrastructureMessages', function () { + it('should have a valid InfrastructureMessages object', function() { + return verifyCompilerMessagesObject(InfrastructureMessages); + }); +}); diff --git a/developer/src/kmc/test/test-project-build.ts b/developer/src/kmc/test/test-project-build.ts new file mode 100644 index 0000000000000000000000000000000000000000..e16c9fd464db80b0cd808dde22aaf37a72707ca8 --- /dev/null +++ b/developer/src/kmc/test/test-project-build.ts @@ -0,0 +1,24 @@ +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { assert } from 'chai'; +import 'mocha'; +import { BuildProject } from '../src/commands/build/BuildProject.js'; +import { makePathToFixture } from './helpers/index.js'; + +const callbacks = new TestCompilerCallbacks(); + +describe('BuildProject', function () { + it('should build a keyboard project', async function() { + const builder = new BuildProject(); + const path = makePathToFixture('relative_paths', 'k_000___null_keyboard.kpj'); + let result = await builder.build(path, callbacks, { + compilerVersion: false, + compilerWarningsAsErrors: true, + debug: false, + warnDeprecatedCode: true, + }); + // 4 messages == starting build x 2, build successful x 2 + // callbacks.printMessages(); + assert.equal(callbacks.messages.length, 4); + assert.isTrue(result); + }); +}); diff --git a/developer/src/kmc/test/tsconfig.json b/developer/src/kmc/test/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..ac8680daf74315e98188cdc229fcd64510eabed8 --- /dev/null +++ b/developer/src/kmc/test/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../tsconfig.kmc-base.json", + + "compilerOptions": { + "rootDir": ".", + "rootDirs": ["./", "../src/"], + "outDir": "../build/test", + "baseUrl": ".", + "paths": { + "@keymanapp/common-types": ["../../../../common/web/types/src/main"], + "@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"], + }, + }, + "include": [ + "**/test-*.ts", + "./helpers/index.ts", + ], + "references": [ + { "path": "../../../../common/web/types/" }, + { "path": "../../common/web/test-helpers/" }, + { "path": "../" } + ] +} \ No newline at end of file diff --git a/developer/src/kmc/tsconfig.json b/developer/src/kmc/tsconfig.json index 41d2ab342860fb953ec7c720d5b7dcc3dcc5b3bd..9b6eb601acd121a775c5d7943a0c7477bb9e9fda 100644 --- a/developer/src/kmc/tsconfig.json +++ b/developer/src/kmc/tsconfig.json @@ -7,7 +7,8 @@ "baseUrl": ".", "paths": { "@keymanapp/common-types": [ "../../../common/web/types/src/main" ], - "@keymanapp/kmc-keyboard": [ "../kmc-keyboard/src/main" ], + "@keymanapp/kmc-kmn": [ "../kmc-kmn/src/main" ], + "@keymanapp/kmc-ldml": [ "../kmc-ldml/src/main" ], "@keymanapp/kmc-model": [ "../kmc-model/src/main" ], "@keymanapp/kmc-model-info": [ "../kmc-model-info/src/model-info-compiler" ], "@keymanapp/kmc-package": [ "../kmc-package/src/kmp-compiler" ], @@ -19,7 +20,8 @@ "references": [ { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, { "path": "../../../common/web/types" }, - { "path": "../kmc-keyboard" }, + { "path": "../kmc-kmn" }, + { "path": "../kmc-ldml" }, { "path": "../kmc-model" }, { "path": "../kmc-model-info" }, { "path": "../kmc-package" }, diff --git a/developer/src/kmcmpdll/CasedKeys.cpp b/developer/src/kmcmpdll/CasedKeys.cpp index 004b2c5ef74c23aed217a708a6bcf91d99421b79..3118f1be15026978ca736a6fb23d4f4779d1bf7a 100644 --- a/developer/src/kmcmpdll/CasedKeys.cpp +++ b/developer/src/kmcmpdll/CasedKeys.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include "../../../common/windows/cpp/include/vkeys.h" #include diff --git a/developer/src/kmcmpdll/CheckFilenameConsistency.cpp b/developer/src/kmcmpdll/CheckFilenameConsistency.cpp index c40302708b705b66de8462880be7399f3c27b9b6..1d553839f49ff830692ee8568f0c6375eda05548 100644 --- a/developer/src/kmcmpdll/CheckFilenameConsistency.cpp +++ b/developer/src/kmcmpdll/CheckFilenameConsistency.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include #include #include diff --git a/developer/src/kmcmpdll/CheckForDuplicates.cpp b/developer/src/kmcmpdll/CheckForDuplicates.cpp index 599c3400735e5c8f4d22dda89903a145647e7f3d..d22bfa80e6fd7445dc00398a0652032978c3fcb8 100644 --- a/developer/src/kmcmpdll/CheckForDuplicates.cpp +++ b/developer/src/kmcmpdll/CheckForDuplicates.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include #include "CheckForDuplicates.h" diff --git a/developer/src/kmcmpdll/CheckNCapsConsistency.cpp b/developer/src/kmcmpdll/CheckNCapsConsistency.cpp index 3d6d14315aef4ef2e1ee0821a3593ada607e338f..4c9e6853f99b50389cbd18d8e9be6aa6d8679811 100644 --- a/developer/src/kmcmpdll/CheckNCapsConsistency.cpp +++ b/developer/src/kmcmpdll/CheckNCapsConsistency.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include #include "CharToKeyConversion.h" diff --git a/developer/src/kmcmpdll/Compiler.cpp b/developer/src/kmcmpdll/Compiler.cpp index 6289f95d11a008f5f637c76d841cc0e4ea27bb93..8cc28d2ab7f18bfdc5e6e65dd78b99bf1fe8afe4 100644 --- a/developer/src/kmcmpdll/Compiler.cpp +++ b/developer/src/kmcmpdll/Compiler.cpp @@ -67,7 +67,7 @@ #include "pch.h" #include -#include +#include #include "../../../common/windows/cpp/include/vkeys.h" #include #include @@ -90,6 +90,8 @@ #include "UnreachableRules.h" #include "CheckForDuplicates.h" +#include "../kmcmplib/include/kmcmplibapi.h" + int xatoi(PWSTR *p); int atoiW(PWSTR p); void safe_wcsncpy(PWSTR out, PWSTR in, int cbMax); @@ -214,6 +216,7 @@ BOOL FSaveDebug, FCompilerWarningsAsErrors, FWarnDeprecatedCode; // I4865 // BOOL FShouldAddCompilerVersion = TRUE; BOOL FOldCharPosMatching = FALSE, FMnemonicLayout = FALSE; NamedCodeConstants *CodeConstants = NULL; + int BeginLine[4]; /* Compile target */ @@ -288,21 +291,33 @@ BOOL AddCompileMessage(DWORD msg) return FALSE; } -typedef struct _COMPILER_OPTIONS { - DWORD dwSize; - BOOL ShouldAddCompilerVersion; -} COMPILER_OPTIONS; - -typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS; +bool flag_use_new_kmcomp = true; // flag to switch to new kmcompx extern "C" BOOL __declspec(dllexport) SetCompilerOptions(PCOMPILER_OPTIONS options) { if(!options || options->dwSize < sizeof(COMPILER_OPTIONS)) { return FALSE; } + + flag_use_new_kmcomp = options->UseKmcmpLib; + + //printf("\n---> started in SetCompilerOptions() of kmcmpdll\n"); + if ( flag_use_new_kmcomp ) + { + KMCMP_COMPILER_OPTIONS kmcmp_options = {0}; + kmcmp_options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); + kmcmp_options.ShouldAddCompilerVersion = options->ShouldAddCompilerVersion; + return kmcmp_SetCompilerOptions(&kmcmp_options); + } + //printf("---> stayed in SetCompilerOptions() of kmcmpdll\n"); + FShouldAddCompilerVersion = options->ShouldAddCompilerVersion; return TRUE; } +int kmcmpMsgproc(int line, uint32_t dwMsgCode, char* szText, void* context) { + return static_cast(context)(line, dwMsgCode, szText); +} + extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL ASaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc) // I4865 // I4866 { HANDLE hInfile = INVALID_HANDLE_VALUE, hOutfile = INVALID_HANDLE_VALUE; @@ -310,6 +325,15 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR p DWORD len; char str[260]; + //printf("\n---> started in CompileKeyboardFile() of kmcmpdll\n"); + + if ( flag_use_new_kmcomp ) + { + return kmcmp_CompileKeyboardFile(pszInfile, pszOutfile, ASaveDebug, ACompilerWarningsAsErrors,AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc); + } + + //printf("---> stayed in CompileKeyboardFile() of kmcmpdll\n"); + FSaveDebug = ASaveDebug; FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 @@ -331,6 +355,8 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR p currentLine = 0; nErrors = 0; + AddCompileString("NOTE: Using legacy compiler"); + hInfile = CreateFileA(pszInfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hInfile == INVALID_HANDLE_VALUE) SetError(CERR_InfileNotExist); @@ -392,6 +418,15 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PSTR pszInfile DWORD len; char str[260]; + //printf("\n---> started in CompileKeyboardFileToBuffer() of kmcmpdll\n"); + if ( flag_use_new_kmcomp ) + { + return kmcmp_CompileKeyboardFileToBuffer( pszInfile, (void*) pfkBuffer, ACompilerWarningsAsErrors, AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc, Target); + } + + //printf("---> stayed in CompileKeyboardFileToBuffer() of kmcmpdll\n"); + + FSaveDebug = TRUE; // I3681 FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 @@ -413,6 +448,8 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PSTR pszInfile currentLine = 0; nErrors = 0; + AddCompileString("NOTE: Using legacy compiler"); + hInfile = CreateFileA(pszInfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hInfile == INVALID_HANDLE_VALUE) SetError(CERR_InfileNotExist); @@ -611,7 +648,6 @@ DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PWSTR p) if(BeginLine[BeginMode] != -1) { return CERR_RepeatedBegin; } - BeginLine[BeginMode] = currentLine; if ((msg = GetRHS(fk, p, tstr, 80, (int)(INT_PTR)(p - pp), FALSE)) != CERR_None) return msg; @@ -942,10 +978,23 @@ int cmpkeys(const void *key, const void *elem) { if (akey->Line < aelem->Line) return -1; if (akey->Line > aelem->Line) return 1; - return 0; + if(akey->Key == aelem->Key) { + if(akey->ShiftFlags == aelem->ShiftFlags) { + return akey->LineStoreIndex - aelem->LineStoreIndex; + } + return akey->ShiftFlags - aelem->ShiftFlags; + } + return akey->Key - aelem->Key; } if (l1 < l2) return 1; if (l1 > l2) return -1; + if(akey->Key == aelem->Key) { + if(akey->ShiftFlags == aelem->ShiftFlags) { + return akey->LineStoreIndex - aelem->LineStoreIndex; + } + return akey->ShiftFlags - aelem->ShiftFlags; + } + return akey->Key - aelem->Key; } return(char_key - char_elem); // akey->Key - aelem->Key); } @@ -1728,6 +1777,7 @@ CheckOutputIsReadonly(const PFILE_KEYBOARD fk, const PWSTR output) { // I4867 wcscpy_s(kp->dpContext, wcslen(pklIn) + 1, pklIn); // I3481 kp->Line = currentLine; + kp->LineStoreIndex = 0; // Finished if we are not using keys @@ -1869,6 +1919,7 @@ DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, DWORD storeIndex) k->ShiftFlags = 0; } k->Line = kpp->Line; + k->LineStoreIndex = (WORD)n; ExpandKp_ReplaceIndex(fk, k, keyIndex, n); } @@ -3315,6 +3366,7 @@ DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, HANDLE hOutfile) offset += gp->cxKeyArray * sizeof(COMP_KEY); for (j = 0; j < gp->cxKeyArray; j++, kp++, fkp++) { + kp->_reserved = 0; kp->Key = fkp->Key; if (FSaveDebug) kp->Line = fkp->Line; else kp->Line = 0; kp->ShiftFlags = fkp->ShiftFlags; diff --git a/developer/src/kmcmpdll/Compiler.rc b/developer/src/kmcmpdll/Compiler.rc index d5b2e5289fbd2f423797ecb3c7342c0bc7e7eeb1..4c58996cb191f619ce468ed9ddd06222b6a36971 100644 --- a/developer/src/kmcmpdll/Compiler.rc +++ b/developer/src/kmcmpdll/Compiler.rc @@ -1,5 +1,5 @@ #include -#include "comperr.h" +#include "kmn_compiler_errors.h" #include "version.rc" @@ -91,12 +91,6 @@ END STRINGTABLE BEGIN CERR_InvalidSystemStore "Invalid system store name found" - CERR_CallIsProfessionalFeature - "Call() is only available in Keyman Developer Professional" - CERR_IncludeCodesIsProfessionalFeature - "&IncludeCodes is only available in Keyman Developer Professional" - CERR_MnemonicLayoutIsProfessionalFeature - "&MnemonicLayout is only available in Keyman Developer Professional" CERR_60FeatureOnly_VirtualCharKey "Virtual character keys require VERSION 6.0" CERR_VersionAlreadyIncluded diff --git a/developer/src/kmcmpdll/DeprecationChecks.cpp b/developer/src/kmcmpdll/DeprecationChecks.cpp index a1e19cb27b743299f06730aaa1f96eb692399efe..d64f2279e06ac71e3e7cc79fd99141b4f2ffef9b 100644 --- a/developer/src/kmcmpdll/DeprecationChecks.cpp +++ b/developer/src/kmcmpdll/DeprecationChecks.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include BOOL WarnDeprecatedHeader() { // I4866 diff --git a/developer/src/kmcmpdll/NamedCodeConstants.cpp b/developer/src/kmcmpdll/NamedCodeConstants.cpp index 981e25700b4ebe057286eeb38fd87ea6bb3c046a..14f523b35ec5bc718c3f07742d7397a4deeee2d7 100644 --- a/developer/src/kmcmpdll/NamedCodeConstants.cpp +++ b/developer/src/kmcmpdll/NamedCodeConstants.cpp @@ -1,18 +1,18 @@ /* Name: NamedCodeConstants Copyright: Copyright (C) 2003-2017 SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 19 Jul 2011 Modified Date: 13 Dec 2012 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear 24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++ 06 Feb 2012 - mcdurdin - I3056 - If file was not found in first search folder, named code constants failed to compile @@ -22,7 +22,7 @@ */ #include "pch.h" -#include +#include #include "NamedCodeConstants.h" #include "CheckFilenameConsistency.h" #include "kmcmpdll.h" @@ -139,7 +139,7 @@ BOOL NamedCodeConstants::IntLoadFile(const char *filename) } fclose(fp); - + return TRUE; } @@ -262,17 +262,17 @@ int IsHangulSyllable(const wchar_t *codename, int *code) int i, LIndex, VIndex, TIndex; - /* Find initial */ + /* Find initial */ - int ch = towupper(*codename); + int ch = towupper(*codename); if(strchr("GNDRMBSJCKTPH", ch)) { - /* Has an initial syllable */ + /* Has an initial syllable */ int fdouble = towupper(*(codename+1)) == ch; LIndex = -1; for(i = 0; i < HangulLCount; i++) - if(Hangul_JAMO_L_TABLE[i][0] == ch && + if(Hangul_JAMO_L_TABLE[i][0] == ch && (!fdouble || (Hangul_JAMO_L_TABLE[i][1] == ch && fdouble))) { LIndex = i; @@ -282,9 +282,9 @@ int IsHangulSyllable(const wchar_t *codename, int *code) codename++; if(fdouble) codename++; } - else LIndex = 11; /* no initial */ + else LIndex = 11; /* no initial */ - /* Find vowel */ + /* Find vowel */ wchar_t V[4] = L""; V[0] = *codename; @@ -299,16 +299,16 @@ int IsHangulSyllable(const wchar_t *codename, int *code) codename += wcslen(V); - /* Find final */ + /* Find final */ TIndex = -1; - + for(i = 0; i < HangulTCount; i++) if(!_wcsicmp(Hangul_JAMO_T_TABLE[i], codename)) { TIndex = i; break; } if(TIndex == -1) return 0; - /* Composition */ + /* Composition */ *code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex; diff --git a/developer/src/kmcmpdll/UnreachableRules.cpp b/developer/src/kmcmpdll/UnreachableRules.cpp index a5c6519ef323ffb771cfe46ff86e05100d29c538..268360904d6a486260faf5c312f8c6b6d07221f7 100644 --- a/developer/src/kmcmpdll/UnreachableRules.cpp +++ b/developer/src/kmcmpdll/UnreachableRules.cpp @@ -2,7 +2,7 @@ #include "pch.h" #include -#include +#include #include "../../../common/windows/cpp/include/vkeys.h" #include @@ -16,7 +16,8 @@ std::wstring MakeHashKeyFromFileKey(PFILE_KEY kp) { std::wstringstream key; key << kp->Key << "," << kp->ShiftFlags << ","; - if (kp->dpContext) key << kp->dpContext; + if (kp->dpContext) + key << kp->dpContext; return key.str(); } diff --git a/developer/src/kmcmpdll/compfile.h b/developer/src/kmcmpdll/compfile.h index f58e041830645e5979c7b8ba48b360a7a0d9d663..9e65c36600e893f302a7c687b25ef7f998fda0dc 100644 --- a/developer/src/kmcmpdll/compfile.h +++ b/developer/src/kmcmpdll/compfile.h @@ -22,6 +22,13 @@ #include "../../../common/windows/cpp/include/legacy_kmx_file.h" + +// This file is deprecated; see kmcmplib/src/compfile.h. However, as long as +// this file continues to live, we need to keep structures in it in exact sync +// with kmcmplib/src/compfile.h, as FILE_KEYBOARD is passed between kmcmplib and +// kmcmpdll. KMX_WCHAR on Windows is identical to wchar_t, so it is safe to map +// accordingly. + #define LINESIZE 8192 #define GLOBAL_BUFSIZE 4096 @@ -70,9 +77,9 @@ enum FileStoreType { FST_STORE, FST_OPTION, FST_RESERVED }; struct FILE_STORE { - DWORD dwSystemID; - WCHAR szName[SZMAX_STORENAME]; // the name of the store - PWSTR dpString; // from start of store structure + DWORD dwSystemID; + WCHAR szName[SZMAX_STORENAME]; // the name of the store + PWSTR dpString; // from start of store structure //FileStoreType fstType; BOOL fIsStore; BOOL fIsReserved; @@ -80,76 +87,75 @@ struct FILE_STORE { BOOL fIsDebug; BOOL fIsCall; int line; // TODO: int vs dword, line vs Line (see FILE_KEY, FILE_GROUP) - }; +}; typedef FILE_STORE *PFILE_STORE; struct FILE_KEY { - WCHAR Key; // WCHAR for consistency; only a byte used however - DWORD Line; - DWORD ShiftFlags; - PWSTR dpOutput; // from start of key structure - PWSTR dpContext; // from start of key structure - }; - + WCHAR Key; // WCHAR for consistency; only a byte used however + WORD LineStoreIndex; + DWORD Line; + DWORD ShiftFlags; + PWSTR dpOutput; // from start of key structure + PWSTR dpContext; // from start of key structure +}; typedef FILE_KEY *PFILE_KEY; struct FILE_GROUP { - WCHAR szName[SZMAX_GROUPNAME]; - PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure - PWSTR dpMatch; // from start of group structure - PWSTR dpNoMatch; // from start of group structure - DWORD cxKeyArray; // in array items - BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not + WCHAR szName[SZMAX_GROUPNAME]; + PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure + PWSTR dpMatch; // from start of group structure + PWSTR dpNoMatch; // from start of group structure + DWORD cxKeyArray; // in array items + BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not BOOL fReadOnly; // group(xx) [readonly] <-- specified or not DWORD Line; }; typedef FILE_GROUP *PFILE_GROUP; -struct FILE_DEADKEY -{ - WCHAR szName[SZMAX_DEADKEYNAME]; +struct FILE_DEADKEY { + WCHAR szName[SZMAX_DEADKEYNAME]; }; typedef FILE_DEADKEY *PFILE_DEADKEY; -struct FILE_VKDICTIONARY -{ +struct FILE_VKDICTIONARY { WCHAR szName[SZMAX_VKDICTIONARYNAME]; }; - typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY; struct FILE_KEYBOARD { - DWORD KeyboardID; // as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts + DWORD KeyboardID; // deprecated, unused - DWORD version; // keyboard file version with VERSION keyword + DWORD version; // keyboard file version with VERSION keyword - PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure - PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure + PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure + PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure - DWORD cxStoreArray; // in number of items - DWORD cxGroupArray; // in number of items - DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] + DWORD cxStoreArray; // in number of items + DWORD cxGroupArray; // in number of items + DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] - DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) + DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) - WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name - WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name - WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information - WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard - PBYTE lpBitmap; - DWORD dwBitmapSize; - DWORD dwFlags; // Flags for the keyboard file + WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name + WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name + WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information + WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard + PBYTE lpBitmap; + DWORD dwBitmapSize; + DWORD dwFlags; // Flags for the keyboard file - DWORD currentGroup; // temp - current processing group - DWORD currentStore; // temp - current processing store - DWORD cxDeadKeyArray; - PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array + DWORD currentGroup; // temp - current processing group + DWORD currentStore; // temp - current processing store + DWORD cxDeadKeyArray; + PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array DWORD cxVKDictionary; PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary - }; + + void* extra; // used by kmcmplib and its consumers; unused in kmcmpdll +}; typedef FILE_KEYBOARD *PFILE_KEYBOARD; @@ -167,34 +173,34 @@ const DWORD sz_FILE_VKDICTIONARY = sizeof(FILE_VKDICTIONARY); const DWORD sz_FILE_KEYBOARD = sizeof(FILE_KEYBOARD); struct COMPMSG { - char szText[SZMAX_ERRORTEXT]; - DWORD Line; - DWORD dwMsgCode; - }; + char szText[SZMAX_ERRORTEXT]; + DWORD Line; + DWORD dwMsgCode; +}; typedef COMPMSG *PCOMPMSG; struct COMPILEMESSAGES { - int nMessages; - int nErrors; + int nMessages; + int nErrors; - PCOMPMSG cm; + PCOMPMSG cm; - DWORD fatalCode; - char szFatalText[SZMAX_ERRORTEXT]; + DWORD fatalCode; + char szFatalText[SZMAX_ERRORTEXT]; - DWORD currentLine; - }; + DWORD currentLine; +}; typedef COMPILEMESSAGES *PCOMPILEMESSAGES; /* struct TVersion { - //int MinVersion; // 0x0500 usually - //int CompilerVersion[4]; - //int MinCompilerVersion[4]; - int KeyboardVersion; // 0x0500 usually + //int MinVersion; // 0x0500 usually + //int CompilerVersion[4]; + //int MinCompilerVersion[4]; + int KeyboardVersion; // 0x0500 usually }; extern TVersion FVersionInfo; diff --git a/developer/src/kmcmpdll/json-validation.cpp b/developer/src/kmcmpdll/json-validation.cpp index 9516925ad0d9969896d76069b42a590768c34d00..e35dc0485dcae1480b00b1f002bad5fa75b56d26 100644 --- a/developer/src/kmcmpdll/json-validation.cpp +++ b/developer/src/kmcmpdll/json-validation.cpp @@ -1,9 +1,14 @@ #include #include +#include "../kmcmplib/include/kmcmplibapi.h" #include +typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); + +extern bool flag_use_new_kmcomp; + using nlohmann::json; using nlohmann::json_uri; using nlohmann::json_schema_draft4::json_validator; @@ -24,7 +29,19 @@ static void loader(const json_uri &uri, json &schema) } } +bool kmcmpMessageProc(int64_t offset, const char* szText, void* context) { + return ((ValidateJsonMessageProc)context)(offset, szText); +} + extern "C" BOOL __declspec(dllexport) ValidateJsonFile(PWSTR pwszSchemaFile, PWSTR pwszJsonFile, ValidateJsonMessageProc MessageProc) { + + if ( flag_use_new_kmcomp ) + { + std::fstream f(pwszSchemaFile); + std::fstream fd(pwszJsonFile); + return kmcmp_ValidateJsonFile(f, fd, kmcmpMessageProc, (void*)MessageProc); + } + std::fstream f(pwszSchemaFile); if (!f.good()) { MessageProc(-1, "Schema file could not be loaded."); diff --git a/developer/src/kmcmpdll/kcframe/kcframe.cpp b/developer/src/kmcmpdll/kcframe/kcframe.cpp index 4ac80bfe18011e36a6eb5bc909b3035f5af8afec..22402e52300d194079fae95422eea2ad6dd01d54 100644 --- a/developer/src/kmcmpdll/kcframe/kcframe.cpp +++ b/developer/src/kmcmpdll/kcframe/kcframe.cpp @@ -24,6 +24,8 @@ #include +extern "C" BOOL CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL FSaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc); // I4865 // I4866 + int WINAPI msgproc(int line, DWORD dwMsgCode, LPSTR szText) { printf("line %d error %x %s\n", line, (unsigned int) dwMsgCode, szText); diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj b/developer/src/kmcmpdll/kmcmpdll.vcxproj index 9d6a5f192149ecbdaa830786c2ba48122b6c6d6c..c7f7e1f48f062c068d8cc15e1846f86f25942d9f 100644 --- a/developer/src/kmcmpdll/kmcmpdll.vcxproj +++ b/developer/src/kmcmpdll/kmcmpdll.vcxproj @@ -87,23 +87,27 @@ - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator + $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) @@ -138,7 +142,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true @@ -186,7 +190,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true Windows @@ -229,7 +233,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true true @@ -273,7 +277,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true true @@ -289,7 +293,7 @@ - + diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters b/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters index 5ad97480ac6db26cab14da78078fcdc370f9158f..4131bedac41ce91cbaa45ce50d4739733d72a6c9 100644 --- a/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters +++ b/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters @@ -7,7 +7,7 @@ - + Header files diff --git a/developer/src/kmcmpdll/pch.h b/developer/src/kmcmpdll/pch.h index 473c0799a2edb47838fcbec0af7b810b444e933c..dcba976ea74dadadba3d36762e305c13a45f2cc9 100644 --- a/developer/src/kmcmpdll/pch.h +++ b/developer/src/kmcmpdll/pch.h @@ -11,12 +11,12 @@ #include #include -#include "../../../../common/windows/cpp/include/legacy_kmx_file.h" -#include "../../../../common/windows/cpp/include/xstring.h" +#include "../../../common/windows/cpp/include/legacy_kmx_file.h" +#include "../../../common/windows/cpp/include/xstring.h" -#include "../../../../common/windows/cpp/include/registry.h" -#include "../../../../common/windows/cpp/include/unicode.h" -#include "../../../../common/windows/cpp/include/crc32.h" +#include "../../../common/windows/cpp/include/registry.h" +#include "../../../common/windows/cpp/include/unicode.h" +#include "../../../common/windows/cpp/include/crc32.h" #include #include diff --git a/developer/src/kmcmpdll/versioning.cpp b/developer/src/kmcmpdll/versioning.cpp index 7604fb11b3a35bc42c7f02649119d307a2323452..1601e955632a8237530911f644dbcd785f339563 100644 --- a/developer/src/kmcmpdll/versioning.cpp +++ b/developer/src/kmcmpdll/versioning.cpp @@ -1,6 +1,6 @@ #include "pch.h" #include -#include +#include #include BOOL CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) { diff --git a/developer/src/kmcmplib/.gitignore b/developer/src/kmcmplib/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9418ac1e3e286b280fc3efd20658b21cc98f277d --- /dev/null +++ b/developer/src/kmcmplib/.gitignore @@ -0,0 +1,4 @@ +build/ +wasm.build +tests/keyboards/ +resources/ \ No newline at end of file diff --git a/developer/src/kmcmplib/Makefile b/developer/src/kmcmplib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4acc7d3cfbf792efae24e4782debc9c6d61fd0b0 --- /dev/null +++ b/developer/src/kmcmplib/Makefile @@ -0,0 +1,39 @@ +# +# KMCmpLib Makefile +# + +!include ..\Defines.mak + +!ifdef GIT_BASH_FOR_KEYMAN +CALLER=$(GIT_BASH_FOR_KEYMAN) +!else +CALLER=start /wait +!endif +!ifdef DEBUG +BUILD_DEBUG=-d +!else +BUILD_DEBUG= +!endif + +build: .virtual + $(CALLER) ./build.sh clean configure build $(BUILD_DEBUG) + +clean: + $(CALLER) ./build.sh clean $(BUILD_DEBUG) + +signcode: + @rem Not required + +wrap-symbols: + @rem Not required + +test-manifest: + @rem This target needed as dependency for TIKE and KMCMPDLL + +test: + $(CALLER) ./build.sh test $(BUILD_DEBUG) + +install: + @rem Not required + +!include ..\Target.mak diff --git a/developer/src/kmcmplib/build.bat b/developer/src/kmcmplib/build.bat new file mode 100644 index 0000000000000000000000000000000000000000..7b5dcf110d82a93e380f7daf0a3c91202897a64e --- /dev/null +++ b/developer/src/kmcmplib/build.bat @@ -0,0 +1,121 @@ +@echo off +rem **************************************************************** +rem Run build.sh, not this file directly, to build on Windows. +rem **************************************************************** +setlocal enabledelayedexpansion + +if "%1"=="" goto help +if "%1"=="all" goto all +if "%1"=="x86" goto build +if "%1"=="x64" goto build + +echo "Invalid parameter." +goto help + +rem ---------------------------------- + +:help +echo Usage: %0 x86^|x64^|all debug^|release [configure] [build] [test] [additional params for meson/ninja] +echo or +echo Usage: %0 x86^|x64 -c +echo -c will leave your environment configured for Visual Studio for selected platform. +echo. +echo Otherwise, %0 is intended to be used by build.sh, not directly. +echo At least one of 'configure', 'build', or 'test' is required. +goto :eof + +rem ---------------------------------- + +:all + +setlocal +cd %KEYMAN_ROOT%\developer\src\kmcmplib +cmd /c build.bat x86 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel! + +cd %KEYMAN_ROOT%\developer\src\kmcmplib +cmd /c build.bat x64 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel! + +goto :eof + +rem ---------------------------------- + +:build + +set ARCH=%1 +shift + +if "%1"=="-c" goto :setup + +echo === Locating Visual Studio === + +rem From https://github.com/microsoft/vswhere +for /f "usebackq delims=#" %%a in (`"%programfiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath`) do ( + set VsDevCmd_Path=%%a\Common7\Tools\VsDevCmd.bat +) + +if errorlevel 1 ( + echo vswhere failed [!errorlevel!] + exit /b !errorlevel! +) + +if not exist "!VsDevCmd_Path!" ( + echo Could not find vsdevcmd.bat [!VsDevCmd_Path!] + exit /b 1 +) + +echo === Configuring VC++ === +set VSCMD_SKIP_SENDTELEMETRY=1 +call "!VsDevCmd_Path!" -arch=!ARCH! -no_logo -startdir=none || exit !errorlevel! + +cd %KEYMAN_ROOT%\developer\src\kmcmplib + +set BUILDTYPE=%1 +shift + +set STATIC_LIBRARY=--default-library both + +set COMMAND=%1 +shift + +if "!COMMAND!" == "configure" ( + echo === Configuring Keyman KMX Compiler for Windows !ARCH! !BUILDTYPE! === + if exist build\!ARCH!\!BUILDTYPE! rd /s/q build\!ARCH!\!BUILDTYPE! + meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! --werror %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel! + shift +) + +if "!COMMAND!" == "build" ( + echo === Building Keyman KMX Compiler for Windows !ARCH! !BUILDTYPE! === + cd build\!ARCH!\!BUILDTYPE! || exit !errorlevel! + ninja %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel! + cd ..\..\.. + shift +) + +if "!COMMAND!" == "test" ( + echo === Testing Keyman KMX Compiler for Windows !ARCH! !BUILDTYPE! === + cd build\!ARCH!\!BUILDTYPE! || exit !errorlevel! + meson test --print-errorlogs %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel! + cd ..\..\.. + shift +) + +goto :eof + +rem ---------------------------------- + +:setup + +rem Standalone build, so we'll make the environment available to the caller +rem Also setup +rem Note: Visual Studio 2022 doesn't provide vcvarsall.bat, so we'll have to find a different solution +endlocal +for /f "usebackq delims=#" %%a in (`"%programfiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath`) do ( + set VsDevCmd_Path=%%a\Common7\Tools\VsDevCmd.bat +) + +set VSCMD_SKIP_SENDTELEMETRY=1 +"!VsDevCmd_Path!" -arch=!ARCH! -no_logo -startdir=none +goto :eof + +rem ---------------------------------- diff --git a/developer/src/kmcmplib/build.sh b/developer/src/kmcmplib/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..1ccd13575c42857614ed6576bf75a4939904de08 --- /dev/null +++ b/developer/src/kmcmplib/build.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +set -e +set -u + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$THIS_SCRIPT_PATH/checkout-keyboards.inc.sh" +. "$THIS_SCRIPT_PATH/commands.inc.sh" + +cd "$THIS_SCRIPT_PATH" + +################################ Main script ################################ + +# +# Restrict available targets to those that can be built on the current system +# + +archtargets=(":wasm WASM build") + +case $BUILDER_OS in + win) + archtargets+=( + ":x86 32-bit Windows (x86) build" + ":x64 64-bit Windows (x64) build" + ) + ;; + mac|linux) + archtargets+=( + ":arch Linux or mac build -- current architecture" + ) + ;; +esac + +# TODO: consider using "linux" and "mac" instead of "arch"? +# ":linux Build for current Linux architecture" +# ":mac Build for current macOS architecture" + +builder_describe \ + "Build Keyman KMX Compiler Static Library +Libraries will be built in 'build///src'. + * : 'debug' or 'release' (see --debug flag) + * All parameters after '--' are passed to meson or ninja +" \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "${archtargets[@]}" \ + "--full-test also run expensive tests that rely on keyboards repo" \ + "--test=opt_tests,-t test[s] to run (space separated)" + +builder_parse "$@" + +builder_describe_outputs \ + configure:x86 build/x86/$BUILDER_CONFIGURATION/build.ninja \ + configure:x64 build/x64/$BUILDER_CONFIGURATION/build.ninja \ + configure:arch build/arch/$BUILDER_CONFIGURATION/build.ninja \ + configure:wasm build/wasm/$BUILDER_CONFIGURATION/build.ninja \ + build:x86 build/x86/$BUILDER_CONFIGURATION/src/libkmcmplib.a \ + build:x64 build/x64/$BUILDER_CONFIGURATION/src/libkmcmplib.a \ + build:arch build/arch/$BUILDER_CONFIGURATION/src/libkmcmplib.a \ + build:wasm build/wasm/$BUILDER_CONFIGURATION/src/libkmcmplib.a + +TARGET_PATH="$THIS_SCRIPT_PATH/build" + +# Iterate through all possible targets; note that targets that cannot be built +# on the current platform have already been excluded through the archtargets +# settings above +targets=(wasm x86 x64 arch) + +do_action() { + local action_function=do_$1 + for target in "${targets[@]}"; do + MESON_PATH="$TARGET_PATH/$target/$BUILDER_CONFIGURATION" + $action_function $target + done +} + +if builder_has_option --full-test; then + locate_keyboards_repo +fi + +# Note, we have a 'global' clean and also a per-arch clean +if builder_start_action clean; then + rm -rf "$THIS_SCRIPT_PATH/tests/keyboards" + builder_finish_action success clean +fi + +do_action clean + +# Note, we have a 'global' configure and also a per-arch configure +if builder_start_action configure; then + # Import our standard compiler defines; this is copied from + # /resources/build/meson/standard.meson.build by build.sh, because meson doesn't + # allow us to reference a file outside its root + mkdir -p "$THIS_SCRIPT_PATH/resources" + cp "$KEYMAN_ROOT/resources/build/meson/standard.meson.build" "$THIS_SCRIPT_PATH/resources/meson.build" + + # We have to checkout the keyboards repo in a 'configure' action because + # otherwise meson will not get the right list of keyboard source files, + # even though we only use it in the 'test' action + if builder_has_option --full-test; then + checkout_keyboards + fi + + builder_finish_action success configure +fi + +do_action configure +do_action build +do_action test \ No newline at end of file diff --git a/developer/src/kmcmplib/checkout-keyboards.inc.sh b/developer/src/kmcmplib/checkout-keyboards.inc.sh new file mode 100644 index 0000000000000000000000000000000000000000..defcb479ef4070a759b2767115d1949e7cc78ac1 --- /dev/null +++ b/developer/src/kmcmplib/checkout-keyboards.inc.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# +# Look for the keyboards repo, either at $KEYBOARDS_ROOT, or, if that is not +# set, prepare to clone from GitHub. It will always be cloned to ./keyboards/ +# +function locate_keyboards_repo() { + if [[ ! -f "$THIS_SCRIPT_PATH/tests/keyboards_commit_ref.txt" ]]; then + builder_die "keyboards_commit_ref.txt does not exist, run prep.sh" + fi + + if [[ ! -d "$THIS_SCRIPT_PATH/tests/fixtures" ]]; then + builder_die "fixtures folder does not exist, run prep.sh" + fi + + if [[ -z "${KEYBOARDS_ROOT+x}" ]]; then + # KEYBOARDS_ROOT is not set, so we'll clone from GitHub + export KEYBOARDS_SOURCE_REPO=https://github.com/keymanapp/keyboards + builder_echo warning "\$KEYBOARDS_ROOT variable not set, using $KEYBOARDS_SOURCE_REPO" + builder_echo warning "For faster test builds, setup a local clone of that repo and set" + builder_echo warning "\$KEYBOARDS_ROOT to point to it" + else + builder_echo "\$KEYBOARDS_ROOT variable is $KEYBOARDS_ROOT" + export KEYBOARDS_SOURCE_REPO="$KEYBOARDS_ROOT" + fi +} + +# +# Shallow-clone of keyboards repo at commit found in keyboards_commit_ref.txt +# +function checkout_keyboards() { + local KEYBOARDS_COMMIT=$(cat $THIS_SCRIPT_PATH/tests/keyboards_commit_ref.txt) + + mkdir -p "$THIS_SCRIPT_PATH/tests/keyboards" + pushd "$THIS_SCRIPT_PATH/tests/keyboards" > /dev/null + + if [[ ! -f "build.sh" ]]; then + # We'll have to init the folder and clone the repo + builder_echo "Checking out keyboards repository at $THIS_SCRIPT_PATH/tests/keyboards" + git init + git remote add origin "$KEYBOARDS_SOURCE_REPO" + fi + + # Make sure we have fetched the commit, as it may have changed + # from a previous build + builder_echo "Fetching commit $KEYBOARDS_COMMIT in repo at $THIS_SCRIPT_PATH/tests/keyboards" + git fetch --depth=1 --no-tags origin "$KEYBOARDS_COMMIT" + git reset --hard "$KEYBOARDS_COMMIT" + + popd > /dev/null +} diff --git a/developer/src/kmcmplib/commands.inc.sh b/developer/src/kmcmplib/commands.inc.sh new file mode 100644 index 0000000000000000000000000000000000000000..bdb8e0f139bfa19b078db1e94e0508dbf24fb2a7 --- /dev/null +++ b/developer/src/kmcmplib/commands.inc.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +# ---------------------------------------------------------------------------- +# clean +# ---------------------------------------------------------------------------- + +do_clean() { + # clean: note build/ will be left, but build// should be gone + local target=$1 + builder_start_action clean:$target || return 0 + + rm -rf "$MESON_PATH" + builder_finish_action success clean:$target +} + +# ---------------------------------------------------------------------------- +# configure +# ---------------------------------------------------------------------------- + +do_configure() { + local target=$1 + builder_start_action configure:$target || return 0 + + local STANDARD_MESON_ARGS= + + if [[ $target == wasm ]]; then + # do_configure_wasm + locate_emscripten + build_meson_cross_file_for_wasm + STANDARD_MESON_ARGS="--cross-file wasm.defs.build --cross-file wasm.build --default-library static" + fi + + if [[ $target =~ ^(x86|x64)$ ]]; then + cmd //C build.bat $target $BUILDER_CONFIGURATION configure "${builder_extra_params[@]}" + else + pushd "$THIS_SCRIPT_PATH" > /dev/null + # Additional arguments are used by Linux build, e.g. -Dprefix=${INSTALLDIR} + meson setup "$MESON_PATH" --werror --buildtype $BUILDER_CONFIGURATION $STANDARD_MESON_ARGS "${builder_extra_params[@]}" + popd > /dev/null + fi + builder_finish_action success configure:$target +} + +# ---------------------------------------------------------------------------- +# build +# ---------------------------------------------------------------------------- + +do_build() { + local target=$1 + builder_start_action build:$target || return 0 + + if [[ $target =~ ^(x86|x64)$ ]]; then + # Build the meson targets, both x86 and x64 also + # We need to use a batch file here so we can get + # the Visual Studio build environment with vcvarsall.bat + # TODO: if PATH is the only variable required, let's try and + # eliminate this difference in the build process + cmd //C build.bat $target $BUILDER_CONFIGURATION build "${builder_extra_params[@]}" + else + pushd "$MESON_PATH" > /dev/null + ninja + popd > /dev/null + fi + builder_finish_action success build:$target +} + +# ---------------------------------------------------------------------------- +# test +# ---------------------------------------------------------------------------- + +do_test() { + local target=$1 + builder_start_action test:$target || return 0 + + # Works on a local clone of keyboards repository, to avoid clobbering + # user's existing keyboards repo, if present + + if builder_has_option --full-test; then + checkout_keyboards + fi + + if [[ $target =~ ^(x86|x64)$ ]]; then + cmd //C build.bat $target $BUILDER_CONFIGURATION test "${builder_extra_params[@]}" + else + pushd "$MESON_PATH" > /dev/null + meson test "${builder_extra_params[@]}" + popd > /dev/null + fi + builder_finish_action success test:$target +} + +# ---------------------------------------------------------------------------- +# install and uninstall +# ---------------------------------------------------------------------------- + +do_install() { + do_command install $1 +} + +do_uninstall() { + do_command uninstall $1 +} + +do_command() { + local command=$1 + local target=$2 + builder_start_action $command:$target || return 0 + pushd "$MESON_PATH" > /dev/null + ninja $command + popd > /dev/null + builder_finish_action success $command:$target +} + +# ---------------------------------------------------------------------------- +# utility functions +# ---------------------------------------------------------------------------- + +# +# We don't want to rely on emcc being on the path, because Emscripten puts far +# too many things onto the path (in particular for us, node). +# +# The following comment suggests that we don't need emcc on the path. +# https://github.com/emscripten-core/emscripten/issues/4848#issuecomment-1097357775 +# +# So we try and locate emcc in common locations ourselves. The search pattern +# is: +# +# 1. Look for $EMSCRIPTEN_BASE (our primary emscripten variable), which should +# point to the folder that emcc is located in +# 2. Look for $EMCC which should point to the emcc executable +# 3. Look for emcc on the path +# +locate_emscripten() { + if [[ -z ${EMSCRIPTEN_BASE+x} ]]; then + if [[ -z ${EMCC+x} ]]; then + local EMCC=`which emcc` + [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (emcc) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" + fi + [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable emcc" + EMSCRIPTEN_BASE="$(dirname "$EMCC")" + fi + + [[ -x ${EMSCRIPTEN_BASE}/emcc ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to emcc's folder" +} + +build_meson_cross_file_for_wasm() { + if [ $BUILDER_OS == win ]; then + local R=$(cygpath -w $(echo $EMSCRIPTEN_BASE) | sed 's_\\_\\\\_g') + else + local R=$(echo $EMSCRIPTEN_BASE | sed 's_/_\\/_g') + fi + sed -e "s/\$EMSCRIPTEN_BASE/$R/g" wasm.build.$BUILDER_OS.in > wasm.build +} \ No newline at end of file diff --git a/developer/src/kmcmplib/getversion.bat b/developer/src/kmcmplib/getversion.bat new file mode 100644 index 0000000000000000000000000000000000000000..dcab02e4d95e572491d8648757ed9fffa50e0850 --- /dev/null +++ b/developer/src/kmcmplib/getversion.bat @@ -0,0 +1,3 @@ +@echo off +REM Get the version number from VERSION.md +more ..\..\..\VERSION.md diff --git a/developer/src/kmcmplib/getversion.sh b/developer/src/kmcmplib/getversion.sh new file mode 100755 index 0000000000000000000000000000000000000000..cd02c07929fd1f8707c2ecfcb4f874a3d9373700 --- /dev/null +++ b/developer/src/kmcmplib/getversion.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Get the version number from VERSION.md +VERSIONFILE=../../../VERSION.md +cat $VERSIONFILE diff --git a/developer/src/kmcmplib/include/keyman_version.h.in b/developer/src/kmcmplib/include/keyman_version.h.in new file mode 100644 index 0000000000000000000000000000000000000000..e55e7b70c745f32f547d79e322b0300450b58ee2 --- /dev/null +++ b/developer/src/kmcmplib/include/keyman_version.h.in @@ -0,0 +1,9 @@ +#define keyman_version_stringify(x) keyman_version_to_string(x) +#define keyman_version_to_string(x) #x + +// Product versioning + +#define KEYMAN_VERSION_MAJOR @majorver@ +#define KEYMAN_VERSION_MINOR @minorver@ +#define KEYMAN_VERSION_PATCH @patchver@ +#define KEYMAN_VERSION_STRING keyman_version_stringify(@majorver@ ## . ## @minorver@ ## . ## @patchver@ ## .0) diff --git a/developer/src/kmcmplib/include/kmcmplibapi.h b/developer/src/kmcmplib/include/kmcmplibapi.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb35979b2def0839a4f36fa552c76abe8604cfe --- /dev/null +++ b/developer/src/kmcmplib/include/kmcmplibapi.h @@ -0,0 +1,106 @@ +#pragma once + +#include + +#ifdef __EMSCRIPTEN__ +#include +#include + +#else +#define EMSCRIPTEN_KEEPALIVE +#endif + +#ifdef __cplusplus +#define EXTERN extern "C" EMSCRIPTEN_KEEPALIVE +#else +#define EXTERN EMSCRIPTEN_KEEPALIVE +#endif + +typedef struct _KMCMP_COMPILER_OPTIONS { + uint32_t dwSize; + bool ShouldAddCompilerVersion; +} KMCMP_COMPILER_OPTIONS; + +EXTERN bool kmcmp_SetCompilerOptions( + KMCMP_COMPILER_OPTIONS* options +); + +typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, char* szText, void* context); + +EXTERN bool kmcmp_CompileKeyboardFile( + char* pszInfile, + char* pszOutfile, + bool ASaveDebug, + bool ACompilerWarningsAsErrors, + bool AWarnDeprecatedCode, + kmcmp_CompilerMessageProc pMsgproc, + void* AmsgprocContext +); + +/* Compile target */ + +#define CKF_KEYMAN 0 +#define CKF_KEYMANWEB 1 + +EXTERN bool kmcmp_CompileKeyboardFileToBuffer( + char* pszInfile, + void* pfkBuffer, + bool ACompilerWarningsAsErrors, + bool AWarnDeprecatedCode, + kmcmp_CompilerMessageProc pMsgproc, + void* AmsgprocContext, + int Target +); + +typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); + +EXTERN bool kmcmp_ValidateJsonFile( + std::fstream& f, + std::fstream& fd, + kmcmp_ValidateJsonMessageProc MessageProc, + void* context +); + +/** + * kmcmp_ParseUnicodeSet is successful if it returns >= USET_OK + */ +static const int KMCMP_USET_OK = 0; + +/** + * Error: Unknown syntax err, failed to parse + */ +static const int KMCMP_ERROR_SYNTAX_ERR = -1; +/** + * Error: Invalid, contains strings (`{abc}` form) + */ +static const int KMCMP_ERROR_HAS_STRINGS = -2; +/** + * Error: Invalid, uses properties \p{Mn} or [:Mn:] + */ +static const int KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3; +/** + * Fatal: output buffer too small + */ +static const int KMCMP_FATAL_OUT_OF_RANGE = -4; + +/** + * Function pointer to kmcmp_ParseUnicodeSet + */ +typedef int (*kmcmp_ParseUnicodeSetProc)(const char* szText, uint32_t* output, uint32_t outputLength); + +/** + * Parse a UnicodeSet into 32-bit ranges. + * For example, "[]" will return 0 (KMCMP_USET_OK) as a zero-length set. + * "[" will return KMCMP_ERROR_SYNTAX_ERR, + * and "[x A-C]" will return 2 and [0x41, 0x43, 0x78, 0x78] + * @param szText input txt, null terminated, in UTF-8 format + * @param outputBuffer output buffer, owned by caller: Pairs of ranges in order + * @param outputBufferSize length of output buffer. Needs to be twice the number of expected ranges + * @return If >= KMCMP_USET_OK, number of ranges, otherwise one of the negative error values. + */ +EXTERN int kmcmp_ParseUnicodeSet( + const char* szText, + uint32_t* outputBuffer, + uint32_t outputBufferSize +); + diff --git a/developer/src/kmcmplib/include/kmcompx.h b/developer/src/kmcmplib/include/kmcompx.h new file mode 100644 index 0000000000000000000000000000000000000000..a1403ad02fdaea0839023ff900bf0efdc26f36e7 --- /dev/null +++ b/developer/src/kmcmplib/include/kmcompx.h @@ -0,0 +1,49 @@ +#pragma once +#include + +// TODO: merge with kmcmplib.h + +typedef wchar_t KMX_WCHART; +typedef KMX_DWORD * PKMX_DWORD; +typedef char * PKMX_STR; +typedef KMX_WCHAR* PKMX_WCHAR ; + +// TODO: these defines are copied out of unicode.h, because unicode.h still +// has windows-specific types. These should be remerged at a future date + +#define Uni_IsSurrogate1(ch) ((ch) >= 0xD800 && (ch) <= 0xDBFF) +#define Uni_IsSurrogate2(ch) ((ch) >= 0xDC00 && (ch) <= 0xDFFF) +#define Uni_IsSMP(ch) ((ch) >= 0x10000) + +#define Uni_SurrogateToUTF32(ch, cl) (((ch) - 0xD800) * 0x400 + ((cl) - 0xDC00) + 0x10000) + +#define Uni_UTF32ToSurrogate1(ch) (((ch) - 0x10000) / 0x400 + 0xD800) +#define Uni_UTF32ToSurrogate2(ch) (((ch) - 0x10000) % 0x400 + 0xDC00) + +#define Uni_UTF16ToUTF32(p) (Uni_IsSurrogate1(*(p)) && Uni_IsSurrogate2(*((p)+1)) ? Uni_SurrogateToUTF32(*(p), *((p)+1)) : *(p)) + +// +// C++17 definition of Windows _countof +// TODO: remove use of _countof throughout +// + +#ifndef _MSC_VER +#include + +template < typename T, size_t N > +size_t _countof( T ( & /*arr*/ )[ N ] ) +{ + return std::extent< T[ N ] >::value; +} + +#endif + +// For MSVC, we want to use the std CRT library functions +// and ignore security warnings at this time, so we are +// not forced to use the non-portable _s variants. +#define _CRT_SECURE_NO_DEPRECATE + +// strtok_s on MSVC works the same as strtok_r +#ifdef _MSC_VER +#define strtok_r strtok_s +#endif diff --git a/developer/src/kmcmplib/include/meson.build b/developer/src/kmcmplib/include/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f98a4d8c762da2134f33054a4b18948aa3a5e658 --- /dev/null +++ b/developer/src/kmcmplib/include/meson.build @@ -0,0 +1,26 @@ +# +# Keyman is copyright (C) SIL International. MIT License. +# +# Cross platform build script to build headers +# + +project_ver = meson.project_version().split('.') + +cfg = configuration_data() +cfg.set('majorver', project_ver[0]) +cfg.set('minorver', project_ver[1]) +cfg.set('patchver', project_ver[2]) + +configure_file( + configuration: cfg, + input: 'keyman_version.h.in', + output: 'keyman_version.h', +) + +inc = include_directories( + '.', + '../../common/include', + '../../../../common/include', + '../../ext/json', + '../../ext/json-schema-validator' +) diff --git a/developer/src/kmcmplib/meson.build b/developer/src/kmcmplib/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c996981f47a896530db7f9070c90467ce98a1315 --- /dev/null +++ b/developer/src/kmcmplib/meson.build @@ -0,0 +1,25 @@ +# +# Keyman is copyright (C) SIL International. MIT License. +# +# Cross platform build script to compile kmcmplib and tests +# + +project('kmcmplib', 'cpp', 'c', + version: run_command(find_program('getversion.bat', 'getversion.sh')).stdout().strip(), + license: 'MIT', + default_options : ['buildtype=release', + 'cpp_std=c++14', + 'b_vscrt=static_from_buildtype', + 'default_library=static', + 'debug=true']) + +# Import our standard compiler defines; this is copied from +# /resources/build/standard.meson.build by build.sh, because +# meson doesn't allow us to reference a file outside its root +subdir('resources') + +# ------------------------------------------------------------ + +subdir('include') +subdir('src') +subdir('tests') diff --git a/developer/src/kmcmplib/meson_options.txt b/developer/src/kmcmplib/meson_options.txt new file mode 100644 index 0000000000000000000000000000000000000000..bef0777abee7e38e97d3a8339a18e4ec45cfb62b --- /dev/null +++ b/developer/src/kmcmplib/meson_options.txt @@ -0,0 +1 @@ +option('full_test', type: 'boolean', value: false) \ No newline at end of file diff --git a/developer/src/kmcmplib/src/CasedKeys.cpp b/developer/src/kmcmplib/src/CasedKeys.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88d3b190ab36d9871a998f04069f69e3baaaca54 --- /dev/null +++ b/developer/src/kmcmplib/src/CasedKeys.cpp @@ -0,0 +1,154 @@ + +#include "pch.h" + +#include "compfile.h" +#include "kmn_compiler_errors.h" +#include "../../../../common/windows/cpp/include/vkeys.h" +#include "kmcmplib.h" + +#include "CharToKeyConversion.h" +#include "kmx_u16.h" +#include "xstring.h" + +namespace kmcmp { + extern KMX_BOOL FMnemonicLayout; // TODO: these globals should be consolidated one day +} + +KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp); + +KMX_DWORD VerifyCasedKeys(PFILE_STORE sp) { + assert(sp != NULL); + + if (kmcmp::FMnemonicLayout) { + // The &CasedKeys system store is not supported for + // mnemonic layouts in 14.0 + return CERR_CasedKeysNotSupportedWithMnemonicLayout; + } + + // We will rewrite this store with virtual keys + + PKMX_WCHAR p = sp->dpString; + PKMX_WCHAR buf = new KMX_WCHAR[u16len(p) * 5 + 1]; // extended keys are 5 units long, so this is the max length + PKMX_WCHAR q = buf; + + while (*p) { + KMX_UINT key = 0, shift = 0; + if (*p != UC_SENTINEL) { + if (!kmcmp::MapUSCharToVK(*p, &key, &shift)) { + return CERR_CasedKeysMustContainOnlyVirtualKeys; + } + if (shift & K_SHIFTFLAG) { + return CERR_CasedKeysMustNotIncludeShiftStates; + } + } + else { + if (*(p + 1) != CODE_EXTENDED) { + return CERR_CasedKeysMustContainOnlyVirtualKeys; + } + shift = *(p + 2); + key = *(p + 3); + if (shift != ISVIRTUALKEY) { + return CERR_CasedKeysMustNotIncludeShiftStates; + } + } + *q++ = UC_SENTINEL; + *q++ = CODE_EXTENDED; + *q++ = shift; + *q++ = key; + *q++ = UC_SENTINEL_EXTENDEDEND; + *q = 0; + + p = incxstr(p); + } + + delete[] sp->dpString; + sp->dpString = buf; + + return CERR_None; +} + +KMX_DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) { + assert(fk != NULL); + assert(gp != NULL); + + if (kmcmp::FMnemonicLayout) { + // The &CasedKeys system store is not supported for + // mnemonic layouts in 14.0 + return CERR_None; + } + + PFILE_STORE sp = FindSystemStore(fk, TSS_CASEDKEYS); + if (!sp) { + // If there is no &CasedKeys system store, then we do not + // process the key + return CERR_None; + } + + KMX_DWORD msg; + // ExpandCapsRule may add extra rules at the end of gp->dpKeyArray, + // reallocating it, so we (a) cache the original length, and (b) + // dereference the array every call + int cxKeyArray = gp->cxKeyArray; + for (int i = 0; i < cxKeyArray; i++) { + if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != CERR_None) { + return msg; + } + } + return CERR_None; +} + +KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) { + KMX_UINT key = kpp->Key; + KMX_UINT shift = kpp->ShiftFlags; + + if (shift == 0) { + // Convert US key cap to a virtual key + if (!kmcmp::MapUSCharToVK(kpp->Key, &key, &shift)) { + return CERR_None; + } + } + + if (shift & (CAPITALFLAG | NOTCAPITALFLAG)) { + // Don't attempt expansion if either Caps Lock flag is specified in the key rule + return CERR_None; + } + + PKMX_WCHAR p = sp->dpString; + for (; *p; p = incxstr(p)) { + // We've already verified that the store contains only virtual keys in VerifyCasedKeys + if (*(p + 3) == key) { + break; + } + } + + if (!*p) { + // This key is not modified by Caps Lock + return CERR_None; + } + + // This key is modified by Caps Lock, so we need to duplicate this rule + PFILE_KEY k = new FILE_KEY[gp->cxKeyArray + 1]; + if (!k) return CERR_CannotAllocateMemory; + memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); + + kpp = &k[(int)(kpp - gp->dpKeyArray)]; + + delete gp->dpKeyArray; + gp->dpKeyArray = k; + gp->cxKeyArray++; + + k = &k[gp->cxKeyArray - 1]; + k->dpContext = new KMX_WCHAR[u16len(kpp->dpContext) + 1]; + k->dpOutput = new KMX_WCHAR[u16len(kpp->dpOutput) + 1]; + u16cpy(k->dpContext, kpp->dpContext ); // copy the context. + u16cpy(k->dpOutput, kpp->dpOutput ); // copy the output. + + k->Key = key; + k->Line = kpp->Line; + // Add the CAPITAL FLAG, invert shift flag for the rule + k->ShiftFlags = (shift ^ K_SHIFTFLAG) | CAPITALFLAG; + kpp->Key = key; + kpp->ShiftFlags = shift | NOTCAPITALFLAG; + + return CERR_None; +} diff --git a/developer/src/kmcmplib/src/CasedKeys.h b/developer/src/kmcmplib/src/CasedKeys.h new file mode 100644 index 0000000000000000000000000000000000000000..e97cc593898cd32b3dcb516bd4bb660b49f9ef6a --- /dev/null +++ b/developer/src/kmcmplib/src/CasedKeys.h @@ -0,0 +1,4 @@ +#pragma once + +KMX_DWORD VerifyCasedKeys(PFILE_STORE sp); +KMX_DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp); diff --git a/developer/src/kmcmplib/src/CharToKeyConversion.cpp b/developer/src/kmcmplib/src/CharToKeyConversion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c2b652d095d57ed9a647e942902df00243e482b --- /dev/null +++ b/developer/src/kmcmplib/src/CharToKeyConversion.cpp @@ -0,0 +1,207 @@ + +#include "pch.h" +#include "CharToKeyConversion.h" + +/* Following VKeys come from Windows */ + +#define VK_COLON 0xBA +#define VK_EQUAL 0xBB +#define VK_COMMA 0xBC +#define VK_HYPHEN 0xBD +#define VK_PERIOD 0xBE +#define VK_SLASH 0xBF +#define VK_ACCENT 0xC0 +#define VK_LBRKT 0xDB +#define VK_BKSLASH 0xDC +#define VK_RBRKT 0xDD +#define VK_QUOTE 0xDE +#define VK_xDF 0xDF + +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD9 0x69 +#define VK_SPACE 0x20 + + +/* Following code lifted from syskbd.cpp and tweaked for compiler use. Todo: consolidate */ + +KMX_WCHAR kmcmp::VKToChar(KMX_WORD keyCode, KMX_UINT shiftFlags) +{ + KMX_CHAR shiftedDigit[] = ")!@#$%^&*("; + int n, Shift; + + if (!(shiftFlags & ISVIRTUALKEY)) return keyCode; + + if (shiftFlags & (LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)) return 0; + + if (keyCode >= '0' && keyCode <= '9') + { + n = keyCode - '0'; + return ((shiftFlags & K_SHIFTFLAG) ? shiftedDigit[n] : keyCode); + } + + if (keyCode >= 'A' && keyCode <= 'Z') + { + Shift = (shiftFlags & K_SHIFTFLAG); + if (shiftFlags & (CAPITALFLAG)) Shift = !Shift; + return (Shift ? keyCode : keyCode + 32); + } + + if (keyCode >= VK_NUMPAD0 && keyCode <= VK_NUMPAD9) + { + if (!(shiftFlags & NUMLOCKFLAG)) return 0; + return keyCode - (VK_NUMPAD0 - '0'); + } + + Shift = (shiftFlags & K_SHIFTFLAG); + + switch (keyCode) + { + case VK_ACCENT: + return Shift ? '~' : '`'; + case VK_HYPHEN: + return Shift ? '_' : '-'; + case VK_EQUAL: + return Shift ? '+' : '='; + case VK_BKSLASH: + return Shift ? '|' : 92; + case VK_LBRKT: + return Shift ? '{' : '['; + case VK_RBRKT: + return Shift ? '}' : ']'; + case VK_COLON: + return Shift ? ':' : ';'; + case VK_QUOTE: + return Shift ? '"' : 39; + case VK_COMMA: + return Shift ? '<' : ','; + case VK_PERIOD: + return Shift ? '>' : '.'; + case VK_SLASH: + return Shift ? '?' : '/'; + case VK_SPACE: + return ' '; + } + return 0; + //keyCode; +} + + +/* This array is lifted from preservedkeymap.cpp */ +// TODO: share this +const struct +{ + KMX_UINT key; + KMX_BOOL shift; +} USCharMap[] = { + { VK_SPACE, FALSE }, // 20 ' ' + { '1', TRUE }, // 21 '!' + { VK_QUOTE, TRUE }, // 22 '"' + { '3', TRUE }, // 23 '#' + { '4', TRUE }, // 24 '$' + { '5', TRUE }, // 25 '%' + { '7', TRUE }, // 26 '&' + { VK_QUOTE, FALSE }, // 27 ''' + { '9', TRUE }, // 28 '(' + { '0', TRUE }, // 29 ')' + { '8', TRUE }, // 2A '*' + { VK_EQUAL, TRUE }, // 2B '+' + { VK_COMMA, FALSE }, // 2C ',' + { VK_HYPHEN, FALSE }, // 2D '-' + { VK_PERIOD, FALSE }, // 2E '.' + { VK_SLASH, FALSE }, // 2F '/' + + { '0', FALSE }, // 30 '0' + { '1', FALSE }, // 31 '1' + { '2', FALSE }, // 32 '2' + { '3', FALSE }, // 33 '3' + { '4', FALSE }, // 34 '4' + { '5', FALSE }, // 35 '5' + { '6', FALSE }, // 36 '6' + { '7', FALSE }, // 37 '7' + { '8', FALSE }, // 38 '8' + { '9', FALSE }, // 39 '9' + { VK_COLON, TRUE }, // 3A ':' + { VK_COLON, FALSE }, // 3B ';' + { VK_COMMA, TRUE }, // 3C '<' + { VK_EQUAL, FALSE }, // 3D '=' + { VK_PERIOD, TRUE }, // 3E '>' + { VK_SLASH, TRUE }, // 3F '?' + + { '2', TRUE }, // 40 '@' + { 'A', TRUE }, // 41 'A' + { 'B', TRUE }, // 42 'B' + { 'C', TRUE }, // 43 'C' + { 'D', TRUE }, // 44 'D' + { 'E', TRUE }, // 45 'E' + { 'F', TRUE }, // 46 'F' + { 'G', TRUE }, // 47 'G' + { 'H', TRUE }, // 48 'H' + { 'I', TRUE }, // 49 'I' + { 'J', TRUE }, // 4A 'J' + { 'K', TRUE }, // 4B 'K' + { 'L', TRUE }, // 4C 'L' + { 'M', TRUE }, // 4D 'M' + { 'N', TRUE }, // 4E 'N' + { 'O', TRUE }, // 4F 'O' + + { 'P', TRUE }, // 50 'P' + { 'Q', TRUE }, // 51 'Q' + { 'R', TRUE }, // 52 'R' + { 'S', TRUE }, // 53 'S' + { 'T', TRUE }, // 54 'T' + { 'U', TRUE }, // 55 'U' + { 'V', TRUE }, // 56 'V' + { 'W', TRUE }, // 57 'W' + { 'X', TRUE }, // 58 'X' + { 'Y', TRUE }, // 59 'Y' + { 'Z', TRUE }, // 5A 'Z' + { VK_LBRKT, FALSE }, // 5B '[' + { VK_BKSLASH, FALSE }, // 5C '\' + { VK_RBRKT, FALSE }, // 5D ']' + { '6', TRUE }, // 5E '^' + { VK_HYPHEN, TRUE }, // 5F '_' + + { VK_ACCENT, FALSE }, // 60 '`' + { 'A', FALSE }, // 61 'a' + { 'B', FALSE }, // 62 'b' + { 'C', FALSE }, // 63 'c' + { 'D', FALSE }, // 64 'd' + { 'E', FALSE }, // 65 'e' + { 'F', FALSE }, // 66 'f' + { 'G', FALSE }, // 67 'g' + { 'H', FALSE }, // 68 'h' + { 'I', FALSE }, // 69 'i' + { 'J', FALSE }, // 6A 'j' + { 'K', FALSE }, // 6B 'k' + { 'L', FALSE }, // 6C 'l' + { 'M', FALSE }, // 6D 'm' + { 'N', FALSE }, // 6E 'n' + { 'O', FALSE }, // 6F 'o' + + { 'P', FALSE }, // 70 'p' + { 'Q', FALSE }, // 71 'q' + { 'R', FALSE }, // 72 'r' + { 'S', FALSE }, // 73 's' + { 'T', FALSE }, // 74 't' + { 'U', FALSE }, // 75 'u' + { 'V', FALSE }, // 76 'v' + { 'W', FALSE }, // 77 'w' + { 'X', FALSE }, // 78 'x' + { 'Y', FALSE }, // 79 'y' + { 'Z', FALSE }, // 7A 'z' + { VK_LBRKT, TRUE }, // 7B '{' + { VK_BKSLASH, TRUE }, // 7C '|' + { VK_RBRKT, TRUE }, // 7D '}' + { VK_ACCENT, TRUE } // 7E '~' +}; + +KMX_BOOL kmcmp::MapUSCharToVK(KMX_UINT ch, KMX_UINT *puKey, KMX_UINT *puShiftFlags) { + assert(puKey != NULL); + assert(puShiftFlags != NULL); + if (ch >= 0x20 && ch < 0x7F) { + *puKey = USCharMap[ch - 0x20].key; + *puShiftFlags = ISVIRTUALKEY | (USCharMap[ch - 0x20].shift ? K_SHIFTFLAG : 0); + return TRUE; + } + return FALSE; +} diff --git a/developer/src/kmcmplib/src/CharToKeyConversion.h b/developer/src/kmcmplib/src/CharToKeyConversion.h new file mode 100644 index 0000000000000000000000000000000000000000..dd71ccbc0b1137027bec9ac46abd4904be562d12 --- /dev/null +++ b/developer/src/kmcmplib/src/CharToKeyConversion.h @@ -0,0 +1,6 @@ +#pragma once + +namespace kmcmp { + KMX_BOOL MapUSCharToVK(KMX_UINT ch, KMX_UINT *puKey, KMX_UINT *puShiftFlags); + KMX_WCHAR VKToChar(KMX_WORD keyCode, KMX_UINT shiftFlags); +} \ No newline at end of file diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp b/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp new file mode 100644 index 0000000000000000000000000000000000000000..627298bf375de6155aeae55fb1ddff96a157df14 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp @@ -0,0 +1,145 @@ + +#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1 +#include "pch.h" +#include "compfile.h" +#include +#include "kmcmplib.h" +#include +#include "CheckFilenameConsistency.h" +#include "kmx_u16.h" +#include "filesystem.h" + +#ifdef _MSC_VER +#include +#endif + +namespace kmcmp { + extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer +} +bool IsRelativePath(KMX_CHAR const * p) { + // Relative path (returns TRUE): + // ..\...\BITMAP.BMP + // PATH\BITMAP.BMP + // BITMAP.BMP + + // Semi-absolute path (returns FALSE): + // \...\BITMAP.BMP + + // Absolute path (returns FALSE): + // C:\...\BITMAP.BMP + // \\SERVER\SHARE\...\BITMAP.BMP + + if ((*p == '\\') || (*p == '/')) return FALSE; + if (*p && *(p + 1) == ':') return FALSE; + + return TRUE; +} + +bool IsRelativePath(KMX_WCHAR const * p) { + // Relative path (returns TRUE): + // ..\...\BITMAP.BMP + // PATH\BITMAP.BMP + // BITMAP.BMP + + // Semi-absolute path (returns FALSE): + // \...\BITMAP.BMP + + // Absolute path (returns FALSE): + // C:\...\BITMAP.BMP + // \\SERVER\SHARE\...\BITMAP.BMP + + if ((*p == u'\\') || (*p == u'/'))return FALSE; + if (*p && *(p + 1) == u':') return FALSE; + + return TRUE; +} + +KMX_DWORD CheckFilenameConsistency( KMX_CHAR const * Filename, bool ReportMissingFile) { + PKMX_WCHAR WFilename = strtowstr(( KMX_CHAR *)Filename); + KMX_DWORD const result = CheckFilenameConsistency(WFilename, ReportMissingFile); + delete WFilename; + return result; +} + +KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile) { + // not ready yet: needs more attention-> common includes for non-Windows platforms + KMX_WCHAR Name[260]; // TODO: fixed buffer sizes bad + + + if (IsRelativePath(Filename)) { + PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir); + u16ncpy(Name, WCompileDir, _countof(Name)); // I3481 + u16ncat(Name, Filename, _countof(Name)); // I3481 + delete[] WCompileDir; + } else { + u16ncpy(Name, Filename, _countof(Name)); // I3481 + } + +#ifndef _MSC_VER + // Filename consistency only needs to be checked on Windows, because other + // platforms are going to fail if the filename is inconsistent anyway! + if(!kmcmp_FileExists(Name)) { + if (ReportMissingFile) { + u16cpy(ErrExtraW, u"referenced file '"); + u16ncat(ErrExtraW, Filename, 256); + u16ncat(ErrExtraW, u"'", 256); + strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + AddWarning(CWARN_MissingFile); + } + return CERR_None; + } + return CERR_None; +#else + _wfinddata_t fi; + intptr_t n; + if ((n = _wfindfirst((const wchar_t*) Name, &fi)) == -1) { + if (ReportMissingFile) { + sprintf(ErrExtraLIB, "referenced file '%ls'", (wchar_t*) Filename); + AddWarning(CWARN_MissingFile); + } + return CERR_None; + } + + _findclose(n); + + KMX_WCHAR FName[_MAX_FNAME], Ext[_MAX_EXT]; + wchar_t WChName[_MAX_PATH]; + _wsplitpath_s((const wchar_t*)Filename, nullptr, 0, nullptr, 0, (wchar_t*) FName, _MAX_FNAME, (wchar_t*) Ext, _MAX_EXT); + _wmakepath_s(WChName, _MAX_PATH, nullptr, nullptr, (const wchar_t*) FName, (const wchar_t*) Ext); + if (wcscmp(WChName, fi.name) != 0) { + sprintf(ErrExtraLIB, "reference '%ls' does not match actual filename '%ls'", WChName, fi.name); + + AddWarning(CHINT_FilenameHasDifferingCase); + + } +#endif + + return CERR_None; +} + +KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk) { + // call() statements depend on a fairly ugly hack for js, + // where store(DllFunction) "my.dll:func" will look for a + // file called function.call_js. ( or should this be func.call_js ? ) + // This is ripe for rewrite! + // But let's check what we have anyway + + PFILE_STORE sp; + KMX_DWORD i, msg; + for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) { + if (!sp->fIsCall) continue; + + const std::u16string callsite(sp->dpString); + const auto colon = callsite.find(':'); + if (colon == std::u16string::npos) continue; + + auto func1 = callsite.substr(colon + 1); + std::u16string str_js(u".call_js"); + std::u16string func = func1+ str_js; + + if ((msg = CheckFilenameConsistency(func.c_str(), FALSE)) != CERR_None) { + return msg; + } + } + return CERR_None; +} diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.h b/developer/src/kmcmplib/src/CheckFilenameConsistency.h new file mode 100644 index 0000000000000000000000000000000000000000..39f86db391b1ac6b870d7a521f297372059bd9b4 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckFilenameConsistency.h @@ -0,0 +1,11 @@ +#pragma once + +#include "compfile.h" +#include "kmcmplib.h" + +KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk); +KMX_DWORD CheckFilenameConsistency(KMX_CHAR const * Filename, bool ReportMissingFile); +KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile); + +bool IsRelativePath(KMX_CHAR const * p); +bool IsRelativePath(KMX_WCHAR const * p); diff --git a/developer/src/kmcmplib/src/CheckForDuplicates.cpp b/developer/src/kmcmplib/src/CheckForDuplicates.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71268a4db59ac9eac0ef8fcdeed64931e6ab5d86 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckForDuplicates.cpp @@ -0,0 +1,48 @@ + +#include "pch.h" + +#include "compfile.h" +#include +#include "kmcmplib.h" +#include +#include "kmx_u16.h" +#include + +#include "CheckForDuplicates.h" + +KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept { + KMX_DWORD i; + PFILE_GROUP gp0 = fk->dpGroupArray; + for (i = 0; i < fk->cxGroupArray; i++, gp0++) { + if (gp0 == gp) { + continue; + } + if (u16icmp(gp0->szName, gp->szName) == 0) { + u16sprintf(ErrExtraW, 256, L" Group '%ls' declared on line %d", u16fmt(gp0->szName).c_str(), gp0->Line); + strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + return CERR_DuplicateGroup; + } + } + return CERR_None; +} + +KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept { + if (!sp->szName[0]) { + // Stores with zero length names are reserved system stores. + // They cannot be defined in user code. This is not an issue. + return CERR_None; + } + KMX_DWORD i; + PFILE_STORE sp0 = fk->dpStoreArray; + for (i = 0; i < fk->cxStoreArray; i++, sp0++) { + if (sp0 == sp) { + continue; + } + if (u16icmp(sp0->szName, sp->szName) == 0) { + u16sprintf(ErrExtraW,256, L" Store '%ls' declared on line %d", u16fmt(sp0->szName).c_str(), sp0->line); + strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + return CERR_DuplicateStore; + } + } + return CERR_None; +} diff --git a/developer/src/kmcmplib/src/CheckForDuplicates.h b/developer/src/kmcmplib/src/CheckForDuplicates.h new file mode 100644 index 0000000000000000000000000000000000000000..20789dd5e2cdae8ab9c5ae7c7ba5a76e63518909 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckForDuplicates.h @@ -0,0 +1,4 @@ +#pragma once + +KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept; +KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept; diff --git a/developer/src/kmcmplib/src/CheckNCapsConsistency.cpp b/developer/src/kmcmplib/src/CheckNCapsConsistency.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c86c62702e7e312cf1fc75fb6bb449cfbfcaa0e3 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckNCapsConsistency.cpp @@ -0,0 +1,99 @@ + +#include "pch.h" + +#include +#include +#include +#include "CharToKeyConversion.h" + +/** + * If any rule uses CAPS or NCAPS for a given key, then every rule that + * uses that key must also use CAPS or NCAPS, as otherwise the results are + * inconsistent. For example, if Caps Lock is on, then [K_F] may still be + * matched: + * + * + [K_F] > 'foo' + * + [CAPS K_F] > 'bar' + * + * Because of the way that KeymanWeb compiles rules, this may even apply when + * we have a key rule that would otherwise be ignored due to context. In the + * following example, [Caps Lock] + [y] would result in no output (or default + * output) rather than 'bar', because the preceding rule would capture the + * Y key, ignoring Caps Lock, meaning that the subsequent rule would never + * even get tested. (Note that this was introduced in the KeymanWeb compiler + * fix for extremely long if/else ladders in Keyman 12 in #1561, and this is + * technically slightly inconsistent with Keyman Core, although in my analysis + * only in this already ambiguous situation. + * + * 'x' + [K_Y] > 'foo' + * [CAPS K_Y] > 'bar' + * + * Given all this, we'll warn any time we find a key that has inconsistent use + * of CAPS/NCAPS in its rules. + * + * @param fk Keyboard to check + */ +bool CheckNCapsConsistency(PFILE_KEYBOARD fk) { + struct CapsUsage { + int ncaps_line, caps_line, neither_line; + }; + + const int oldCurrentLine = kmcmp::currentLine; + + // 256 virtual key codes + sizeof the virtual key dictionary is max key code possible + const int nkeys = 256 + fk->cxVKDictionary; + auto caps_ncaps_usage = new CapsUsage [nkeys]; + + memset(caps_ncaps_usage, 0, nkeys * sizeof(CapsUsage)); + + PFILE_GROUP gp; + KMX_DWORD gn; + for (gn = 0, gp = fk->dpGroupArray; gn < fk->cxGroupArray; gn++, gp++) { + if (!gp->fUsingKeys) { + continue; + } + + PFILE_KEY kp; + KMX_DWORD kn; + for (kn = 0, kp = gp->dpKeyArray; kn < gp->cxKeyArray; kn++, kp++) { + KMX_UINT key; + KMX_UINT shift; + if (kp->ShiftFlags & ISVIRTUALKEY) { + if (kp->Key >= nkeys) { + assert(false); + continue; + } + key = kp->Key; + shift = kp->ShiftFlags; + } + else if (!kmcmp::MapUSCharToVK(kp->Key, &key, &shift)) { + // Not a valid key + continue; + } + + if (shift & NOTCAPITALFLAG) { + if (!caps_ncaps_usage[key].ncaps_line) caps_ncaps_usage[key].ncaps_line = (kp->Line == 0 ? 1 : kp->Line); + } + else if (shift & CAPITALFLAG) { + if (!caps_ncaps_usage[key].caps_line) caps_ncaps_usage[key].caps_line = (kp->Line == 0 ? 1 : kp->Line); + } + else { + if (!caps_ncaps_usage[key].neither_line) caps_ncaps_usage[key].neither_line = (kp->Line == 0 ? 1 : kp->Line); + } + } + } + + for (int i = 0; i < nkeys; i++) { + if (caps_ncaps_usage[i].neither_line && (caps_ncaps_usage[i].caps_line || caps_ncaps_usage[i].ncaps_line)) { + // We set the current line to one needing work: the developer should add the NCAPS flag + kmcmp::currentLine = caps_ncaps_usage[i].neither_line; + AddWarningBool(CWARN_KeyShouldIncludeNCaps); + } + } + + delete[] caps_ncaps_usage; + + kmcmp::currentLine = oldCurrentLine; + + return TRUE; +} diff --git a/developer/src/kmcmplib/src/CheckNCapsConsistency.h b/developer/src/kmcmplib/src/CheckNCapsConsistency.h new file mode 100644 index 0000000000000000000000000000000000000000..50fa744c4ac5819c648e7c21784f540c17edec96 --- /dev/null +++ b/developer/src/kmcmplib/src/CheckNCapsConsistency.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +bool CheckNCapsConsistency(PFILE_KEYBOARD fk); diff --git a/developer/src/kmcmplib/src/CompMsg.cpp b/developer/src/kmcmplib/src/CompMsg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fa0d68322dc1217207bb8541a62e304fee40f76 --- /dev/null +++ b/developer/src/kmcmplib/src/CompMsg.cpp @@ -0,0 +1,158 @@ +#include + +struct CompilerError { + KMX_DWORD ErrorCode; + const KMX_CHAR* Text; + }; + +const struct CompilerError CompilerErrors[] = { + { CERR_InvalidLayoutLine , "Invalid 'layout' command"}, + { CERR_NoVersionLine , "No version line found for file"}, + { CERR_InvalidGroupLine , "Invalid 'group' command"}, + { CERR_InvalidStoreLine , "Invalid 'store' command"}, + { CERR_InvalidCodeInKeyPartOfRule , "Invalid command or code found in key part of rule"}, + { CERR_InvalidDeadkey , "Invalid 'deadkey' or 'dk' command"}, + { CERR_InvalidValue , "Invalid value in extended string"}, + { CERR_ZeroLengthString , "A string of zero characters was found"}, + { CERR_TooManyIndexToKeyRefs , "Too many index commands refering to key string"}, + { CERR_UnterminatedString , "Unterminated string in line"}, + { CERR_StringInVirtualKeySection , "extend string illegal in virtual key section"}, + { CERR_AnyInVirtualKeySection , "'any' command is illegal in virtual key section"}, + { CERR_InvalidAny , "Invalid 'any' command"}, + { CERR_StoreDoesNotExist , "Store referenced does not exist"}, + { CERR_BeepInVirtualKeySection , "'beep' command is illegal in virtual key section"}, + { CERR_IndexInVirtualKeySection , "'index' command is illegal in virtual key section"}, + { CERR_BadCallParams , "CompileKeyboardFile was called with bad parameters"}, + { CERR_InfileNotExist , "Cannot find the input file"}, + { CERR_CannotCreateOutfile , "Cannot open output file for writing"}, + { CERR_UnableToWriteFully , "Unable to write the file completely"}, + { CERR_CannotReadInfile , "Cannot read the input file"}, + { CERR_SomewhereIGotItWrong , "Internal error: contact Keyman"}, + { CERR_BufferOverflow , "The compiler memory buffer overflowed"}, + { CERR_Break , "Compiler interrupted by user"}, + { CERR_CannotAllocateMemory , "Out of memory"}, + { CERR_InvalidBitmapLine , "Invalid 'bitmaps' command"}, + { CERR_CannotReadBitmapFile , "Cannot open the bitmap or icon file for reading"}, + { CERR_IndexDoesNotPointToAny , "An index() in the output does not have a corresponding any() statement"}, + { CERR_ReservedCharacter , "A reserved character was found"}, + { CERR_InvalidCharacter , "A character was found that is outside the valid Unicode range (U+0000 - U+10FFFF)"}, + { CERR_InvalidCall , "The 'call' command is invalid"}, + { CERR_CallInVirtualKeySection , "'call' command is illegal in virtual key section"}, + { CERR_CodeInvalidInKeyStore , "The command is invalid inside a store that is used in a key part of the rule"}, + { CERR_CannotLoadIncludeFile , "Cannot load the included file: it is either invalid or does not exist"}, + { CERR_60FeatureOnly_EthnologueCode , "EthnologueCode system store requires VERSION 6.0 or higher"}, + { CERR_60FeatureOnly_MnemonicLayout , "MnemonicLayout functionality requires VERSION 6.0 or higher"}, + { CERR_60FeatureOnly_OldCharPosMatching , "OldCharPosMatching system store requires VERSION 6.0 or higher"}, + { CERR_60FeatureOnly_NamedCodes , "Named character constants requires VERSION 6.0 or higher"}, + { CERR_60FeatureOnly_Contextn , "Context(n) requires VERSION 6.0 or higher"}, + { CERR_501FeatureOnly_Call , "Call() requires VERSION 5.01 or higher"}, + { CERR_InvalidNamedCode , "Invalid named code constant"}, + { CERR_InvalidSystemStore , "Invalid system store name found"}, + { CERR_60FeatureOnly_VirtualCharKey , "Virtual character keys require VERSION 6.0 or higher"}, + { CERR_VersionAlreadyIncluded , "Only one VERSION or store(version) line allowed in a source file."}, + { CERR_70FeatureOnly , "This feature requires store(version) '7.0' or higher"}, + { CERR_80FeatureOnly , "This feature requires store(version) '8.0' or higher"}, + { CERR_InvalidInVirtualKeySection , "This statement is not valid in a virtual key section"}, + { CERR_InvalidIf , "The if() statement is not valid"}, + { CERR_InvalidReset , "The reset() statement is not valid"}, + { CERR_InvalidSet , "The set() statement is not valid"}, + { CERR_InvalidSave , "The save() statement is not valid"}, + { CERR_InvalidEthnologueCode , "Invalid ethnologuecode format"}, + { CERR_90FeatureOnly_IfSystemStores , "if(store) requires store(version) '9.0' or higher"}, + { CERR_IfSystemStore_NotFound , "System store in if() not found"}, + { CERR_90FeatureOnly_SetSystemStores , "set(store) requires store(version) '9.0' or higher"}, + { CERR_SetSystemStore_NotFound , "System store in set() not found"}, + { CERR_90FeatureOnlyVirtualKeyDictionary , "Custom virtual key names require store(version) '9.0'"}, + { CERR_InvalidIndex , "Invalid 'index' command"}, + { CERR_OutsInVirtualKeySection , "'outs' command is illegal in virtual key section"}, + { CERR_InvalidOuts , "Invalid 'outs' command"}, + { CERR_ContextInVirtualKeySection , "'context' command is illegal in virtual key section"}, + { CERR_InvalidUse , "Invalid 'use' command"}, + { CERR_GroupDoesNotExist , "Group does not exist"}, + { CERR_VirtualKeyNotAllowedHere , "Virtual key is not allowed here"}, + { CERR_InvalidSwitch , "Invalid 'switch' command"}, + { CERR_NoTokensFound , "No tokens found in line"}, + { CERR_InvalidLineContinuation , "Invalid line continuation"}, + { CERR_LineTooLong , "Line too long"}, + { CERR_InvalidCopyright , "Invalid 'copyright' command"}, + { CERR_CodeInvalidInThisSection , "This line is invalid in this section of the file"}, + { CERR_InvalidMessage , "Invalid 'message' command"}, + { CERR_InvalidLanguageName , "Invalid 'languagename' command"}, + { CERR_EndOfFile , "(no error - reserved code)"}, + { CERR_InvalidToken , "Invalid token found"}, + { CERR_InvalidBegin , "Invalid 'begin' command"}, + { CERR_InvalidName , "Invalid 'name' command"}, + { CERR_InvalidVersion , "Invalid 'version' command"}, + { CERR_InvalidLanguageLine , "Invalid 'language' command"}, + { CERR_LayoutButNoLanguage , "Layout command found but no language command"}, + { CERR_CannotCreateTempfile , "Cannot create temp file"}, + { CERR_90FeatureOnlyLayoutFile , "Touch layout file reference requires store(version) '9.0'or higher"}, + { CERR_90FeatureOnlyKeyboardVersion , "KeyboardVersion system store requires store(version) '9.0'or higher"}, + { CERR_KeyboardVersionFormatInvalid , "KeyboardVersion format is invalid, expecting dot-separated integers"}, + { CERR_ContextExHasInvalidOffset , "context() statement has offset out of range"}, + { CERR_90FeatureOnlyEmbedCSS , "Embedding CSS requires store(version) '9.0'or higher"}, + { CERR_90FeatureOnlyTargets , "TARGETS system store requires store(version) '9.0'or higher"}, + { CERR_ContextAndIndexInvalidInMatchNomatch , "context and index statements cannot be used in a match or nomatch statement"}, + { CERR_140FeatureOnlyContextAndNotAnyWeb , "For web and touch platforms, context() statement referring to notany() requires store(version) '14.0'or higher"}, + { CERR_ExpansionMustFollowCharacterOrVKey , "An expansion must follow a character or a virtual key"}, + { CERR_VKeyExpansionMustBeFollowedByVKey , "A virtual key expansion must be terminated by a virtual key"}, + { CERR_CharacterExpansionMustBeFollowedByCharacter , "A character expansion must be terminated by a character key"}, + { CERR_VKeyExpansionMustUseConsistentShift , "A virtual key expansion must use the same shift state for both terminators"}, + { CERR_ExpansionMustBePositive , "An expansion must have positive difference (i.e. A-Z, not Z-A)"}, + { CERR_CasedKeysMustContainOnlyVirtualKeys , "The &CasedKeys system store must contain only virtual keys or characters found on a US English keyboard"}, + { CERR_CasedKeysMustNotIncludeShiftStates , "The &CasedKeys system store must not include shift states"}, + { CERR_CasedKeysNotSupportedWithMnemonicLayout , "The &CasedKeys system store is not supported with mnemonic layouts"}, + { CERR_CannotUseReadWriteGroupFromReadonlyGroup , "Group used from a readonly group must also be readonly"}, + { CERR_StatementNotPermittedInReadonlyGroup , "Statement is not permitted in output of readonly group"}, + { CERR_OutputInReadonlyGroup , "Output is not permitted in a readonly group"}, + { CERR_NewContextGroupMustBeReadonly , "Group used in begin newContext must be readonly"}, + { CERR_PostKeystrokeGroupMustBeReadonly , "Group used in begin postKeystroke must be readonly"}, + { CERR_DuplicateGroup , "A group with this name has already been defined."}, + { CERR_DuplicateStore , "A store with this name has already been defined."}, + { CERR_RepeatedBegin , "Begin has already been set"}, + + { CHINT_FilenameHasDifferingCase , "Casing differences may fail on some platforms: "}, + { CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"}, + + { CWARN_TooManyWarnings , "Too many warnings or errors"}, + { CWARN_OldVersion , "The keyboard file is an old version"}, + { CWARN_BitmapNotUsed , "The 'bitmaps' statement is obsolete and only the first bitmap referred to will be used, you should use 'bitmap'."}, + { CWARN_CustomLanguagesNotSupported , "Languages over 0x1FF, 0x1F are not supported correctly by Windows. You should use no LANGUAGE line instead."}, + { CWARN_KeyBadLength , "There are too many characters in the keystroke part of the rule."}, + { CWARN_IndexStoreShort , "The store referenced in index() is shorter than the store referenced in any()"}, + { CWARN_UnicodeInANSIGroup , "A Unicode character was found in an ANSI group"}, + { CWARN_ANSIInUnicodeGroup , "An ANSI character was found in a Unicode group"}, + { CWARN_UnicodeSurrogateUsed , "A Unicode surrogate character was found. You should use Unicode scalar values to represent values > U+FFFF"}, + { CWARN_ReservedCharacter , "A Unicode character was found that should not be used"}, + { CWARN_Info , "Information"}, + { CWARN_VirtualKeyWithMnemonicLayout , "Virtual key used instead of virtual character key with a mnemonic layout"}, + { CWARN_VirtualCharKeyWithPositionalLayout , "Virtual character key used with a positional layout instead of mnemonic layout"}, + { CWARN_StoreAlreadyUsedAsOptionOrCall , "Store already used as an option or in a call statement and should not be used as a normal store"}, + { CWARN_StoreAlreadyUsedAsStoreOrCall , "Store already used as a normal store or in a call statement and should not be used as an option"}, + { CWARN_StoreAlreadyUsedAsStoreOrOption , "Store already used as a normal store or as an option and should not be used in a call statement"}, + { CWARN_PunctuationInEthnologueCode , "Punctuation should not be used to separate Ethnologue codes; instead use spaces"}, + { CWARN_PlatformNotInTargets , "The specified platform is not a target platform"}, + { CWARN_HeaderStatementIsDeprecated , "Header statements are deprecated; use instead the equivalent system store"}, + { CWARN_UseNotLastStatementInRule , "A rule with use() statements in the output should not have other content following the use() statements"}, + { CWARN_KVKFileIsInSourceFormat , ".kvk file should be binary but is an XML file"}, + { CWARN_DontMixChiralAndNonChiralModifiers , "Don't mix the use of left/right modifiers with non-left/right modifiers in the same platform"}, + { CWARN_MixingLeftAndRightModifiers , "Left and right modifiers should not both be used in the same rule"}, + { CWARN_LanguageHeadersDeprecatedInKeyman10 , "This language header has been deprecated in Keyman 10. Instead, add language metadata in the package file"}, + { CWARN_HotkeyHasInvalidModifier , "Hotkey has modifiers that are not supported. Use only SHIFT, CTRL and ALT"}, + { CWARN_NulNotFirstStatementInContext , "nul must be the first statement in the context"}, + { CWARN_IfShouldBeAtStartOfContext , "if, platform and baselayout should be at start of context (after nul, if present)"}, + { CWARN_KeyShouldIncludeNCaps , "Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches"}, + { CWARN_MissingFile , "The referenced file could not be found: "}, + + { 0, nullptr } + }; + +KMX_CHAR *GetCompilerErrorString(KMX_DWORD code) +{ + for(int i = 0; CompilerErrors[i].ErrorCode; i++) { + if(CompilerErrors[i].ErrorCode == code) { + return ( KMX_CHAR*) CompilerErrors[i].Text; + } + } + return nullptr; +} diff --git a/developer/src/kmcmplib/src/CompMsg.h b/developer/src/kmcmplib/src/CompMsg.h new file mode 100644 index 0000000000000000000000000000000000000000..5517ea88112251f73b36f902884ec564e5de2376 --- /dev/null +++ b/developer/src/kmcmplib/src/CompMsg.h @@ -0,0 +1,5 @@ + +#include "km_types.h" +#include + +KMX_CHAR *GetCompilerErrorString(KMX_DWORD code) ; diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b09e36cbc1079fdf47feeb9ac882eaf640b3f425 --- /dev/null +++ b/developer/src/kmcmplib/src/Compiler.cpp @@ -0,0 +1,3519 @@ +/* + Name: Compiler + Copyright: Copyright (C) SIL International. + Documentation: + Description: + Create Date: 20 Jun 2006 + + Modified Date: 25 Oct 2016 + Authors: mcdurdin + Related Files: + Dependencies: + + Bugs: + Todo: + Notes: + History: 20 Jun 2006 - mcdurdin - Initial version + 23 Aug 2006 - mcdurdin - Add VISUALKEYBOARD, KMW_RTL, KMW_HELPFILE, KMW_HELPTEXT, KMW_EMBEDJS system stores + 14 Sep 2006 - mcdurdin - Support icons in version 7 + 28 Sep 2006 - mcdurdin - Added product validation + 28 Sep 2006 - mcdurdin - Added test for version 7.0 icon support + 06 Oct 2006 - mcdurdin - Fix buffer overflow in UTF8 conversion + 04 Dec 2006 - mcdurdin - Fix readfile buffer bug + 04 Jan 2007 - mcdurdin - Add notany support + 22 Jan 2007 - mcdurdin - Add K_NPENTER reference + 25 Jan 2007 - mcdurdin - Resize buffers to 4095 + 30 May 2007 - mcdurdin - I786 - Compiler crash if zero length string in keystroke any + 23 Aug 2007 - mcdurdin - I1011 - Fix buffer clobbering for UTF8 conversion of large files + 27 Mar 2008 - mcdurdin - I1358 - Support for multiple languages for office config + 14 Jun 2008 - mcdurdin - Support documenting language id as a single WORD instead of by PRIMARY/SUB + 14 Jun 2008 - mcdurdin - Support Windows languages list + 28 Jul 2008 - mcdurdin - I1569 - line prefixes + 22 Mar 2010 - mcdurdin - Compiler fixup - x64 support + 25 May 2010 - mcdurdin - I1632 - Keyboard Options + 24 Jun 2010 - mcdurdin - I2432 - Use local buffers so GetXString can be re-entrant (used by if()) + 26 Jul 2010 - mcdurdin - I2467 - 8.0 renumber + 18 Mar 2011 - mcdurdin - I2646 - Compiler warning on invalid Ethnologue codes + 18 Mar 2011 - mcdurdin - I2525 - Unterminated string can crash compiler + 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear + 26 Jun 2012 - mcdurdin - I3377 - KM9 - Update code references from 8.0 to 9.0 + 17 Aug 2012 - mcdurdin - I3431 - V9.0 - Spaces in values in if comparisons break the compiler parser + 27 Aug 2012 - mcdurdin - I3439 - V9.0 - Refactor xstring support in C++ code + 27 Aug 2012 - mcdurdin - I3437 - V9.0 - Add support for set(&layer) and layer() + 27 Aug 2012 - mcdurdin - I3438 - V9.0 - Add support for custom virtual keys + 27 Aug 2012 - mcdurdin - I3430 - V9.0 - Add support for if(&platform) and if(&baselayout) to compilers + 27 Aug 2012 - mcdurdin - I3440 - V9.0 - Tidy up set statement delimiter recognition + 24 Oct 2012 - mcdurdin - I3483 - V9.0 - Add support for compiling in the layout file + 24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++ + 24 Jan 2012 - mcdurdin - I3137 - If key part of VK rule is missing, compiler generates invalid file + 06 Feb 2012 - mcdurdin - I3228 - kmcmpdll sometimes tries to write temp files to Program Files + 03 Nov 2012 - mcdurdin - I3510 - V9.0 - Merge of I3228 - kmcmpdll sometimes tries to write temp files to Program Files + 03 Nov 2012 - mcdurdin - I3511 - V9.0 - Merge of I3137 - If key part of VK rule is missing, compiler generates invalid file + 13 Dec 2012 - mcdurdin - I3654 - V9.0 - Compiler appears to create unregistered keyboards even when registered + 13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs + 13 Dec 2012 - mcdurdin - I3681 - V9.0 - KeymanWeb compiler should output formatted js when debug=1 + 13 Dec 2012 - mcdurdin - I3686 - V9.0 - AddStore attaches property flags to wrong store structure + 19 Mar 2014 - mcdurdin - I4140 - V9.0 - Add keyboard version information to keyboards + 04 Nov 2014 - mcdurdin - I4504 - V9.0 - Consolidate the compile action into single command + 02 Jul 2015 - mcdurdin - I4784 - Compiler does not recognise baselayout, layer or platform at the start of a line + 02 Jul 2015 - mcdurdin - I4785 - baselayout(), layer() and platform() produce incorrect compiled code + 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project + 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile + 24 Aug 2015 - mcdurdin - I4867 - Add test for code after use warning to compile + 06 Nov 2015 - mcdurdin - I4914 - kmcmpdll does not pick an index() statement that has an offset one past the key + 23 Feb 2016 - mcdurdin - I4982 - Defined character constants cannot be referenced correctly in other stores + 25 Oct 2016 - mcdurdin - I5135 - Remove product and licensing references from Developer projects +*/ +#include "pch.h" + +#include + +#include "compfile.h" +#include +#include "../../../../common/windows/cpp/include/vkeys.h" +#include +#include "versioning.h" +#include "kmcmplib.h" +#include "DeprecationChecks.h" + +#include "virtualcharkeys.h" + +// TODO: These three should be under common/cpp/include -- not windows specific +#include "../../../../common/windows/cpp/include/keymanversion.h" +#include "../../../../common/windows/cpp/include/crc32.h" +#include "../../../../common/windows/cpp/include/ConvertUTF.h" + +#include "debugstore.h" +#include "NamedCodeConstants.h" + +#include "xstring.h" + +#include "Edition.h" + +#include "CharToKeyConversion.h" +#include "CasedKeys.h" +#include +#include +#include "CheckFilenameConsistency.h" +#include "UnreachableRules.h" +#include "CheckForDuplicates.h" +#include "kmx_u16.h" +#include "filesystem.h" +#include + +/* These macros are adapted from winnt.h and legacy use only */ +#define MAKELANGID(p, s) ((((uint16_t)(s)) << 10) | (uint16_t)(p)) +#define PRIMARYLANGID(lgid) ((uint16_t)(lgid) & 0x3ff) +#define SUBLANGID(lgid) ((uint16_t)(lgid) >> 10) + +#define COMPILE_ERROR_MAX_LEN (SZMAX_ERRORTEXT + 1 + 280) + +using namespace kmcmp; + + char ErrExtraLIB[ERR_EXTRA_LIB_LEN]; + KMX_WCHAR ErrExtraW[ERR_EXTRA_W_LEN]; + KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB; + +namespace kmcmp{ + KMX_BOOL FShouldAddCompilerVersion = TRUE; + KMX_BOOL FSaveDebug, FCompilerWarningsAsErrors; // I4865 // I4866 + int ErrChr; + int nErrors = 0; + KMX_BOOL FMnemonicLayout = FALSE; + KMX_BOOL FOldCharPosMatching = FALSE; + int CompileTarget; + KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer + int BeginLine[4]; + + KMX_BOOL IsValidCallStore(PFILE_STORE fs); + KMX_BOOL CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall); + KMX_DWORD UTF32ToUTF16(int n, int *n1, int *n2); + KMX_DWORD CheckUTF16(int n); + int cmpkeys(const void *key, const void *elem); +} + +int xatoi(PKMX_WCHAR *p); +int atoiW(PKMX_WCHAR p); +void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax); +int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p); + +KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token); +KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, int offset, int IsUnicode); +PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags); +KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isVKey, int isUnicode); + +int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p); + +KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str); +KMX_DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode); +KMX_DWORD ProcessEthnologueStore(PKMX_WCHAR p); // I2646 +KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk); +KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf); + +KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex); + +int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p); // I3438 // TODO: Consolidate GetDeadKey and GetVKCode? +KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp); + +KMX_DWORD process_if(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); +KMX_DWORD process_reset(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); +KMX_DWORD process_set(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); +KMX_DWORD process_save(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); +KMX_DWORD process_platform(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); // I3430 +KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); // I3430 +KMX_DWORD process_set_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); // I3437 +KMX_DWORD process_expansion(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx, int max); + +KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString); + +const KMX_WCHAR * LineTokens[] = { + u"SVNBHBGMNSCCLLCMLB", u"store", u"VERSION ", u"NAME ", + u"BITMAP ", u"HOTKEY ", u"begin", u"group", u"match", u"nomatch", + u"SHIFT FREES CAPS", u"CAPS ON ONLY", u"CAPS ALWAYS OFF", + u"LANGUAGE ", u"LAYOUT ", u"COPYRIGHT ", u"MESSAGE ", u"LANGUAGENAME ", + u"BITMAPS " }; + +#define SSN__PREFIX u"&" + +const KMX_WCHAR * StoreTokens[TSS__MAX + 2] = { + u"", + SSN__PREFIX u"BITMAP", + SSN__PREFIX u"COPYRIGHT", + SSN__PREFIX u"HOTKEY", + SSN__PREFIX u"LANGUAGE", + SSN__PREFIX u"LAYOUT", + SSN__PREFIX u"MESSAGE", + SSN__PREFIX u"NAME", + SSN__PREFIX u"VERSION", + SSN__PREFIX u"CAPSONONLY", + SSN__PREFIX u"CAPSALWAYSOFF", + SSN__PREFIX u"SHIFTFREESCAPS", + SSN__PREFIX u"LANGUAGENAME", + u"", + u"", + SSN__PREFIX u"ETHNOLOGUECODE", + u"", + SSN__PREFIX u"MNEMONICLAYOUT", + SSN__PREFIX u"INCLUDECODES", + SSN__PREFIX u"OLDCHARPOSMATCHING", + u"", + u"", + u"", + u"", + SSN__PREFIX u"VISUALKEYBOARD", + SSN__PREFIX u"KMW_RTL", + SSN__PREFIX u"KMW_HELPFILE", + SSN__PREFIX u"KMW_HELPTEXT", + SSN__PREFIX u"KMW_EMBEDJS", + SSN__PREFIX u"WINDOWSLANGUAGES", + u"", + SSN__PREFIX u"PLATFORM", // read only // I3430 + SSN__PREFIX u"BASELAYOUT", // read only // I3430 + SSN__PREFIX u"LAYER", // read-write via set? // I3430 + u"", // I3438 + SSN__PREFIX u"LAYOUTFILE", // I3483 + SSN__PREFIX u"KEYBOARDVERSION", // I4140 + SSN__PREFIX u"KMW_EMBEDCSS", + SSN__PREFIX u"TARGETS", // I4504 + SSN__PREFIX u"CASEDKEYS", // #2241 + SSN__PREFIX u"", // TSS_BEGIN_NEWCONTEXT + SSN__PREFIX u"", // TSS_BEGIN_POSTKEYSTROKE + SSN__PREFIX u"NEWLAYER", + SSN__PREFIX u"OLDLAYER", + NULL +}; + +static_assert(sizeof(StoreTokens) / sizeof(StoreTokens[0]) == TSS__MAX + 2, "StoreTokens should have exactly TSS__MAX+2 elements"); + +enum LinePrefixType { lptNone, lptKeymanAndKeymanWeb, lptKeymanWebOnly, lptKeymanOnly, lptOther }; + +/* Compile target */ + +kmcmp_CompilerMessageProc msgproc = NULL; +void* msgprocContext = NULL; + +int kmcmp::currentLine = 0; + +kmcmp::NamedCodeConstants *kmcmp::CodeConstants = NULL; + +PKMX_WCHAR strtowstr(PKMX_STR in) +{ + PKMX_WCHAR result; + + auto c = u16string_from_string(in); + + result = new KMX_WCHAR[c.length() + 1]; + u16cpy(result, c.c_str()); + return result; +} + +PKMX_STR wstrtostr(PKMX_WCHAR in) +{ + PKMX_STR result; + + auto c = string_from_u16string(in); + + result = new KMX_CHAR[c.length() + 1]; + strcpy(result, c.c_str()); + return result; +} + +KMX_BOOL kmcmp::AddCompileWarning(PKMX_CHAR buf) +{ + (*msgproc)(kmcmp::currentLine + 1, CWARN_Info, buf, msgprocContext); + return FALSE; +} + +KMX_BOOL AddCompileError(KMX_DWORD msg) +{ + KMX_CHAR szText[COMPILE_ERROR_MAX_LEN]; + KMX_CHAR* szTextp = NULL; + + if (msg & CERR_FATAL) + { + szTextp = GetCompilerErrorString(msg); + (*msgproc)(kmcmp::currentLine + 1, msg, szTextp, msgprocContext); + kmcmp::nErrors++; + return TRUE; + } + + if (msg & CERR_ERROR) + kmcmp::nErrors++; + szTextp = GetCompilerErrorString(msg); + + if (szTextp) { + strcpy(szText, szTextp); + } else { + snprintf(szText, COMPILE_ERROR_MAX_LEN, "Unknown error %x", msg); + } + + if (kmcmp::ErrChr > 0) { + char *szTextNull = strchr(szText, 0); + snprintf(szTextNull, COMPILE_ERROR_MAX_LEN-(szTextNull-szText), " character offset: %d", kmcmp::ErrChr); + } + + if (*ErrExtraLIB) { + char *szTextNull = strchr(szText, 0); + snprintf(szTextNull, COMPILE_ERROR_MAX_LEN-(szTextNull-szText), "%s", ErrExtraLIB); + } + + ErrChr = 0; *ErrExtraLIB =0; + if (!(*msgproc)(kmcmp::currentLine, msg, szText, msgprocContext)) return TRUE; + return FALSE; +} + +KMX_DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + KMX_WCHAR tstr[128]; + PKMX_WCHAR q, pp; + int BeginMode; + KMX_DWORD msg; + + pp = p; + + q = ( PKMX_WCHAR) u16chr(p, '>'); + if (!q) return CERR_NoTokensFound; + + while (iswspace(*p)) p++; + if (u16nicmp(p, u"unicode", 7) == 0) BeginMode = BEGIN_UNICODE; + else if (u16nicmp(p, u"ansi", 4) == 0) BeginMode = BEGIN_ANSI; + else if (u16nicmp(p, u"newContext", 10) == 0) BeginMode = BEGIN_NEWCONTEXT; + else if (u16nicmp(p, u"postKeystroke", 13) == 0) BeginMode = BEGIN_POSTKEYSTROKE; + else if (*p != '>') return CERR_InvalidToken; + else BeginMode = BEGIN_ANSI; + + if(kmcmp::BeginLine[BeginMode] != -1) { + return CERR_RepeatedBegin; + } + + kmcmp::BeginLine[BeginMode] = kmcmp::currentLine; + + if ((msg = GetRHS(fk, p, tstr, 80, (int)(p - pp), FALSE)) != CERR_None) return msg; + + if (tstr[0] != UC_SENTINEL || tstr[1] != CODE_USE) { + return CERR_InvalidBegin; + } + if (tstr[3] != 0) { + return CERR_InvalidToken; + } + + if (BeginMode == BEGIN_ANSI || BeginMode == BEGIN_UNICODE) { + fk->StartGroup[BeginMode] = tstr[2] - 1; + //mcd-03-01-2000: removed the secondary group idea; this was undocumented and + //is not supported under Keyman 5.0: ugly!! + //if(tstr[3] == UC_SENTINEL && tstr[4] == CODE_USE) fk->StartGroup[1] = tstr[5] - 1; + + if (kmcmp::FSaveDebug) { + /* Record a system store for the line number of the begin statement */ + AddDebugStore(fk, BeginMode == BEGIN_UNICODE ? DEBUGSTORE_BEGIN u"Unicode" : DEBUGSTORE_BEGIN u"ANSI"); + } + } + else { + PFILE_GROUP gp = &fk->dpGroupArray[tstr[2] - 1]; + if (!gp->fReadOnly) { + return BeginMode == BEGIN_NEWCONTEXT ? + CERR_NewContextGroupMustBeReadonly : + CERR_PostKeystrokeGroupMustBeReadonly; + } + return AddStore(fk, BeginMode == BEGIN_NEWCONTEXT ? TSS_BEGIN_NEWCONTEXT : TSS_BEGIN_POSTKEYSTROKE, tstr, NULL); + } + + + + return CERR_None; +} + +KMX_DWORD ValidateMatchNomatchOutput(PKMX_WCHAR p) { + while (p && *p) { + if (*p == UC_SENTINEL) { + switch (*(p + 1)) { + case CODE_CONTEXT: + case CODE_CONTEXTEX: + case CODE_INDEX: + return CERR_ContextAndIndexInvalidInMatchNomatch; + } + } + p = incxstr(p); + } + return CERR_None; +} + +KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str) +{ + PKMX_WCHAR p, q, pp; + PFILE_GROUP gp; + KMX_DWORD msg; + int IsUnicode = TRUE; // For NOW! + + KMX_WCHAR sep[2] = u"\n"; + PKMX_WCHAR p_sep =sep; + p = str; + pp = str; + + switch (LineTokenType(&p)) + { + case T_BLANK: + case T_COMMENT: + break; // Ignore the line + case T_VERSION: + case T_STORE: + break; // The line has already been processed + + case T_BEGIN: + // after a begin can be "Unicode" or "ANSI" or nothing (=ANSI) + if ((msg = ProcessBeginLine(fk, p)) != CERR_None) return msg; + break; + + case T_GROUP: + if (fk->currentGroup == 0xFFFFFFFF) fk->currentGroup = 0; + else + { + if ((msg = ProcessGroupFinish(fk)) != CERR_None) return msg; // finish off previous group first? + fk->currentGroup++; + } + // if( (err = ProcessGroupLine( fk, p )) != CERR_None ) return err; + break; + + case T_NAME: + kmcmp::WarnDeprecatedHeader(); // I4866 + q = GetDelimitedString(&p, u"\"\"", 0); + if (!q) return CERR_InvalidName; + + if ((msg = AddStore(fk, TSS_NAME, q)) != CERR_None) return msg; + break; + + case T_COPYRIGHT: + kmcmp::WarnDeprecatedHeader(); // I4866 + q = GetDelimitedString(&p, u"\"\"", 0); + if (!q) return CERR_InvalidCopyright; + + if ((msg = AddStore(fk, TSS_COPYRIGHT, q)) != CERR_None) return msg; + break; + + case T_MESSAGE: + kmcmp::WarnDeprecatedHeader(); // I4866 + q = GetDelimitedString(&p, u"\"\"", 0); + if (!q) return CERR_InvalidMessage; + + if ((msg = AddStore(fk, TSS_MESSAGE, q)) != CERR_None) return msg; + break; + + case T_LANGUAGENAME: + kmcmp::WarnDeprecatedHeader(); // I4866 + q = GetDelimitedString(&p, u"\"\"", 0); + if (!q) return CERR_InvalidLanguageName; + + if ((msg = AddStore(fk, TSS_LANGUAGENAME, q)) != CERR_None) return msg; + break; + + case T_LANGUAGE: + { + kmcmp::WarnDeprecatedHeader(); // I4866 + KMX_WCHAR *tokcontext = NULL; + q = u16tok(p, p_sep, &tokcontext); // I3481 + + if ((msg = AddStore(fk, TSS_LANGUAGE, q)) != CERR_None) return msg; + break; + } + case T_LAYOUT: + { + kmcmp::WarnDeprecatedHeader(); // I4866 + KMX_WCHAR *tokcontext = NULL; + q = u16tok(p, p_sep, &tokcontext); // I3481 + if ((msg = AddStore(fk, TSS_LAYOUT, q)) != CERR_None) return msg; + break; + } + case T_CAPSOFF: + kmcmp::WarnDeprecatedHeader(); // I4866 + if ((msg = AddStore(fk, TSS_CAPSALWAYSOFF, u"1")) != CERR_None) return msg; + break; + + case T_CAPSON: + kmcmp::WarnDeprecatedHeader(); // I4866 + if ((msg = AddStore(fk, TSS_CAPSONONLY, u"1")) != CERR_None) return msg; + break; + + case T_SHIFT: + kmcmp::WarnDeprecatedHeader(); // I4866 + if ((msg = AddStore(fk, TSS_SHIFTFREESCAPS, u"1")) != CERR_None) return msg; + break; + + case T_HOTKEY: + { + kmcmp::WarnDeprecatedHeader(); // I4866 + KMX_WCHAR *tokcontext = NULL; + if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_CodeInvalidInThisSection; // I3481 + if ((msg = AddStore(fk, TSS_HOTKEY, q)) != CERR_None) return msg; + break; + } + case T_BITMAP: + { + kmcmp::WarnDeprecatedHeader(); // I4866 + KMX_WCHAR *tokcontext = NULL; + if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481 + + while (iswspace(*q)) q++; + if (*q == '"') { + p = q; + q = GetDelimitedString(&p, u"\"\"", 0); + if (!q) return CERR_InvalidBitmapLine; + } + + if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg; + break; + } + case T_BITMAPS: + { + kmcmp::WarnDeprecatedHeader(); // I4866 + KMX_WCHAR *tokcontext = NULL; + AddWarning(CWARN_BitmapNotUsed); + + if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481 + + if ((PKMX_WCHAR) u16chr(q, ',')) *(PKMX_WCHAR) u16chr(q, ',') = 0; + if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg; + + break; + } + case T_KEYTOKEY: // A rule + if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; + if ((msg = ProcessKeyLine(fk, p, IsUnicode)) != CERR_None) return msg; + break; + + case T_MATCH: + if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; + { + PKMX_WCHAR buf = new KMX_WCHAR[GLOBAL_BUFSIZE]; + if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE - 1, (int)(p - pp), IsUnicode)) != CERR_None) + { + delete[] buf; + return msg; + } + + if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) { + delete[] buf; + return msg; + } + + gp = &fk->dpGroupArray[fk->currentGroup]; + + gp->dpMatch = new KMX_WCHAR[u16len(buf) + 1]; + u16ncpy(gp->dpMatch, buf, u16len(buf) + 1); // I3481 + + delete[] buf; + + if (kmcmp::FSaveDebug) + { + KMX_WCHAR tstr[128]; + //swprintf(tstr, "%d", fk->currentGroup); + /* Record a system store for the line number of the begin statement */ + //wcscpy(tstr, DEBUGSTORE_MATCH); + u16sprintf(tstr, _countof(tstr), L"%ls%d ", u16fmt(DEBUGSTORE_MATCH).c_str(), (int) fk->currentGroup); + u16ncat(tstr, gp->szName, _countof(tstr)); + + AddDebugStore(fk, tstr); + } + } + break; + + case T_NOMATCH: + if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; + { + PKMX_WCHAR buf = new KMX_WCHAR[GLOBAL_BUFSIZE]; + if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE, (int)(p - pp), IsUnicode)) != CERR_None) + { + delete[] buf; + return msg; + } + + if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) { + delete[] buf; + return msg; + } + + gp = &fk->dpGroupArray[fk->currentGroup]; + + gp->dpNoMatch = new KMX_WCHAR[u16len(buf) + 1]; + u16ncpy(gp->dpNoMatch, buf, u16len(buf) + 1); // I3481 + + delete[] buf; + + if (kmcmp::FSaveDebug) + { + KMX_WCHAR tstr[128]; + /* Record a system store for the line number of the begin statement */ + u16sprintf(tstr, _countof(tstr), L"%ls%d ", u16fmt(DEBUGSTORE_NOMATCH).c_str(), (int) fk->currentGroup); + u16ncat(tstr, gp->szName, _countof(tstr)); + AddDebugStore(fk, tstr); + } + } + break; + + default: + return CERR_InvalidToken; + } + + return CERR_None; +} + +//********************************************************************************************************************** + +KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + PFILE_GROUP gp; + PKMX_WCHAR q; + + gp = new FILE_GROUP[fk->cxGroupArray + 1]; + if (!gp) return CERR_CannotAllocateMemory; + + if (fk->dpGroupArray) + { + memcpy(gp, fk->dpGroupArray, sizeof(FILE_GROUP) * fk->cxGroupArray); + delete fk->dpGroupArray; + } + + fk->dpGroupArray = gp; + gp = &fk->dpGroupArray[fk->cxGroupArray]; + fk->cxGroupArray++; + + gp->dpKeyArray = NULL; + gp->dpMatch = NULL; + gp->dpNoMatch = NULL; + gp->cxKeyArray = 0; + + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q) return CERR_InvalidGroupLine; + + gp->fUsingKeys = FALSE; + gp->fReadOnly = IsSameToken(&p, u"readonly"); + if (!gp->fReadOnly) { + if (IsSameToken(&p, u"using") && IsSameToken(&p, u"keys")) + gp->fUsingKeys = TRUE; + } + + safe_wcsncpy(gp->szName, q, SZMAX_GROUPNAME); + + gp->Line = kmcmp::currentLine; + + if (kmcmp::FSaveDebug) + { + KMX_WCHAR tstr[128]; + /* Record a system store for the line number of the begin statement */ + u16sprintf(tstr, _countof(tstr), L"%ls%d ", u16fmt(DEBUGSTORE_GROUP).c_str(), fk->cxGroupArray - 1); + u16ncat(tstr, gp->szName, _countof(tstr)); + AddDebugStore(fk, tstr); + } + + return CheckForDuplicateGroup(fk, gp); +} + +int kmcmp::cmpkeys(const void *key, const void *elem) +{ + PFILE_KEY akey; + PFILE_KEY aelem; + int l1, l2; + KMX_WCHAR char_key, char_elem; + akey = (PFILE_KEY)key; + aelem = (PFILE_KEY)elem; + char_key = kmcmp::VKToChar(akey->Key, akey->ShiftFlags); + char_elem = kmcmp::VKToChar(aelem->Key, aelem->ShiftFlags); + if (char_key == char_elem) //akey->Key == aelem->Key) + { + l1 = xstrlen(akey->dpContext); l2 = xstrlen(aelem->dpContext); + if (l1 == l2) + { + if (akey->Line < aelem->Line) return -1; + if (akey->Line > aelem->Line) return 1; + if(akey->Key == aelem->Key) { + if(akey->ShiftFlags == aelem->ShiftFlags) { + return akey->LineStoreIndex - aelem->LineStoreIndex; + } + return akey->ShiftFlags - aelem->ShiftFlags; + } + return akey->Key - aelem->Key; + } + if (l1 < l2) return 1; + if (l1 > l2) return -1; + if(akey->Key == aelem->Key) { + if(akey->ShiftFlags == aelem->ShiftFlags) { + return akey->LineStoreIndex - aelem->LineStoreIndex; + } + return akey->ShiftFlags - aelem->ShiftFlags; + } + return akey->Key - aelem->Key; + } + return(char_key - char_elem); // akey->Key - aelem->Key); +} + +KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk) +{ + PFILE_GROUP gp; + KMX_DWORD msg; + + if (fk->currentGroup == 0xFFFFFFFF) return CERR_None; + // Just got to first group - so nothing to finish yet + + gp = &fk->dpGroupArray[fk->currentGroup]; + + // Finish off the previous group stuff! + if ((msg = ExpandCapsRulesForGroup(fk, gp)) != CERR_None) return msg; + qsort(gp->dpKeyArray, gp->cxKeyArray, sizeof(FILE_KEY), kmcmp::cmpkeys); + + return VerifyUnreachableRules(gp); +} + +/*************************************** +* Store management +*/ + +KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + PKMX_WCHAR q, pp; + PFILE_STORE sp; + //WCHAR temp[GLOBAL_BUFSIZE]; + KMX_DWORD msg; + int i = 0; + + pp = p; + + if ((q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL)) == NULL) return CERR_InvalidStoreLine; + + if (*q == *SSN__PREFIX) + { + for (i = 0; StoreTokens[i]; i++) + if (!u16icmp(q, StoreTokens[i])) // I3481 + break; + if (!StoreTokens[i]) return CERR_InvalidSystemStore; + } + + sp = new FILE_STORE[fk->cxStoreArray + 1]; + if (!sp) return CERR_CannotAllocateMemory; + + if (fk->dpStoreArray) + { + memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); + delete fk->dpStoreArray; + } + + fk->dpStoreArray = sp; + sp = &fk->dpStoreArray[fk->cxStoreArray]; + + sp->line = kmcmp::currentLine; + sp->fIsOption = FALSE; + sp->fIsReserved = FALSE; + sp->fIsStore = FALSE; + sp->fIsDebug = FALSE; + sp->fIsCall = FALSE; + + safe_wcsncpy(sp->szName, q, SZMAX_STORENAME); + { + PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE]; + + if ((msg = GetXString(fk, p, u"c\n", temp, GLOBAL_BUFSIZE - 1, (int)(p - pp), &p, FALSE, TRUE)) != CERR_None) + { + delete[] temp; + return msg; + } + + sp->dwSystemID = i; + sp->dpString = new KMX_WCHAR[u16len(temp) + 1]; + u16ncpy(sp->dpString, temp, u16len(temp) + 1); // I3481 + + delete[] temp; + } + + if (xstrlen(sp->dpString) == 1 && *sp->dpString != UC_SENTINEL && + sp->dwSystemID == 0 && (fk->version >= VERSION_60 || fk->version == 0)) + { + // In this case, we want to change behaviour for older versioned keyboards so that + // we don't mix up named character codes which weren't supported in 5.x + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); + // Add a single char store as a defined character constant + if (Uni_IsSurrogate1(*sp->dpString)) + kmcmp::CodeConstants->AddCode(Uni_SurrogateToUTF32(sp->dpString[0], sp->dpString[1]), sp->szName, fk->cxStoreArray); + else + kmcmp::CodeConstants->AddCode(sp->dpString[0], sp->szName, fk->cxStoreArray); + kmcmp::CodeConstants->reindex(); // has to be done after every character add due to possible use in another store. // I4982 + } + + fk->cxStoreArray++; // increment now, because GetXString refers to stores + + if (i > 0) + if ((msg = ProcessSystemStore(fk, i, sp)) != CERR_None) return msg; + + return CheckForDuplicateStore(fk, sp); +} + +KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID) +{ + PFILE_STORE sp; + sp = new FILE_STORE[fk->cxStoreArray + 1]; + if (!sp) return CERR_CannotAllocateMemory; + + if (fk->dpStoreArray) + { + memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); + delete fk->dpStoreArray; + } + + fk->dpStoreArray = sp; + sp = &fk->dpStoreArray[fk->cxStoreArray]; + + sp->line = kmcmp::currentLine; + sp->fIsOption = FALSE; // I3686 + sp->fIsReserved = (SystemID != TSS_NONE); + sp->fIsStore = FALSE; + sp->fIsDebug = FALSE; + sp->fIsCall = FALSE; + + safe_wcsncpy(sp->szName, (PKMX_WCHAR) StoreTokens[SystemID], SZMAX_STORENAME); + + sp->dpString = new KMX_WCHAR[u16len(str) + 1]; + u16ncpy(sp->dpString, str, u16len(str) + 1); // I3481 + + sp->dwSystemID = SystemID; + + if (dwStoreID) *dwStoreID = fk->cxStoreArray; + + fk->cxStoreArray++; + + return ProcessSystemStore( fk, SystemID, sp); +} + +KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str) +{ + PFILE_STORE sp; + KMX_WCHAR tstr[16]; + u16sprintf(tstr, _countof(tstr), L"%d", kmcmp::currentLine); // I3481 + + sp = new FILE_STORE[fk->cxStoreArray + 1]; + if (!sp) return CERR_CannotAllocateMemory; + + if (fk->dpStoreArray) + { + memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); + delete[] fk->dpStoreArray; + } + + fk->dpStoreArray = sp; + sp = &fk->dpStoreArray[fk->cxStoreArray]; + + safe_wcsncpy(sp->szName, (PKMX_WCHAR) str, SZMAX_STORENAME); + + sp->dpString = new KMX_WCHAR[u16len(tstr) + 1]; + u16ncpy(sp->dpString, tstr, u16len(tstr) + 1); // I3481 + sp->line = 0; + sp->fIsOption = FALSE; + sp->fIsReserved = TRUE; + sp->fIsStore = FALSE; + sp->fIsDebug = TRUE; + sp->fIsCall = FALSE; + sp->dwSystemID = TSS_DEBUG_LINE; + fk->cxStoreArray++; + + return CERR_None; +} + +PKMX_WCHAR pssBuf = NULL; + +KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp) +{ + //WCHAR buf[GLOBAL_BUFSIZE]; + int i, j; + KMX_DWORD msg; + PKMX_WCHAR p, q; + KMX_CHAR *pp; + + if (!pssBuf) pssBuf = new KMX_WCHAR[GLOBAL_BUFSIZE]; + PKMX_WCHAR buf = pssBuf; + + switch (SystemID) + { + case TSS_BITMAP: + if ((msg = ImportBitmapFile(fk, sp->dpString, &fk->dwBitmapSize, &fk->lpBitmap)) != CERR_None) + return msg; + break; + + case TSS_CALLDEFINITION: + break; + + case TSS_CALLDEFINITION_LOADFAILED: + break; + + case TSS_CAPSALWAYSOFF: + if (*sp->dpString == u'1') fk->dwFlags |= KF_CAPSALWAYSOFF; + break; + + case TSS_CAPSONONLY: + if (*sp->dpString == u'1') fk->dwFlags |= KF_CAPSONONLY; + break; + + case TSS_COMPILEDVERSION: + break; + + case TSS_COPYRIGHT: + break; + + case TSS_DEBUG_LINE: + break; + + case TSS_ETHNOLOGUECODE: + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_EthnologueCode); + if ((msg = ProcessEthnologueStore(sp->dpString)) != CERR_None) return msg; // I2646 + break; + + case TSS_HOTKEY: + if ((msg = ProcessHotKey(sp->dpString, &fk->dwHotKey)) != CERR_None) return msg; + u16sprintf(buf, GLOBAL_BUFSIZE, L"%d", (int)fk->dwHotKey); // I3481 + delete[] sp->dpString; + sp->dpString = new KMX_WCHAR[u16len(buf) + 1]; + u16ncpy(sp->dpString, buf, u16len(buf) + 1); // I3481 + break; + + case TSS_INCLUDECODES: + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); + pp = wstrtostr(sp->dpString); + if (!kmcmp::CodeConstants->LoadFile(pp)) + { + delete[] pp; + return CERR_CannotLoadIncludeFile; + } + delete[] pp; + kmcmp::CodeConstants->reindex(); // I4982 + break; + + case TSS_LANGUAGE: + { + KMX_WCHAR *context = NULL; + KMX_WCHAR sep_c[3] = u", "; + PKMX_WCHAR p_sep_c = sep_c; + q = u16tok(sp->dpString, p_sep_c, &context); // I3481 + if (!q) return CERR_InvalidLanguageLine; + + i = xatoi(&q); + KMX_WCHAR sep_n[4] = u" c\n"; + PKMX_WCHAR p_sep_n = sep_n; + q = u16tok(NULL, p_sep_n, &context); // I3481 + if (!q) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_InvalidLanguageLine); + j = SUBLANGID(i); + i = PRIMARYLANGID(i); + } + else + j = xatoi(&q); + + if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) return CERR_InvalidLanguageLine; + if (i >= 0x200 || j >= 0x20) AddWarning(CWARN_CustomLanguagesNotSupported); + + fk->KeyboardID = (KMX_DWORD)MAKELANGID(i, j); + + u16sprintf(buf, GLOBAL_BUFSIZE, L"%x %x", i, j); // I3481 + delete[] sp->dpString; + sp->dpString = new KMX_WCHAR[u16len(buf) + 1]; + u16ncpy(sp->dpString, buf, u16len(buf) + 1); // I3481 + + break; + } + case TSS_LANGUAGENAME: + break; + + case TSS_LAYOUT: + if (fk->KeyboardID == 0) return CERR_LayoutButNoLanguage; + + q = sp->dpString; + + fk->KeyboardID |= (xatoi(&q) << 16L); + break; + + case TSS_MESSAGE: + break; + + case TSS_MNEMONIC: + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_MnemonicLayout); + kmcmp::FMnemonicLayout = atoiW(sp->dpString) == 1; + if (kmcmp::FMnemonicLayout && FindSystemStore(fk, TSS_CASEDKEYS) != NULL) { + // The &CasedKeys system store is not supported for + // mnemonic layouts + return CERR_CasedKeysNotSupportedWithMnemonicLayout; + } + break; + + case TSS_NAME: + break; + + case TSS_OLDCHARPOSMATCHING: + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_OldCharPosMatching); + kmcmp::FOldCharPosMatching = atoiW(sp->dpString); + break; + + case TSS_SHIFTFREESCAPS: + if (*sp->dpString == u'1') fk->dwFlags |= KF_SHIFTFREESCAPS; + break; + + case TSS_VERSION: + if ((fk->dwFlags & KF_AUTOMATICVERSION) == 0) return CERR_VersionAlreadyIncluded; + p = sp->dpString; + if (u16tof (p) < 5.0) { + AddWarning(CWARN_OldVersion); + } + + if (u16ncmp(p, u"3.0", 3) == 0) fk->version = VERSION_50; //0x0a0b000n= a.bn + else if (u16ncmp(p, u"3.1", 3) == 0) fk->version = VERSION_50; //all versions < 5.0 + else if (u16ncmp(p, u"3.2", 3) == 0) fk->version = VERSION_50; //we compile as if + else if (u16ncmp(p, u"4.0", 3) == 0) fk->version = VERSION_50; //they are 5.0.100.0 + else if (u16ncmp(p, u"5.01", 4) == 0) fk->version = VERSION_501; + else if (u16ncmp(p, u"5.0", 3) == 0) fk->version = VERSION_50; + else if (u16ncmp(p, u"6.0", 3) == 0) fk->version = VERSION_60; + else if (u16ncmp(p, u"7.0", 3) == 0) fk->version = VERSION_70; + else if (u16ncmp(p, u"8.0", 3) == 0) fk->version = VERSION_80; + else if (u16ncmp(p, u"9.0", 3) == 0) fk->version = VERSION_90; + else if (u16ncmp(p, u"10.0", 4) == 0) fk->version = VERSION_100; + else if (u16ncmp(p, u"14.0", 4) == 0) fk->version = VERSION_140; // Adds support for #917 -- context() with notany() for KeymanWeb + else if (u16ncmp(p, u"15.0", 4) == 0) fk->version = VERSION_150; // Adds support for U_xxxx_yyyy #2858 + + else return CERR_InvalidVersion; + + if (fk->version < VERSION_60) kmcmp::FOldCharPosMatching = TRUE; + + fk->dwFlags &= ~KF_AUTOMATICVERSION; + + break; + + case TSS_VISUALKEYBOARD: + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); + { + // Store extra metadata for callers as we mutate this store during + // compilation + fk->extra->kvksFilename = sp->dpString; + // Strip path from the store, leaving bare filename only + p = sp->dpString; + + KMX_WCHAR *pp2 = (KMX_WCHAR*) u16rchr_slash((const PKMX_WCHAR) p); + + if (!pp2) { + pp2 = p; + } else { + pp2++; + } + q = new KMX_WCHAR[u16len(pp2) + 1]; + u16ncpy(q, pp2, u16len(pp2) + 1); + + // Change compiled reference file extension to .kvk + pp2 = ( km_kbp_cp *) u16chr(q, 0) - 5; + if (pp2 > q && u16icmp(pp2, u".kvks") == 0) { + pp2[4] = 0; + } + + delete[] sp->dpString; + sp->dpString = q; + + if ((msg = CheckFilenameConsistency( (sp->dpString), FALSE)) != CERR_None) { + return msg; + } + } + break; + case TSS_KMW_RTL: + case TSS_KMW_HELPTEXT: + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); + break; + + case TSS_KMW_HELPFILE: + case TSS_KMW_EMBEDJS: + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); + if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { + return msg; + } + break; + + case TSS_KMW_EMBEDCSS: + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyEmbedCSS); + if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { + return msg; + } + break; + + case TSS_TARGETS: // I4504 + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyTargets); + break; + + case TSS_WINDOWSLANGUAGES: + { + KMX_WCHAR *context = NULL; + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); + size_t szQ = u16len(sp->dpString) * 6 + 1; // I3481 + q = new KMX_WCHAR[szQ]; // guaranteed to be enough space for recoding + *q = 0; KMX_WCHAR *r = q; + KMX_WCHAR sep_s[4] = u" "; + PKMX_WCHAR p_sep_s = sep_s; + p = u16tok(sp->dpString, p_sep_s, &context); // I3481 + while (p) + { + int n = xatoi(&p); + + j = SUBLANGID(n); + i = PRIMARYLANGID(n); + + if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) { + delete[] q; + return CERR_InvalidLanguageLine; + } + + u16sprintf(r, szQ - (size_t)(r - q), L"x%04.4x ", n); // I3481 + p = u16tok(NULL, sep_s, &context); // I3481 + r = (KMX_WCHAR*) u16chr(q, 0); // I3481 + } + delete[] sp->dpString; + if (*q) { + *((KMX_WCHAR*) u16chr(q, 0) - 1) = 0; // delete final space - safe because we control the formatting - ugly? scared? + } + sp->dpString = q; + break; + } + case TSS_COMPARISON: + VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); + break; + + case TSS_VKDICTIONARY: // I3438 + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyVirtualKeyDictionary); + break; + + case TSS_LAYOUTFILE: // I3483 + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyLayoutFile); // I4140 + if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { + return msg; + } + // Used by KMW compiler + break; + + case TSS_KEYBOARDVERSION: // I4140 + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyKeyboardVersion); + if (!IsValidKeyboardVersion(sp->dpString)) { + return CERR_KeyboardVersionFormatInvalid; + } + break; + + case TSS_CASEDKEYS: + if ((msg = VerifyCasedKeys(sp)) != CERR_None) { + return msg; + } + break; + + case TSS_BEGIN_NEWCONTEXT: + case TSS_BEGIN_POSTKEYSTROKE: + break; + + case TSS_NEWLAYER: + case TSS_OLDLAYER: + break; + + default: + return CERR_InvalidSystemStore; + } + return CERR_None; +} + +KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString) { // I4140 + /* version format \d+(\.\d+)* e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, blank is not allowed */ + + do { + if (!iswdigit(*dpString)) { + return FALSE; + } + while (iswdigit(*dpString)) { + dpString++; + } + if (*dpString == '.') { + dpString++; + if (!iswdigit(*dpString)) { + return FALSE; + } + } + } while (*dpString != 0); + + return TRUE; +} + + +KMX_DWORD kmcmp::AddCompilerVersionStore(PFILE_KEYBOARD fk) +{ + KMX_DWORD msg; + + if(!kmcmp::FShouldAddCompilerVersion) { + return CERR_None; + } + + if ((msg = AddStore(fk, TSS_COMPILEDVERSION, KEYMAN_VersionWin_W16)) != CERR_None) return msg; + + return CERR_None; +} + +/**************************** +* Rule lines +*/ + +KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR context, PKMX_WCHAR output, PKMX_WCHAR key) { + PKMX_WCHAR p, q; + int i; + for (p = output; *p; p = incxstr(p)) { + if (*p == UC_SENTINEL) { + if (*(p + 1) == CODE_INDEX) { + int indexStore = *(p + 2) - 1; + int contextOffset = *(p + 3); + for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++); + + if (*q == 0) { + if (!gp->fUsingKeys) + // no key in the rule, so offset is past end of context + return CERR_IndexDoesNotPointToAny; + if (i < contextOffset) // I4914 + // offset is beyond the key + return CERR_IndexDoesNotPointToAny; + q = key; + } + + // find the any + if (*q != UC_SENTINEL || *(q + 1) != CODE_ANY) + return CERR_IndexDoesNotPointToAny; + + int anyStore = *(q + 2) - 1; + + if (xstrlen(fk->dpStoreArray[indexStore].dpString) < xstrlen(fk->dpStoreArray[anyStore].dpString)) { + AddWarning(CWARN_IndexStoreShort); //TODO: if this fails, then we return FALSE instead of an error + } + } else if (*(p + 1) == CODE_CONTEXTEX) { + int contextOffset = *(p + 2); + if (contextOffset > xstrlen(context)) + return CERR_ContextExHasInvalidOffset; + + // Due to a limitation in earlier versions of KeymanWeb, the minimum version + // for context() referring to notany() is 14.0. See #917 for details. + if (kmcmp::CompileTarget == CKF_KEYMANWEB) { + for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++); + if (*q == UC_SENTINEL && *(q + 1) == CODE_NOTANY) { + VERIFY_KEYBOARD_VERSION(fk, VERSION_140, CERR_140FeatureOnlyContextAndNotAnyWeb); + } + } + } + } + } + return CERR_None; +} + +/** + *Checks that the order of statements in the context matches the specification + * Rule structure: [context] ['+' key] '>' output + * Context structure: [nul] [if()|baselayout()|platform()]+ [char|any|context()|deadkey()|dk()|index()|notany()|outs()] + * Test that nul is first, then if(), baselayout(), platform() statements are before any other content + */ +KMX_BOOL CheckContextStatementPositions(PKMX_WCHAR context) { + KMX_BOOL hadContextChar = FALSE; + for (PKMX_WCHAR p = context; *p; p = incxstr(p)) { + if (*p == UC_SENTINEL) { + switch (*(p + 1)) { + case CODE_NUL: + if (p > context) { + AddWarningBool(CWARN_NulNotFirstStatementInContext); + } + break; + case CODE_IFOPT: + case CODE_IFSYSTEMSTORE: + if (hadContextChar) { + AddWarningBool(CWARN_IfShouldBeAtStartOfContext); + } + break; + default: + hadContextChar = TRUE; + } + } + else { + hadContextChar = TRUE; + } + } + + return TRUE; +} +/** + * Checks if a use() statement is followed by other content in the output of a rule + */ +KMX_DWORD CheckUseStatementsInOutput(PKMX_WCHAR output) { + KMX_BOOL hasUse = FALSE; + PKMX_WCHAR p; + for (p = output; *p; p = incxstr(p)) { + if (*p == UC_SENTINEL && *(p + 1) == CODE_USE) { + hasUse = TRUE; + } else if (hasUse) { + AddWarning(CWARN_UseNotLastStatementInRule); + break; + } + } + return CERR_None; +} + +/** + * Adds implicit `context` to start of output of rules for readonly groups + */ +KMX_DWORD InjectContextToReadonlyOutput(PKMX_WCHAR pklOut) { + if (pklOut[0] != UC_SENTINEL || pklOut[1] != CODE_CONTEXT) { + if (u16len(pklOut) > GLOBAL_BUFSIZE - 3) { + return CERR_CannotAllocateMemory; + } + memmove(pklOut + 2, pklOut, (u16len(pklOut) + 1) * 2); + pklOut[0] = UC_SENTINEL; + pklOut[1] = CODE_CONTEXT; + } + return CERR_None; +} + +/** + * Verifies that a keyboard does not attempt to emit characters or + * other changes to text store when processing a readonly group + */ +KMX_DWORD CheckOutputIsReadonly(const PFILE_KEYBOARD fk, const PKMX_WCHAR output) { // I4867 + PKMX_WCHAR p; + for (p = output; *p; p = incxstr(p)) { + if (*p != UC_SENTINEL) { + return CERR_OutputInReadonlyGroup; + } + switch (*(p + 1)) { + case CODE_CALL: + // We cannot be sure that the callee is going to be readonly + // but we have to operate on a trust basis for call() in any + // case, so we'll allow it. + continue; + case CODE_USE: + // We only allow use() of other readonly groups + { + PFILE_GROUP targetGroup = &fk->dpGroupArray[*(p + 2) - 1]; + if (!targetGroup->fReadOnly) { + return CERR_CannotUseReadWriteGroupFromReadonlyGroup; + } + } + continue; + case CODE_SETOPT: + case CODE_RESETOPT: + case CODE_SAVEOPT: + // it is okay to set, reset or save keyboard options + // although it's hard to see good use cases for this + continue; + case CODE_SETSYSTEMSTORE: + // it is okay to set system stores; Engine or Core will + // ignore set(&) that are not permissible in the given context + continue; + case CODE_CONTEXT: + // We allow `context` but only as the very first statement in output + if (p == output) { + continue; + } + return CERR_OutputInReadonlyGroup; + default: + // Note: conceptually, CODE_NUL could be transformed to CODE_CONTEXT + // if the context was also empty, but it is probably safest to avoid this, + // given CODE_CONTEXT does what we need anyway + return CERR_StatementNotPermittedInReadonlyGroup; + } + } + return CERR_None; +} + +KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode, PKMX_WCHAR pklIn, PKMX_WCHAR pklKey, PKMX_WCHAR pklOut); + +KMX_DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode) +{ + PKMX_WCHAR pklIn, pklKey, pklOut; + + pklIn = new KMX_WCHAR[GLOBAL_BUFSIZE]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer + pklKey = new KMX_WCHAR[GLOBAL_BUFSIZE]; + pklOut = new KMX_WCHAR[GLOBAL_BUFSIZE]; + if (!pklIn || !pklKey || !pklOut) + return CERR_CannotAllocateMemory; // forget about the little leak if pklKey or pklOut fail... + + KMX_DWORD result = ProcessKeyLineImpl(fk, str, IsUnicode, pklIn, pklKey, pklOut); + + delete[] pklIn; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer + delete[] pklKey; + delete[] pklOut; + + return result; +} + +KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode, PKMX_WCHAR pklIn, PKMX_WCHAR pklKey, PKMX_WCHAR pklOut) { + PKMX_WCHAR p, pp; + KMX_DWORD msg; + PFILE_GROUP gp; + PFILE_KEY kp; + + gp = &fk->dpGroupArray[fk->currentGroup]; + + pp = str; + + if (gp->fUsingKeys) { + if ((msg = GetXString(fk, str, u"+", pklIn, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; + + str = p + 1; + if ((msg = GetXString(fk, str, u">", pklKey, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; + if (pklKey[0] == 0) return CERR_ZeroLengthString; + if (xstrlen(pklKey) > 1) AddWarning(CWARN_KeyBadLength); + } else { + if ((msg = GetXString(fk, str, u">", pklIn, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; + if (pklIn[0] == 0) return CERR_ZeroLengthString; + } + + str = p + 1; + if ((msg = GetXString(fk, str, u"c\n", pklOut, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; + + if (pklOut[0] == 0) return CERR_ZeroLengthString; + + CheckContextStatementPositions(pklIn); + + // Test index and context offsets in context + if ((msg = CheckStatementOffsets(fk, gp, pklIn, pklOut, pklKey)) != CERR_None) return msg; + + // Test that use() statements are not followed by other content + if ((msg = CheckUseStatementsInOutput(pklOut)) != CERR_None) { + return msg; // I4867 + } + + if (gp->fReadOnly) { + // Ensure no output is made from the rule, and that + // use() statements meet required readonly semantics + if ((msg = CheckOutputIsReadonly(fk, pklOut)) != CERR_None) { + return msg; + } + + // Inject `context` to start of output if group is readonly + // to keep the output internally consistent + if ((msg = InjectContextToReadonlyOutput(pklOut)) != CERR_None) { + return msg; + } + } + + kp = new FILE_KEY[gp->cxKeyArray + 1]; + if (!kp) return CERR_CannotAllocateMemory; + if (gp->dpKeyArray) + { + memcpy(kp, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); + delete gp->dpKeyArray; + } + + gp->dpKeyArray = kp; + kp = &gp->dpKeyArray[gp->cxKeyArray]; + + gp->cxKeyArray++; + + kp->dpOutput = new KMX_WCHAR[u16len(pklOut) + 1]; + u16ncpy(kp->dpOutput, pklOut, u16len(pklOut) + 1); // I3481 + + kp->dpContext = new KMX_WCHAR[u16len(pklIn) + 1]; + u16ncpy(kp->dpContext, pklIn, u16len(pklIn) + 1); // I3481 + + kp->Line = kmcmp::currentLine; + kp->LineStoreIndex = 0; + + // Finished if we are not using keys + + if (!gp->fUsingKeys) + { + kp->Key = 0; + kp->ShiftFlags = 0; + return CERR_None; + } + + // Expand each rule out into multiple rules - much faster processing at the key hit time + + if (*pklKey == 0) return CERR_ZeroLengthString; + + if (*pklKey == UC_SENTINEL) + switch (*(pklKey + 1)) + { + case CODE_ANY: + kp->ShiftFlags = 0; + if ((msg = ExpandKp(fk, kp, *(pklKey + 2) - 1)) != CERR_None) return msg; + break; + + case CODE_EXTENDED: + kp->Key = *(pklKey + 3); + kp->ShiftFlags = *(pklKey + 2); + break; + + default: + return CERR_InvalidCodeInKeyPartOfRule; + } + else + { + kp->ShiftFlags = 0; + kp->Key = *pklKey; + } + + return CERR_None; +} + + + +KMX_DWORD ExpandKp_ReplaceIndex(PFILE_KEYBOARD fk, PFILE_KEY k, KMX_DWORD keyIndex, int nAnyIndex) +{ + /* Replace each index(xx,keyIndex) in k->dpOutput with appropriate char as based on nAnyIndex */ + PFILE_STORE s; + int i; + PKMX_WCHAR pIndex, pStore; + + for (pIndex = k->dpOutput; *pIndex; pIndex = incxstr(pIndex)) + { + if (*pIndex == UC_SENTINEL && *(pIndex + 1) == CODE_INDEX && *(pIndex + 3) == keyIndex) + { + s = &fk->dpStoreArray[*(pIndex + 2) - 1]; + for (i = 0, pStore = s->dpString; i < nAnyIndex; i++, pStore = incxstr(pStore)); + PKMX_WCHAR qStore = incxstr(pStore); + + int w = (int)(qStore - pStore); + if (w > 4) + { + *pIndex = UC_SENTINEL; + *(pIndex + 1) = CODE_BEEP; + memmove(pIndex + 2, pIndex + 4, u16len(pIndex + 3) * 2); + } + else + { + memcpy(pIndex, pStore, w * 2); + if (w < 4) memmove(pIndex + w, pIndex + 4, u16len(pIndex + 3) * 2); + } + } + } + + return CERR_None; +} + + +KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex) +{ + PFILE_KEY k; + PKMX_WCHAR pn; + KMX_DWORD nchrs, n; + int keyIndex; + + PFILE_STORE sp = &fk->dpStoreArray[storeIndex]; + PFILE_GROUP gp = &fk->dpGroupArray[fk->currentGroup]; + + PKMX_WCHAR dpContext = kpp->dpContext; + PKMX_WCHAR dpOutput = kpp->dpOutput; + + nchrs = xstrlen(sp->dpString); + pn = sp->dpString; + keyIndex = xstrlen(dpContext) + 1; + + /* + Now we change them to plain characters in the output in multiple rules, + and set the keystroke to the appropriate character in the store. + */ + + k = new FILE_KEY[gp->cxKeyArray + nchrs - 1]; + if (!k) return CERR_CannotAllocateMemory; + memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); + + kpp = &k[(int)(kpp - gp->dpKeyArray)]; + + delete gp->dpKeyArray; + gp->dpKeyArray = k; + gp->cxKeyArray += nchrs - 1; + + for (k = kpp, n = 0, pn = sp->dpString; *pn; pn = incxstr(pn), k++, n++) + { + k->dpContext = new KMX_WCHAR[u16len(dpContext) + 1]; + k->dpOutput = new KMX_WCHAR[u16len(dpOutput) + 1]; + + u16ncpy(k->dpContext, dpContext, u16len(dpContext) + 1); // copy the context. // I3481 + u16ncpy(k->dpOutput, dpOutput, u16len(dpOutput) + 1); // copy the output. + + if (*pn == UC_SENTINEL) + { + switch (*(pn + 1)) + { + case CODE_EXTENDED: + k->Key = *(pn + 3); // set the key to store offset. + k->ShiftFlags = *(pn + 2); + break; + default: + return CERR_CodeInvalidInKeyStore; + } + } + else + { + k->Key = *pn; // set the key to store offset. + k->ShiftFlags = 0; + } + k->Line = kpp->Line; + k->LineStoreIndex = n; + ExpandKp_ReplaceIndex(fk, k, keyIndex, n); + } + + delete dpContext; + delete dpOutput; + + return CERR_None; +} + + +PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags) +{ + PKMX_WCHAR q, r; + KMX_WCHAR dOpen, dClose; + + dOpen = *Delimiters; dClose = *(Delimiters + 1); + + q = *p; + while (iswspace(*q)) q++; //***QUERY + + if (*q != dOpen) return NULL; + + q++; + + r = xstrchr(q, &dClose); // Find closing delimiter + if (!r) return NULL; + + if (Flags & GDS_CUTLEAD) + while (iswspace(*q)) q++; // cut off leading spaces + + if (Flags & GDS_CUTFOLL) + if (!iswspace(*(r - 1))) *r = 0; + else + { + r--; // Cut off following spaces + while (iswspace(*r) && r > q) r--; + r++; + *r = 0; r = xstrchr((r + 1), &dClose); + } + else *r = 0; + + r++; while (iswspace(*r)) r++; // Ignore spaces after the close + if (*r == 0) r--; // Safety for terminating strings. + + *p = r; // Update pointer position + + return q; // Return delimited string +} + +#ifndef __iswcsym +#define __iswcsym(_c) (iswalnum(_c) || ((_c)=='_')) +#endif + +LinePrefixType GetLinePrefixType(PKMX_WCHAR *p) +{ + PKMX_WCHAR s = *p; + + while (iswspace(*s)) s++; + + PKMX_WCHAR q = s; + + if (*s != '$') return lptNone; + + /* I1569 - fix named constants at the start of the line */ + s++; + while (__iswcsym(*s)) s++; + if (*s != ':') return lptNone; + + if (u16nicmp(q, u"$keyman:", 8) == 0) + { + *p += 8; + return lptKeymanAndKeymanWeb; + } + if (u16nicmp(q, u"$keymanweb:", 11) == 0) + { + *p += 11; + return lptKeymanWebOnly; + } + if (u16nicmp(q, u"$keymanonly:", 12) == 0) + { + *p += 12; + return lptKeymanOnly; + } + + return lptOther; +} + +int LineTokenType(PKMX_WCHAR *str) +{ + int i; + size_t l; + PKMX_WCHAR p = *str; + + LinePrefixType lpt = GetLinePrefixType(&p); + if (lpt == lptOther) return T_BLANK; + + /* Test KeymanWeb, Keyman and KeymanOnly prefixes */ + if (kmcmp::CompileTarget == CKF_KEYMAN && lpt == lptKeymanWebOnly) return T_BLANK; + if (kmcmp::CompileTarget == CKF_KEYMANWEB && lpt == lptKeymanOnly) return T_BLANK; + + while (iswspace(*p)) p++; + + if (u16chr(LineTokens[0], towupper(*p))) + for (i = 0; i <= T_W_END - T_W_START; i++) + { + l = u16len(LineTokens[i + 1]); + if (u16nicmp(p, LineTokens[i + 1], l) == 0) + { + p += l; while (iswspace(*p)) p++; *str = p; + return i + T_W_START; + } + } + + switch (towupper(*p)) + { + case 'C': + if (iswspace(*(p + 1))) return T_COMMENT; + break; + case 0: + return T_BLANK; + default: + if (u16chr(u"\"aAbBlLpPnN[OoxXdD0123456789\'+UuiI$", *p)) // I4784 + { + *str = p; + return T_KEYTOKEY; + } + } + return T_UNKNOWN; +} + +KMX_WCHAR const * DeadKeyChars = +u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + +KMX_BOOL StrValidChrs(PKMX_WCHAR q, KMX_WCHAR const * chrs) +{ + for (; *q; q++) + if (!u16chr(chrs, *q)) return FALSE; + return TRUE; +} + +KMX_DWORD GetXStringImpl(PKMX_WCHAR tstr, PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, + PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isUnicode +); + +KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, + PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int /*isVKey*/, int isUnicode +) { + PKMX_WCHAR tstr = new KMX_WCHAR[max]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - GetXString is re-entrant with if() + KMX_DWORD err = GetXStringImpl(tstr, fk, str, token, output, max, offset, newp, isUnicode); + delete[] tstr; + return err; +} + +KMX_DWORD GetXStringImpl(PKMX_WCHAR tstr, PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, + PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isUnicode +) { + KMX_DWORD err; + PKMX_WCHAR p = str, q, r; + int type, mx = 0, n, n1, n2, tokenFound = FALSE, z, sFlag = 0, j; + KMX_DWORD i; + KMX_BOOL finished = FALSE; + KMX_WCHAR c; + + *tstr = 0; + + *output = 0; + + p = str; + do + { + tokenFound = FALSE; + while (iswspace(*p) && !u16chr(token, *p)) p++; + if (!*p) break; + + ErrChr = (int)(p - str) + offset + 1; + + /* + char *tokenTypes[] = { + "clearcontext", "deadkey", "context", "return", "switch", + "index", "outs", "beep", "nul", "use", "any", "fix", "dk", "k_", "x", "d", "c", + "[", "]" }; + */ + + switch (towupper(*p)) + { + case 'X': + case 'D': type = 0; break; // xFF, d130: chars, deadkey(n) + case '\"': type = 1; break; // "xxxx": chars + case '\'': type = 2; break; // 'xxxx': chars + case 'A': type = 3; break; // any(s) + case 'B': type = 4; break; // beep, baselayout (synonym for if(&baselayout)) // I3430 + case 'I': type = 5; break; // index(s,n), if + case 'O': type = 6; break; // outs(s) + case 'C': type = 7; break; // context, comments, clearcontext, call(s) + case 'N': type = 8; break; // nul, notany + case 'U': type = 9; break; // use(g) + case 'R': type = 10; break; // return, reset + case '[': type = 11; break; // start of vkey section + //case ']': type = 12; break; // end of vkey section + //case 'K': type = 13; break; // virtual key name or "key" + case 'S': type = 14; break; // switch, set, save + case 'F': type = 15; break; // fix (synonym for clearcontext) + case '$': type = 16; break; // named code constants + case 'P': type = 17; break; // platform (synonym for if(&platform)) // I3430 + case 'L': type = 18; break; // layer (synonym for set(&layer)) // I3437 + case '.': type = 19; break; // .. allows us to specify ranges -- either vkeys or characters + default: + if (iswdigit(*p)) type = 0; // octal number + else type = 99; // error! + } + if (u16chr(token, *p)) tokenFound = TRUE; + + switch (type) + { + case 99: + if (tokenFound) break; + { + snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, "token: %c",(int)*p); + } + return CERR_InvalidToken; + case 0: + if (u16nicmp(p, u"deadkey", z = 7) == 0 || + u16nicmp(p, u"dk", z = 2) == 0) + { + p += z; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidDeadkey; + + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_DEADKEY; + if (!StrValidChrs(q, DeadKeyChars)) return CERR_InvalidDeadkey; + tstr[mx++] = GetDeadKey(fk, q); //atoiW(q); 7-5-01: named deadkeys + tstr[mx] = 0; + } + else + { + n = xatoi(&p); + if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue; + if ((err = kmcmp::UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err; + tstr[mx++] = n1; + if (n2 >= 0) tstr[mx++] = n2; + tstr[mx] = 0; + } + continue; + + case 1: + q = (PKMX_WCHAR) u16chr(p + 1, '\"'); + if (!q) return CERR_UnterminatedString; + if ((int)(q - p) - 1 + mx > max) return CERR_UnterminatedString; + if (sFlag) return CERR_StringInVirtualKeySection; + u16ncat(tstr, p + 1, (int)(q - p) - 1); // I3481 + mx += (int)(q - p) - 1; + tstr[mx] = 0; + p = q + 1; + continue; + case 2: + q = (PKMX_WCHAR) u16chr(p + 1, '\''); + if (!q) return CERR_UnterminatedString; + if ((int)(q - p) - 1 + mx > max) return CERR_UnterminatedString; + if (sFlag) return CERR_StringInVirtualKeySection; + u16ncat(tstr, p + 1, (int)(q - p) - 1); // I3481 + mx += (int)(q - p) - 1; + tstr[mx] = 0; + p = q + 1; + continue; + case 3: + if (u16nicmp(p, u"any", 3) != 0) return CERR_InvalidToken; + if (sFlag) return CERR_AnyInVirtualKeySection; + p += 3; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidAny; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + + if (!*fk->dpStoreArray[i].dpString) return CERR_ZeroLengthString; + kmcmp::CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); + + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_ANY; + tstr[mx++] = (KMX_WCHAR)i + 1; // store to index + 1, avoids End-of-string + tstr[mx] = 0; + continue; + case 4: + if (u16nicmp(p, u"beep", 4) == 0) + { + if (sFlag) return CERR_BeepInVirtualKeySection; + p += 4; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_BEEP; + tstr[mx] = 0; + } + else if (u16nicmp(p, u"baselayout", 10) == 0) // I3430 + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 10; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidToken; + err = process_baselayout(fk, q, tstr, &mx); + if (err != CERR_None) return err; + } + else + return CERR_InvalidToken; + + continue; + case 5: + if (u16nicmp(p, u"if", 2) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 2; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidIf; + + err = process_if(fk, q, tstr, &mx); + if (err != CERR_None) return err; + } + else + { + if (u16nicmp(p, u"index", 5) != 0) return CERR_InvalidToken; + if (sFlag) return CERR_IndexInVirtualKeySection; + p += 5; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + + if (!q || !*q) return CERR_InvalidIndex; + + { + KMX_WCHAR *context = NULL; + KMX_WCHAR sep_com[3] = u" ,"; + PKMX_WCHAR p_sep_com = sep_com; + r = u16tok(q, p_sep_com, &context); // I3481 + if (!r) return CERR_InvalidIndex; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(r, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + + kmcmp::CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); + + r = u16tok(NULL, p_sep_com, &context); // I3481 + if (!r) return CERR_InvalidIndex; + } + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_INDEX; + tstr[mx++] = (KMX_WCHAR)i + 1; // avoid EOS for stores + tstr[mx++] = atoiW(r); // character offset of original any. + + tstr[mx] = 0; + } + continue; + case 6: + if (u16nicmp(p, u"outs", 4) != 0) return CERR_InvalidToken; + if (sFlag) return CERR_OutsInVirtualKeySection; + p += 4; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidOuts; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + + kmcmp::CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); + + for (q = fk->dpStoreArray[i].dpString; *q; q++) + { + tstr[mx++] = *q; + if (mx >= max - 1) return CERR_BufferOverflow; + } + tstr[mx] = 0; + continue; + case 7: + if (iswspace(*(p + 1))) break; // is a comment -- pre-stripped - so why this test? + if (u16nicmp(p, u"context", 7) == 0) + { + if (sFlag) return CERR_ContextInVirtualKeySection; + p += 7; + + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (q && *q) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_Contextn); + int n1b; + n1b = atoiW(q); + if (n1b < 1 || n1b >= 0xF000) return CERR_InvalidToken; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_CONTEXTEX; + tstr[mx++] = n1b; + tstr[mx] = 0; + } + else + { + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_CONTEXT; + tstr[mx] = 0; + } + } + else if (u16nicmp(p, u"clearcontext", 12) == 0) + { + p += 12; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_CLEARCONTEXT; + tstr[mx] = 0; + } + else if (u16nicmp(p, u"call", 4) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_501, CERR_501FeatureOnly_Call); + if (sFlag) return CERR_CallInVirtualKeySection; + p += 4; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidCall; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + + if (!kmcmp::IsValidCallStore(&fk->dpStoreArray[i])) return CERR_InvalidCall; + kmcmp::CheckStoreUsage(fk, i, FALSE, FALSE, TRUE); + + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_CALL; + tstr[mx++] = (KMX_WCHAR)i + 1; + tstr[mx] = 0; + + fk->dpStoreArray[i].dwSystemID = TSS_CALLDEFINITION; + } + else + return CERR_InvalidToken; + continue; + case 8: + if (u16nicmp(p, u"notany", 6) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly) + if (sFlag) return CERR_AnyInVirtualKeySection; + p += 6; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidAny; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + kmcmp::CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_NOTANY; + tstr[mx++] = (KMX_WCHAR)i + 1; // store to index + 1, avoids End-of-string + tstr[mx] = 0; + continue; + } + if (u16nicmp(p, u"nul", 3) != 0) return CERR_InvalidToken; + + p += 3; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_NUL; + tstr[mx] = 0; + continue; + case 9: + if (u16nicmp(p, u"use", 3) != 0) + { + if (*(p + 1) == '+') + { + n = xatoi(&p); + if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue; + if ((err = kmcmp::UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err; + tstr[mx++] = n1; + if (n2 >= 0) tstr[mx++] = n2; + tstr[mx] = 0; + if (!isUnicode) AddWarning(CWARN_UnicodeInANSIGroup); + continue; + } + return CERR_InvalidToken; + } + p += 3; + + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidUse; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_USE; + tstr[mx] = GetGroupNum(fk, q); + if (tstr[mx] == 0) return CERR_GroupDoesNotExist; + tstr[++mx] = 0; + continue; + case 10: + if (u16nicmp(p, u"reset", 5) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 5; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidReset; + + err = process_reset(fk, q, tstr, &mx); + if (err != CERR_None) return err; + } + else + { + if (u16nicmp(p, u"return", 6) != 0) return CERR_InvalidToken; + + p += 6; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_RETURN; + tstr[mx] = 0; + u16ncpy(output, tstr, max); // I3481 + output[max - 1] = 0; + return 0; + } + continue; + case 11: + p++; sFlag = ISVIRTUALKEY /* 0 */; finished = FALSE; + + //printf("--EXTENDEDSTRING--\n"); + + do + { + while (iswspace(*p)) p++; + + switch (towupper(*p)) + { + case 'N': + if (u16nicmp(p, u"NCAPS", 5) == 0) + sFlag |= NOTCAPITALFLAG, p += 5; + else finished = TRUE; + break; + case 'L': + if (u16nicmp(p, u"LALT", 4) == 0) + sFlag |= LALTFLAG, p += 4; + else if (u16nicmp(p, u"LCTRL", 5) == 0) + sFlag |= LCTRLFLAG, p += 5; + else finished = TRUE; + break; + case 'R': + if (u16nicmp(p, u"RALT", 4) == 0) + sFlag |= RALTFLAG, p += 4; + else if (u16nicmp(p, u"RCTRL", 5) == 0) + sFlag |= RCTRLFLAG, p += 5; + else finished = TRUE; + break; + case 'A': + if (u16nicmp(p, u"ALT", 3) == 0) + sFlag |= K_ALTFLAG, p += 3; + else finished = TRUE; + break; + case 'C': + if (u16nicmp(p, u"CTRL", 4) == 0) + sFlag |= K_CTRLFLAG, p += 4; + else if (u16nicmp(p, u"CAPS", 4) == 0) + sFlag |= CAPITALFLAG, p += 4; + else finished = TRUE; + break; + case 'S': + if (u16nicmp(p, u"SHIFT", 5) == 0) + sFlag |= K_SHIFTFLAG, p += 5; + else finished = TRUE; + break; + default: + finished = TRUE; + break; + } + } while (!finished); + + if ((sFlag & (LCTRLFLAG | LALTFLAG)) && (sFlag & (RCTRLFLAG | RALTFLAG))) { + AddWarning(CWARN_MixingLeftAndRightModifiers); + } + + // If we use chiral modifiers, or we use state keys, and we target web in the keyboard, and we don't manually specify a keyboard version, bump the minimum + // version to 10.0. This makes an assumption that if we are using these features in a keyboard and it has no version specified, that we want to use the features + // in the web target platform, even if there are platform() rules excluding this possibility. In that (rare) situation, the keyboard developer should simply specify + // the &version to be 9.0 or whatever to avoid this behaviour. + if (sFlag & (LCTRLFLAG | LALTFLAG | RCTRLFLAG | RALTFLAG | CAPITALFLAG | NOTCAPITALFLAG | NUMLOCKFLAG | NOTNUMLOCKFLAG | SCROLLFLAG | NOTSCROLLFLAG) && + kmcmp::CompileTarget == CKF_KEYMANWEB && + fk->dwFlags & KF_AUTOMATICVERSION) { + VERIFY_KEYBOARD_VERSION(fk, VERSION_100, 0); + } + //printf("sFlag: %x\n", sFlag); + + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_EXTENDED; + tstr[mx++] = sFlag; + + while (iswspace(*p)) p++; + + q = p; + + if (*q == ']') + { + return CERR_InvalidToken; // I3137 - key portion of VK is missing e.g. "[CTRL ALT]", this generates invalid kmx file that can crash Keyman or compiler later on // I3511 + } + + while (*q != ']') + { + if (*q == '\'' || *q == '"') + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_VirtualCharKey); + if (!kmcmp::FMnemonicLayout) AddWarning(CWARN_VirtualCharKeyWithPositionalLayout); + KMX_WCHAR chQuote = *q; + q++; if (*q == chQuote || *q == '\n' || *q == 0) return CERR_InvalidToken; + tstr[mx - 1] |= VIRTUALCHARKEY; + tstr[mx++] = *q; + q++; if (*q != chQuote) return CERR_InvalidToken; + q++; + while (iswspace(*q)) q++; + if (*q != ']') return CERR_InvalidToken; + break; /* out of while loop */ + } + + for (j = 0; !iswspace(*q) && *q != ']' && *q != 0; q++, j++); + + if (*q == 0) return CERR_InvalidToken; + + KMX_WCHAR vkname[SZMAX_VKDICTIONARYNAME]; // I3438 + + if (j >= SZMAX_VKDICTIONARYNAME) return CERR_InvalidToken; + + u16ncpy(vkname, p, j); // I3481 + vkname[j] = 0; + + if (u16icmp(vkname, u"K_NPENTER") == 0) + i = 5; // I649 - K_NPENTER hack + else + { + for (i = 0; i <= VK__MAX; i++) + { + if (u16icmp(vkname, VKeyNames[i]) == 0 || u16icmp(vkname, VKeyISO9995Names[i]) == 0) + break; + } + } + + if (i == VK__MAX + 1) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_InvalidToken); + + i = GetVKCode(fk, vkname); // I3438 + if (i == 0) + return CERR_InvalidToken; + } + + p = q; + + tstr[mx++] = (int)i; + + if (kmcmp::FMnemonicLayout && (i <= VK__MAX) && VKeyMayBeVCKey[i]) AddWarning(CWARN_VirtualKeyWithMnemonicLayout); // I3438 + + while (iswspace(*q)) q++; + } + tstr[mx++] = UC_SENTINEL_EXTENDEDEND; + tstr[mx] = 0; + //printf("--EXTENDEDEND--\n"); + + p = q + 1; + + sFlag = 0; + + continue; + case 14: + if (u16nicmp(p, u"set", 3) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); + p += 3; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidSet; + + err = process_set(fk, q, tstr, &mx); + if (err != CERR_None) return err; + } + else if (u16nicmp(p, u"save", 4) == 0) + { + VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); + p += 4; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidSave; + + err = process_save(fk, q, tstr, &mx); + if (err != CERR_None) return err; + } + else + { + if (u16nicmp(p, u"switch", 6) != 0) return CERR_InvalidToken; + p += 6; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidSwitch; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_SWITCH; + tstr[mx++] = atoiW(q); + tstr[mx] = 0; + } + continue; + case 15: + if (u16nicmp(p, u"fix", 3) == 0) + { + p += 3; + tstr[mx++] = UC_SENTINEL; + tstr[mx++] = CODE_CLEARCONTEXT; + tstr[mx] = 0; + } + else + return CERR_InvalidToken; + continue; + case 16: + VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); + q = p + 1; + while (*q && !iswspace(*q)) q++; + c = *q; *q = 0; + n = kmcmp::CodeConstants->GetCode(p + 1, &i); + *q = c; + if (n == 0) return CERR_InvalidNamedCode; + if (i < 0xFFFFFFFFL) kmcmp::CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); // I2993 + if (n > 0xFFFF) + { + tstr[mx++] = Uni_UTF32ToSurrogate1(n); + tstr[mx++] = Uni_UTF32ToSurrogate2(n); + } + else + tstr[mx++] = n; + tstr[mx] = 0; + p = q; + continue; + case 17: + if (u16nicmp(p, u"platform", 8) != 0) return CERR_InvalidToken; // I3430 + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 8; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidToken; + err = process_platform(fk, q, tstr, &mx); + if (err != CERR_None) return err; + continue; + case 18: // I3437 + if (u16nicmp(p, u"layer", 5) != 0) return CERR_InvalidToken; + VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores); + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 5; + q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL); + if (!q || !*q) return CERR_InvalidToken; + err = process_set_synonym(TSS_LAYER, fk, q, tstr, &mx); + if (err != CERR_None) return err; + continue; + case 19: // #2241 + if (*(p + 1) != '.') return CERR_InvalidToken; + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 2; + err = process_expansion(fk, p, tstr, &mx, max); + if (err != CERR_None) return err; + continue; + default: + return CERR_InvalidToken; + } + if (tokenFound) + { + *newp = p; + u16ncpy(output, tstr, max); // I3481 + output[max - 1] = 0; + ErrChr = 0; + return CERR_None; + } + if (mx >= max) return CERR_BufferOverflow; + } while (*p); + + if (!*token) + { + *newp = p; + u16ncpy(output, tstr, max); // I3481 + output[max - 1] = 0; + ErrChr = 0; + return CERR_None; + } + + return CERR_NoTokensFound; +} + +KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx); // I3430 + +KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) // I3430 +{ + /* baselayout() */ + return process_if_synonym(TSS_BASELAYOUT, fk, q, tstr, mx); +} + +KMX_DWORD process_platform(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) // I3430 +{ + /* platform() */ + return process_if_synonym(TSS_PLATFORM, fk, q, tstr, mx); +} + +KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) // I3430 +{ + PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE]; + + KMX_DWORD msg; + + PKMX_WCHAR r; + + if ((msg = GetXString(fk, q, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + delete[] temp; + return msg; + } + + KMX_DWORD dwStoreID; + + if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None) + { + delete[] temp; + return msg; + } + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = (KMX_WCHAR)CODE_IFSYSTEMSTORE; + tstr[(*mx)++] = (KMX_WCHAR)(dwSystemID + 1); // I4785 + tstr[(*mx)++] = 2; + tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1); + tstr[(*mx)] = 0; + + delete[] temp; + + return CERR_None; +} + +KMX_DWORD process_if(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) // I3431 +{ + /* if( <'='|'!='> ) */ + KMX_DWORD i, code; + KMX_DWORD nnot = FALSE; + PKMX_WCHAR r = q, s = q; + while (*s && *s != u' ' && *s != u'!' && *s !=u'=') s++; + r = s; + while (*s == u' ') s++; + if (*s == u'!') + { + s++; + nnot = TRUE; + } + + if (*s != '=') return CERR_InvalidIf; + s++; + while (*s == ' ') s++; + *r = 0; + r = q; + + if (r[0] == '&') + { + VERIFY_KEYBOARD_VERSION( fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); + for (i = 0; StoreTokens[i]; i++) + { + if (u16icmp(r, StoreTokens[i]) == 0) break; + } + if (!StoreTokens[i]) return CERR_IfSystemStore_NotFound; + code = CODE_IFSYSTEMSTORE; + } + else + { + code = CODE_IFOPT; + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(r, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + kmcmp::CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); + } + + PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE]; + + KMX_DWORD msg; + + if ((msg = GetXString(fk, s, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + delete[] temp; + return msg; + } + + KMX_DWORD dwStoreID; + + if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None) + { + delete[] temp; + return msg; + } + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = (KMX_WCHAR)code; + tstr[(*mx)++] = (KMX_WCHAR)(i + 1); + tstr[(*mx)++] = nnot ? 1 : 2; + tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1); + tstr[(*mx)] = 0; + + return CERR_None; +} + +KMX_DWORD process_reset(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) +{ + /* reset() */ + KMX_DWORD i; + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + kmcmp::CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = CODE_RESETOPT; + tstr[(*mx)++] = (KMX_WCHAR)(i + 1); + tstr[(*mx)] = 0; + + return CERR_None; +} + +KMX_DWORD process_expansion(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx, int max) { + KMX_BOOL isVKey = FALSE; + + KMX_WORD BaseKey=0, BaseShiftFlags=0; + KMX_DWORD BaseChar=0; + + if (*mx == 0) { + return CERR_ExpansionMustFollowCharacterOrVKey; + } + PKMX_WCHAR p = &tstr[*mx]; + p = decxstr(p, tstr); + if (*p == UC_SENTINEL) { + if (*(p + 1) != CODE_EXTENDED) { + return CERR_ExpansionMustFollowCharacterOrVKey; + } + isVKey = TRUE; + BaseKey = *(p + 3); + BaseShiftFlags = *(p + 2); + } + else { + BaseChar = Uni_UTF16ToUTF32(p); + } + + // Look ahead at next element + KMX_WCHAR temp[GLOBAL_BUFSIZE]; + PKMX_WCHAR r = NULL; + + KMX_DWORD msg; + + if ((msg = GetXString(fk, q, u"", temp, (KMX_DWORD)_countof(temp) - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + return msg; + } + + KMX_WORD HighKey, HighShiftFlags; + KMX_DWORD HighChar; + + switch(temp[0]) { + case 0: + return isVKey ? CERR_VKeyExpansionMustBeFollowedByVKey : CERR_CharacterExpansionMustBeFollowedByCharacter; + case UC_SENTINEL: + // Verify that range is valid virtual key range + if(!isVKey) { + return CERR_CharacterExpansionMustBeFollowedByCharacter; + } + if (temp[1] != CODE_EXTENDED) { + return CERR_VKeyExpansionMustBeFollowedByVKey; + } + HighKey = temp[3], HighShiftFlags = temp[2]; + if (HighShiftFlags != BaseShiftFlags) { + return CERR_VKeyExpansionMustUseConsistentShift; + } + if (HighKey <= BaseKey) { + return CERR_ExpansionMustBePositive; + } + // Verify space in buffer + if (*mx + (HighKey - BaseKey) * 5 + 1 >= max) return CERR_BufferOverflow; + // Inject an expansion. + for (BaseKey++; BaseKey < HighKey; BaseKey++) { + // < HighKey because caller will add HighKey to output + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = CODE_EXTENDED; + tstr[(*mx)++] = BaseShiftFlags; + tstr[(*mx)++] = BaseKey; + tstr[(*mx)++] = UC_SENTINEL_EXTENDEDEND; + } + tstr[*mx] = 0; + break; + default: + // Verify that range is a valid character range + if (isVKey) { + return CERR_VKeyExpansionMustBeFollowedByVKey; + } + + HighChar = Uni_UTF16ToUTF32(temp); + if (HighChar <= BaseChar) { + return CERR_ExpansionMustBePositive; + } + // Inject an expansion. + for (BaseChar++; BaseChar < HighChar; BaseChar++) { + // < HighChar because caller will add HighChar to output + if (Uni_IsSMP(BaseChar)) { + // We'll test on each char to avoid complex calculations crossing SMP boundary + if (*mx + 3 >= max) return CERR_BufferOverflow; + tstr[(*mx)++] = (KMX_WCHAR) Uni_UTF32ToSurrogate1(BaseChar); + tstr[(*mx)++] = (KMX_WCHAR) Uni_UTF32ToSurrogate2(BaseChar); + } + else { + if (*mx + 2 >= max) return CERR_BufferOverflow; + tstr[(*mx)++] = (KMX_WCHAR) BaseChar; + } + } + tstr[*mx] = 0; + } + + return CERR_None; +} + +KMX_DWORD process_set_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) // I3437 +{ + /* set( <'='> ), layer */ + PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE], r; + KMX_DWORD msg; + + if ((msg = GetXString(fk, q, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + delete[] temp; + return msg; + } + + KMX_DWORD dwStoreID; + + msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID); + delete[] temp; + if (msg != CERR_None) return msg; + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = (KMX_WCHAR)CODE_SETSYSTEMSTORE; + tstr[(*mx)++] = (KMX_WCHAR)(dwSystemID + 1); + tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1); + tstr[(*mx)] = 0; + return CERR_None; +} + +KMX_DWORD process_set(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) +{ + /* set( <'='> */ + PKMX_WCHAR r = q, s = q; // I3440 + while (*s && *s != u' ' && *s != u'=') s++; + r = s; + while (*s == u' ') s++; + if (*s != '=') return CERR_InvalidSet; + s++; + while (*s == ' ') s++; + *r = 0; + r = q; + + KMX_DWORD i, code; + + if (r[0] == '&') + { + VERIFY_KEYBOARD_VERSION((PFILE_KEYBOARD) fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores); // I3437 + for (i = 0; StoreTokens[i]; i++) + { + if (u16icmp(r, StoreTokens[i]) == 0) break; + } + if (!StoreTokens[i]) return CERR_SetSystemStore_NotFound; + code = CODE_SETSYSTEMSTORE; + } + else + { + KMX_WCHAR *context = NULL; + KMX_WCHAR sep_eq[3] = u" ="; + PKMX_WCHAR r2 = u16tok(q, sep_eq, &context); // I3481 + + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(r2, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + kmcmp::CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); + code = CODE_SETOPT; + } + + PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE]; + + KMX_DWORD msg; + + //r = wcstok(NULL, L" ="); + + if ((msg = GetXString(fk, s, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + delete[] temp; + return msg; + } + + KMX_DWORD dwStoreID; + + msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID); + delete[] temp; + if (msg != CERR_None) return msg; + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = (KMX_WCHAR)code; + tstr[(*mx)++] = (KMX_WCHAR)(i + 1); + tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1); + tstr[(*mx)] = 0; + return CERR_None; +} + +KMX_DWORD process_save(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx) +{ + /* save() */ + KMX_DWORD i; + for (i = 0; i < fk->cxStoreArray; i++) + { + if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break; + } + if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; + kmcmp::CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); + + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = CODE_SAVEOPT; + tstr[(*mx)++] = (KMX_WCHAR)(i + 1); + tstr[(*mx)] = 0; + return CERR_None; +} + +int xatoi(PKMX_WCHAR *p) +{ + PKMX_WCHAR endptr; + int n; + + switch (towupper(**p)) + { + case 'U': + (*p)++; + if (**p != '+') return 0; + (*p)++; + n = (int)u16tol(*p, &endptr, 16); + *p = endptr; + break; + case 'X': + (*p)++; + n = (int)u16tol(*p, &endptr, 16); + *p = endptr; + break; + case 'D': + (*p)++; + n = (int)u16tol(*p, &endptr, 10); + *p = endptr; + break; + default: + n = (int)u16tol(*p, &endptr, 8); + *p = endptr; + break; + } + return n; +} + +int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + PFILE_GROUP gp; + KMX_DWORD i; + + for (i = 0, gp = fk->dpGroupArray; i < fk->cxGroupArray; gp++, i++) + { + if (u16icmp(gp->szName, p) == 0) return i + 1; + } + return 0; +} + + +KMX_DWORD ProcessEthnologueStore(PKMX_WCHAR p) // I2646 +{ + KMX_DWORD res = CERR_None; + PKMX_WCHAR q = NULL; + while (*p) + { + while (u16chr(u" ,;", *p)) + { + if (*p != ' ') res = CWARN_PunctuationInEthnologueCode; + p++; + } + if (q == p) return CERR_InvalidEthnologueCode; + if (*p) + { + for (int i = 0; i < 3; i++) + { + if (!iswalpha(*p)) return CERR_InvalidEthnologueCode; + p++; + } + } + q = p; + } + return res; +} + +#define K_HOTKEYSHIFTFLAGS (K_SHIFTFLAG | K_CTRLFLAG | K_ALTFLAG | ISVIRTUALKEY) + +KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk) +{ + PKMX_WCHAR q, r; + KMX_DWORD sFlag; + int j, i; + + *hk = 0; + + if (*p == UC_SENTINEL && *(p + 1) == CODE_EXTENDED) { + KMX_WORD Key = *(p + 3); + KMX_WORD ShiftFlags = *(p + 2); + + // Convert virtual key to hotkey (different bitflags) + + if (ShiftFlags & ~K_HOTKEYSHIFTFLAGS) { + AddWarning(CWARN_HotkeyHasInvalidModifier); + } + + if (ShiftFlags & K_SHIFTFLAG) *hk |= HK_SHIFT; + if (ShiftFlags & K_CTRLFLAG) *hk |= HK_CTRL; + if (ShiftFlags & K_ALTFLAG) *hk |= HK_ALT; + + *hk |= Key; + + return CERR_None; + } + + q = (PKMX_WCHAR) u16chr(p, '['); + if (q) + { + q++; + sFlag = 0; + + do + { + while (iswspace(*q)) q++; + + if (u16nicmp(q, u"ALT", 3) == 0) sFlag |= HK_ALT, q += 3; + else if (u16nicmp(q, u"CTRL", 4) == 0) sFlag |= HK_CTRL, q += 4; + else if (u16nicmp(q, u"SHIFT", 5) == 0) sFlag |= HK_SHIFT, q += 5; + else if (towupper(*q) != 'K') return CERR_InvalidToken; + } while (towupper(*q) != 'K'); + + r = (PKMX_WCHAR) u16chr(q, ']'); + if (r) + { + r--; + while (iswspace(*r) && r > q) r--; + r++; + } + else return CERR_NoTokensFound; + + j = (int)(r - q); + + for (i = 0; i <= VK__MAX; i++) // I3438 + if (j == (int) u16len(VKeyNames[i]) && u16nicmp(q, VKeyNames[i], j) == 0) break; + + if (i == VK__MAX + 1) return CERR_InvalidToken; // I3438 + + *hk = i | sFlag; + + return CERR_None; + } + + q = GetDelimitedString(&p, u"\"\"", GDS_CUTLEAD | GDS_CUTFOLL); + if (q) + { + if (u16chr(q, '^')) *hk |= HK_CTRL; + if (u16chr(q, '+')) *hk |= HK_SHIFT; + if (u16chr(q, '%')) *hk |= HK_ALT; + q = (PKMX_WCHAR) u16chr(q, 0) - 1; + *hk |= *q; + return CERR_None; + } + + return CERR_CodeInvalidInThisSection; +} + + +void SetChecksum(PKMX_BYTE buf, PKMX_DWORD CheckSum, KMX_DWORD sz) +{ + BuildCRCTable(); + *CheckSum = (KMX_DWORD)CalculateBufferCRC(buf, sz); +} + + +KMX_BOOL kmcmp::CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall) +{ + PFILE_STORE sp = &fk->dpStoreArray[storeIndex]; + if (fIsStore && !sp->fIsStore) + { + if (sp->fIsDebug || sp->fIsOption || sp->fIsReserved || sp->fIsCall) + AddWarningBool(CWARN_StoreAlreadyUsedAsOptionOrCall); + sp->fIsStore = TRUE; + } + else if (fIsOption && !sp->fIsOption) + { + if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsCall) + AddWarningBool(CWARN_StoreAlreadyUsedAsStoreOrCall); + sp->fIsOption = TRUE; + } + else if (fIsCall && !sp->fIsCall) + { + if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsOption) + AddWarningBool(CWARN_StoreAlreadyUsedAsStoreOrOption); + sp->fIsCall = TRUE; + } + + return TRUE; +} + +KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize) +{ + PFILE_GROUP fgp; + PFILE_STORE fsp; + PFILE_KEY fkp; + + PCOMP_KEYBOARD ck; + PCOMP_GROUP gp; + PCOMP_STORE sp; + PCOMP_KEY kp; + PKMX_BYTE buf; + size_t offset; + size_t size; + KMX_DWORD i, j; + + *data = nullptr; + dataSize = 0; + + // Calculate how much memory to allocate + + size = sizeof(COMP_KEYBOARD) + + fk->cxGroupArray * sizeof(COMP_GROUP) + + fk->cxStoreArray * sizeof(COMP_STORE) + + /*wcslen(fk->szName)*2 + 2 + + wcslen(fk->szCopyright)*2 + 2 + + wcslen(fk->szLanguageName)*2 + 2 + + wcslen(fk->szMessage)*2 + 2 +*/ + fk->dwBitmapSize; + + for (i = 0, fgp = fk->dpGroupArray; i < fk->cxGroupArray; i++, fgp++) + { + if (kmcmp::FSaveDebug) size += u16len(fgp->szName) * 2 + 2; + size += fgp->cxKeyArray * sizeof(COMP_KEY); + for (j = 0, fkp = fgp->dpKeyArray; j < fgp->cxKeyArray; j++, fkp++) + { + size += u16len(fkp->dpOutput) * 2 + 2; + size += u16len(fkp->dpContext) * 2 + 2; + } + + if (fgp->dpMatch) size += u16len(fgp->dpMatch) * 2 + 2; + if (fgp->dpNoMatch) size += u16len(fgp->dpNoMatch) * 2 + 2; + } + + for (i = 0; i < fk->cxStoreArray; i++) + { + size += u16len(fk->dpStoreArray[i].dpString) * 2 + 2; + if (kmcmp::FSaveDebug || fk->dpStoreArray[i].fIsOption) size += u16len(fk->dpStoreArray[i].szName) * 2 + 2; + } + + buf = new KMX_BYTE[size]; + if (!buf) return CERR_CannotAllocateMemory; + memset(buf, 0, size); + + ck = (PCOMP_KEYBOARD)buf; + + ck->dwIdentifier = FILEID_COMPILED; + ck->dwFileVersion = fk->version; + ck->dwCheckSum = 0; // do checksum afterwards. + ck->KeyboardID = fk->KeyboardID; + ck->IsRegistered = TRUE; // I5135 + ck->cxStoreArray = fk->cxStoreArray; + ck->cxGroupArray = fk->cxGroupArray; + ck->StartGroup[0] = fk->StartGroup[0]; + ck->StartGroup[1] = fk->StartGroup[1]; + ck->dwHotKey = fk->dwHotKey; + + ck->dwFlags = fk->dwFlags; + + offset = sizeof(COMP_KEYBOARD); + + /*ck->dpLanguageName = offset; + wcscpy((PWSTR)(buf + offset), fk->szLanguageName); + offset += wcslen(fk->szLanguageName)*2 + 2; + + ck->dpName = offset; + wcscpy((PWSTR)(buf + offset), fk->szName); + offset += wcslen(fk->szName)*2 + 2; + + ck->dpCopyright = offset; + wcscpy((PWSTR)(buf + offset), fk->szCopyright); + offset += wcslen(fk->szCopyright)*2 + 2; + + ck->dpMessage = offset; + wcscpy((PWSTR)(buf + offset), fk->szMessage); + offset += wcslen(fk->szMessage)*2 + 2;*/ + + ck->dpStoreArray = (KMX_DWORD)offset; + sp = (PCOMP_STORE)(buf + offset); + fsp = fk->dpStoreArray; + offset += sizeof(COMP_STORE) * ck->cxStoreArray; + for (i = 0; i < ck->cxStoreArray; i++, sp++, fsp++) + { + sp->dwSystemID = fsp->dwSystemID; + sp->dpString = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fsp->dpString, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fsp->dpString) * 2 + 2; + + if (kmcmp::FSaveDebug || fsp->fIsOption) + { + sp->dpName = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fsp->szName, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fsp->szName) * 2 + 2; + } + else sp->dpName = 0; + } + + ck->dpGroupArray = (KMX_DWORD)offset; + gp = (PCOMP_GROUP)(buf + offset); + fgp = fk->dpGroupArray; + + offset += sizeof(COMP_GROUP) * ck->cxGroupArray; + + for (i = 0; i < ck->cxGroupArray; i++, gp++, fgp++) + { + gp->cxKeyArray = fgp->cxKeyArray; + gp->fUsingKeys = fgp->fUsingKeys; + + gp->dpMatch = gp->dpNoMatch = 0; + + if (fgp->dpMatch) + { + gp->dpMatch = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fgp->dpMatch, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fgp->dpMatch) * 2 + 2; + } + if (fgp->dpNoMatch) + { + gp->dpNoMatch = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fgp->dpNoMatch, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fgp->dpNoMatch) * 2 + 2; + } + + if (kmcmp::FSaveDebug) + { + gp->dpName = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fgp->szName, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fgp->szName) * 2 + 2; + } + else gp->dpName = 0; + + gp->dpKeyArray = (KMX_DWORD)offset; + kp = (PCOMP_KEY )(buf + offset); + fkp = fgp->dpKeyArray; + offset += gp->cxKeyArray * sizeof(COMP_KEY); + for (j = 0; j < gp->cxKeyArray; j++, kp++, fkp++) + { + kp->_reserved = 0; + kp->Key = fkp->Key; + if (kmcmp::FSaveDebug) kp->Line = fkp->Line; else kp->Line = 0; + kp->ShiftFlags = fkp->ShiftFlags; + kp->dpOutput = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fkp->dpOutput, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fkp->dpOutput) * 2 + 2; + kp->dpContext = (KMX_DWORD)offset; + u16ncpy((PKMX_WCHAR)(buf + offset), fkp->dpContext, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641 + offset += u16len(fkp->dpContext) * 2 + 2; + } + } + + ck->dwBitmapSize = fk->dwBitmapSize; + ck->dpBitmapOffset = (KMX_DWORD)offset; + memcpy(buf + offset, fk->lpBitmap, fk->dwBitmapSize); + offset += fk->dwBitmapSize; + + if (offset != size) { + delete[] buf; + return CERR_SomewhereIGotItWrong; + } + + SetChecksum(buf, &ck->dwCheckSum, (KMX_DWORD)size); + + *data = buf; + dataSize = size; + + return CERR_None; +} + +KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess) +{ + KMX_DWORD len; + PKMX_WCHAR p; + KMX_BOOL LineCarry = FALSE, InComment = FALSE; + KMX_DWORD n; + KMX_WCHAR currentQuotes = 0; + KMX_WCHAR str[LINESIZE + 3]; + len = (KMX_DWORD)fread( str , 1 ,LINESIZE * 2,fp_in); + if (ferror(fp_in) ) return CERR_CannotReadInfile; + len /= 2; + str[len] = 0; auto cur = ftell(fp_in); + fseek(fp_in, 0, SEEK_END); + auto fsize = ftell(fp_in); + fseek(fp_in, cur, SEEK_SET); + + if (cur == fsize) + + // \r\n is still added here even though Linux doesn`t use \r. + // This is to ensure to still have a working windows-only-version + u16ncat(str, u"\r\n", _countof(str)); // I3481 // Always a "\r\n" to the EOF, avoids funny bugs + + if (len == 0) return CERR_EndOfFile; + + // neccessary to add this block for using on non-windows platforms (removes all \r for platforms that use \n instead of \r\n) + for (p = str, n = 0; n < len; n++, p++) { + if (*p == L'\r') + *p = L' '; + } + + // \r is still left in this block even though Linux doesn`t use \r. + // This is to ensure to still have a working windows-only-version + for (p = str, n = 0; n < len; n++, p++) + { + if (currentQuotes != 0) + { + if (*p == L'\n') + { + *p = 0; // I2525 + u16ncpy(wstr, str, LINESIZE); // I3481 + return (PreProcess ? CERR_None : CERR_UnterminatedString); + } + if (*p == currentQuotes) currentQuotes = 0; + continue; + } + if (InComment) { + if (*p == L'\n') break; + *p = L' '; + continue; + } + if(*p == L'\\') { + LineCarry = TRUE; + *p = L' '; + continue; + } + if (LineCarry) + { + switch (*p) + { + case L' ': + case L'\t': + case L'\r': + *p = L' '; + continue; + case L'\n': + kmcmp::currentLine++; + LineCarry = FALSE; + *p = L' '; + continue; + } + *p = 0; // I2525 + u16ncpy(wstr, str, LINESIZE); // I3481 + return (PreProcess ? CERR_None : CERR_InvalidLineContinuation); + } + + if (*p == L'\n') break; + switch (*p) + { + case L'c': + case L'C': + if ((p == str || iswspace(*(p - 1))) && iswspace(*(p + 1))) { + InComment = TRUE; + *p = L' '; + } + continue; + case L'\r': + case L'\t': + *p = L' '; + continue; + case L'\'': + case L'\"': + currentQuotes = *p; + continue; + } + } + + if (n == len) + { + str[LINESIZE - 1] = 0; // I2525 + u16ncpy(wstr, str, LINESIZE); // I3481 + if (len == LINESIZE) + return (PreProcess ? CERR_None : CERR_LineTooLong); + } + + if (*p == L'\n') kmcmp::currentLine++; + + fseek(fp_in, -(int)(len * 2 - (int)(p - str) * 2 - 2), SEEK_CUR); + + p--; + while (p >= str && iswspace(*p)) p--; + p++; + *p++ = L'\n'; + *p = 0; + // trim spaces now, why not? + u16ncpy(wstr, str, LINESIZE); // I3481 + + return CERR_None; +} + +KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, int offset, int IsUnicode) +{ + PKMX_WCHAR q; + p = (const PKMX_WCHAR) u16chr(p, '>'); + + if (!p) return CERR_NoTokensFound; + + p++; + + return GetXString(fk, p, u"c\n", buf, bufsize, offset, &q, TRUE, IsUnicode); +} + +void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax) +{ + u16ncpy(out, in, cbMax - 1); // I3481 + out[cbMax - 1] = 0; +} + +KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token) +{ + PKMX_WCHAR q; + q = *p; + while (iswspace(*q)) q++; + if (u16nicmp(q, token, u16len(token)) == 0) + { + q += u16len(token); + while (iswspace(*q)) q++; + *p = q; + return TRUE; + } + return FALSE; +} + +KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf) +{ + FILE *fp; + KMX_WCHAR szNewName[260]; + + if (IsRelativePath(szName)) + { + PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir); + u16ncpy(szNewName, WCompileDir, _countof(szNewName)); // I3481 + u16ncat(szNewName,szName, _countof(szNewName )); // I3481 + } + else + u16ncpy(szNewName, szName, _countof(szNewName)); // I3481 + + fp=Open_File(szNewName, u"rb"); + + if ( fp == NULL) + { + // else if filename.bmp is not in the folder -> attempt to open filename.bmp.bmp ! + if ( u16cmp(szNewName+u16len(szNewName)-4, u".bmp") ) + u16ncat(szNewName, u".bmp", _countof(szNewName)); // I3481 + + fp= Open_File(szNewName, u"rb"); + + if ( fp == NULL) + return CERR_CannotReadBitmapFile; + } + + KMX_DWORD msg; + if ((msg = CheckFilenameConsistency(szNewName, FALSE)) != CERR_None) { + return msg; + } + + fseek(fp, 0, SEEK_END); + *FileSize = (KMX_DWORD)ftell(fp); + fseek(fp ,0,SEEK_SET); + if (*FileSize < 0) { + fclose(fp); + return CERR_CannotReadBitmapFile; + } + + if (*FileSize < 2) return CERR_CannotReadBitmapFile; + *Buf = new KMX_BYTE[*FileSize]; + + if (fread(*Buf, 1, *FileSize, fp) < (size_t) *FileSize) { + delete[] * Buf; + *Buf = NULL; + return CERR_CannotReadBitmapFile; + } + + fclose(fp); + + /* Test for version 7.0 icon support */ + if (*((PKMX_CHAR)*Buf) != 'B' && *(((PKMX_CHAR)*Buf) + 1) != 'M') { + VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); + } + + return CERR_None; +} + +int atoiW(PKMX_WCHAR p) +{ + PKMX_STR q = wstrtostr(p); + int i = atoi(q); + delete[] q; + return i; +} + +KMX_DWORD kmcmp::CheckUTF16(int n) +{ + const int res[] = { + 0xFDD0, 0xFDD1, 0xFDD2, 0xFDD3, 0xFDD4, 0xFDD5, 0xFDD6, 0xFDD7, + 0xFDD8, 0xFDD9, 0xFDDA, 0xFDDB, 0xFDDC, 0xFDDD, 0xFDDE, 0xFDDF, + 0xFDE0, 0xFDE1, 0xFDE2, 0xFDE3, 0xFDE4, 0xFDE5, 0xFDE6, 0xFDE7, + 0xFDE8, 0xFDE9, 0xFDEA, 0xFDEB, 0xFDEC, 0xFDED, 0xFDEE, 0xFDEF, + 0xFFFF, 0xFFFE, 0 }; + + if (n == 0) return CERR_ReservedCharacter; + for (int i = 0; res[i] > 0; i++) + if (n == res[i]) + { + AddWarning(CWARN_ReservedCharacter); + break; + } + return CERR_None; +} + +KMX_DWORD kmcmp::UTF32ToUTF16(int n, int *n1, int *n2) +{ + *n2 = -1; + if (n <= 0xFFFF) + { + *n1 = n; + if (n >= 0xD800 && n <= 0xDFFF) AddWarning(CWARN_UnicodeSurrogateUsed); + return kmcmp::CheckUTF16(*n1); + } + + if ((n & 0xFFFF) == 0xFFFF || (n & 0xFFFF) == 0xFFFE) AddWarning(CWARN_ReservedCharacter); + if (n < 0 || n > 0x10FFFF) return CERR_InvalidCharacter; + n = n - 0x10000; + *n1 = (n / 0x400) + 0xD800; + *n2 = (n % 0x400) + 0xDC00; + KMX_DWORD msg; + if ((msg = kmcmp::CheckUTF16(*n1)) != CERR_None) return msg; + return kmcmp::CheckUTF16(*n2); +} + +KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk) +{ + KMX_DWORD i; + size_t len = 0; + if (fk->cxVKDictionary == 0) return CERR_None; + for (i = 0; i < fk->cxVKDictionary; i++) + { + len += u16len(fk->dpVKDictionary[i].szName) + 1; + } + PKMX_WCHAR storeval = new KMX_WCHAR[len], p = storeval; + for (i = 0; i < fk->cxVKDictionary; i++) + { + u16ncpy(p, fk->dpVKDictionary[i].szName, len - (size_t)(p - storeval)); // I3481 + p = (PKMX_WCHAR) u16chr(p, 0); + *p = ' '; + p++; + } + + p--; + *p = 0; + + KMX_DWORD dwStoreID; + KMX_DWORD msg = AddStore(fk, TSS_VKDICTIONARY, storeval, &dwStoreID); + delete[] storeval; + return msg; +} + +int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + KMX_DWORD i; + + for (i = 0; i < fk->cxVKDictionary; i++) + if (u16icmp(fk->dpVKDictionary[i].szName, p) == 0) + return i + VK__MAX + 1; // 256 + + if (fk->cxVKDictionary % 10 == 0) + { + PFILE_VKDICTIONARY pvk = new FILE_VKDICTIONARY[fk->cxVKDictionary + 10]; + memcpy(pvk, fk->dpVKDictionary, fk->cxVKDictionary * sizeof(FILE_VKDICTIONARY)); + delete fk->dpVKDictionary; + fk->dpVKDictionary = pvk; + } + u16ncpy(fk->dpVKDictionary[fk->cxVKDictionary].szName, p, _countof(fk->dpVKDictionary[fk->cxVKDictionary].szName) ); // I3481 + fk->dpVKDictionary[fk->cxVKDictionary].szName[SZMAX_VKDICTIONARYNAME - 1] = 0; + + fk->cxVKDictionary++; + return fk->cxVKDictionary + VK__MAX; // 256-1 +} + +int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p) +{ + KMX_DWORD i; + + for (i = 0; i < fk->cxDeadKeyArray; i++) + if (u16icmp(fk->dpDeadKeyArray[i].szName, p) == 0) + return i + 1; + + if (fk->cxDeadKeyArray % 10 == 0) + { + PFILE_DEADKEY dk = new FILE_DEADKEY[fk->cxDeadKeyArray + 10]; + memcpy(dk, fk->dpDeadKeyArray, fk->cxDeadKeyArray * sizeof(FILE_DEADKEY)); + delete[] fk->dpDeadKeyArray; + fk->dpDeadKeyArray = dk; + } + u16ncpy(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName,p, _countof(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName)); // I3481 + fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName[SZMAX_DEADKEYNAME - 1] = 0; + + fk->cxDeadKeyArray++; + return fk->cxDeadKeyArray; +} + +void kmcmp::RecordDeadkeyNames(PFILE_KEYBOARD fk) +{ + KMX_WCHAR buf[SZMAX_DEADKEYNAME + 16]; + KMX_DWORD i; + for (i = 0; i < fk->cxDeadKeyArray; i++) + { + u16sprintf(buf, _countof(buf), L"%ls%d ", u16fmt(DEBUGSTORE_DEADKEY).c_str(), (int)i); + u16ncat(buf, fk->dpDeadKeyArray[i].szName, _countof(buf)); + + AddDebugStore(fk, buf); + } +} + +KMX_BOOL kmcmp::IsValidCallStore(PFILE_STORE fs) +{ + int i; + PKMX_WCHAR p; + for (i = 0, p = fs->dpString; *p; p++) + if (*p == ':') i++; + else if (!((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '.' || + *p == '_')) + return FALSE; + + return i == 1; +} + +FILE* CreateTempFile() +{ + return tmpfile(); +} + +/////////////////// + +FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble) +{ + FILE *fp_out = CreateTempFile(); + if(fp_out == NULL) // I3228 // I3510 + { + fclose(fp_in); + return NULL; //return INVALID_HANDLE_VALUE; _S2 can I exchange that? + } + + PKMX_BYTE buf, p; + PKMX_WCHAR outbuf, poutbuf; + KMX_DWORD len; + KMX_DWORD len2; + KMX_WCHAR prolog = 0xFEFF; + fwrite(&prolog,2, 1, fp_out); + + fseek(fp_in, 0, SEEK_END); + len = (KMX_DWORD)ftell(fp_in); + fseek(fp_in, 0, SEEK_SET); + if (hasPreamble) { + fseek( fp_in,3,SEEK_SET); // Cut off UTF-8 marker + len -= 3; + } + + buf = new KMX_BYTE[len + 1]; // null terminated + outbuf = new KMX_WCHAR[len + 1]; + + len2= (KMX_DWORD)fread(buf,1,len,fp_in); + if (len2) { + buf[len2] = 0; + p = buf; + poutbuf = outbuf; + if (hasPreamble) { + // We have a preamble, so we attempt to read as UTF-8 and allow conversion errors to be filtered. This is not great for a + // compiler but matches existing behaviour -- in future versions we may not do lenient conversion. + ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], lenientConversion); + fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out); + } + else { + // No preamble, so we attempt to read as strict UTF-8 and fall back to ANSI if that fails + ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], strictConversion); + if (cr == sourceIllegal) { + // Not a valid UTF-8 file, so fall back to ANSI + // AddCompileError(CHINT_NonUnicodeFile); + // note, while this message is defined, for now we will not emit it + // because we don't support HINT/INFO messages yet and we don't want + // this to cause a blocking compile at this stage + // do strtowstr only when no invalid characters are found + if( p==0){ + poutbuf = strtowstr((PKMX_STR)buf); + fwrite(poutbuf, (KMX_DWORD)u16len(poutbuf) * 2 , 1, fp_out); + delete[] poutbuf; + } + else + AddCompileError(CERR_InvalidCharacter); + } + + else { + fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out); + } + } + } + + fclose( fp_in); + delete[] buf; + delete[] outbuf; + fseek( fp_out,2,SEEK_SET); + return fp_out; +} + +PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID) +{ + assert(fk != NULL); + assert(dwSystemID != 0); + + PFILE_STORE sp = fk->dpStoreArray; + for (KMX_DWORD i = 0; i < fk->cxStoreArray; i++, sp++) { + if (sp->dwSystemID == dwSystemID) { + return sp; + } + } + return NULL; +} diff --git a/developer/src/kmcmplib/src/CompilerInterfaces.cpp b/developer/src/kmcmplib/src/CompilerInterfaces.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1609d35449562695c622e689649f962f6f6c57f --- /dev/null +++ b/developer/src/kmcmplib/src/CompilerInterfaces.cpp @@ -0,0 +1,453 @@ +#include "pch.h" + +#include +#include +#include "kmcmplib.h" +#include "filesystem.h" +#include "CheckFilenameConsistency.h" +#include "CheckNCapsConsistency.h" +#include "DeprecationChecks.h" +#include "versioning.h" +#include "../../../../common/windows/cpp/include/ConvertUTF.h" +#include "../../../../common/windows/cpp/include/keymanversion.h" + +bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk); +bool CompileKeyboard(const char* pszInfile, + void* pfkBuffer, bool ASaveDebug, bool ACompilerWarningsAsErrors, + bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, const void* AmsgprocContext, + int Target); + +EXTERN bool kmcmp_SetCompilerOptions(KMCMP_COMPILER_OPTIONS* options) { + //printf("°°-> changed to SetCompilerOptions() of kmcmplib \n"); + if(!options || options->dwSize < sizeof(KMCMP_COMPILER_OPTIONS)) { + return FALSE; + } + kmcmp::FShouldAddCompilerVersion = options->ShouldAddCompilerVersion; + return TRUE; +} + +#ifdef __EMSCRIPTEN__ + +/* + WASM interface for compiler message callback +*/ +EM_JS(int, wasm_msgproc, (int line, int msgcode, char* text, char* context), { + const proc = globalThis[UTF8ToString(context)]; + if(!proc || typeof proc != 'function') { + console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`); + return 0; + } else { + return proc(line, msgcode, UTF8ToString(text)); + } +}); + +int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, char* szText, void* context) { + char* msgProc = static_cast(context); + return wasm_msgproc(line, dwMsgCode, szText, msgProc); +} + +//DEPRECATED +EXTERN bool kmcmp_Wasm_SetCompilerOptions(int ShouldAddCompilerVersion) { + KMCMP_COMPILER_OPTIONS options; + options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); + options.ShouldAddCompilerVersion = ShouldAddCompilerVersion; + return kmcmp_SetCompilerOptions(&options); +} + +//DEPRECATED +EXTERN bool kmcmp_Wasm_CompileKeyboardFile(char* pszInfile, + char* pszOutfile, int ASaveDebug, int ACompilerWarningsAsErrors, + int AWarnDeprecatedCode, char* msgProc +) { + return kmcmp_CompileKeyboardFile( + pszInfile, + pszOutfile, + ASaveDebug, + ACompilerWarningsAsErrors, + AWarnDeprecatedCode, + wasm_CompilerMessageProc, + msgProc + ); +} + +EXTERN int kmcmp_Wasm_ParseUnicodeSet(char* pat, + uint32_t* buf, int length +) { + return kmcmp_ParseUnicodeSet( + pat, buf, length + ); +} + + +struct COMPILER_INTERFACE { + bool saveDebug; + bool compilerWarningsAsErrors; + bool warnDeprecatedCode; + bool shouldAddCompilerVersion; + int target; // CKF_KEYMAN, CKF_KEYMANWEB + std::string messageCallback; // int line, uint32_t dwMsgCode, char* szText + std::string loadFileCallback; // char* infile, char* filenameRelativeToInfile --> buffer +}; + +struct COMPILER_RESULT { + bool result; + // Following are pointer offsets in heap + buffer size + int kmx; + int kmxSize; + // Following are compiler side-channel data, required for + // follow-on transform + std::string kvksFilename; + // TODO: additional data to be passed back +}; + +COMPILER_RESULT kmcmp_compile(std::string pszInfile, const COMPILER_INTERFACE intf) { + COMPILER_RESULT r = {false}; + + FILE_KEYBOARD fk; + + // TODO: this should be included in CompileKeyboard? + kmcmp::FShouldAddCompilerVersion = intf.shouldAddCompilerVersion; + + r.result = CompileKeyboard( + pszInfile.c_str(), + &fk, + intf.saveDebug, + intf.compilerWarningsAsErrors, + intf.warnDeprecatedCode, + wasm_CompilerMessageProc, + intf.messageCallback.c_str(), + intf.target); + + if(!r.result) { + return r; + } + + KMX_DWORD msg; + KMX_BYTE* data = nullptr; + size_t dataSize = 0; + msg = WriteCompiledKeyboard(&fk, &data, dataSize); + //TODO: FreeKeyboardPointers(fk); + + if(msg != CERR_None) { + AddCompileError(msg); + r.result = FALSE; + return r; + } + + r.kmx = (int) data; + r.kmxSize = (int) dataSize; + r.kvksFilename = string_from_u16string(fk.extra->kvksFilename); // convert to UTF8 + + return r; +} + +EMSCRIPTEN_BINDINGS(compiler_interface) { + emscripten::class_("CompilerInterface") + .constructor<>() + .property("saveDebug", &COMPILER_INTERFACE::saveDebug) + .property("compilerWarningsAsErrors", &COMPILER_INTERFACE::compilerWarningsAsErrors) + .property("warnDeprecatedCode", &COMPILER_INTERFACE::warnDeprecatedCode) + .property("shouldAddCompilerVersion", &COMPILER_INTERFACE::shouldAddCompilerVersion) + .property("target", &COMPILER_INTERFACE::target) + .property("messageCallback", &COMPILER_INTERFACE::messageCallback) + .property("loadFileCallback", &COMPILER_INTERFACE::loadFileCallback) + ; + + emscripten::class_("CompilerResult") + .constructor<>() + .property("result", &COMPILER_RESULT::result) + .property("kmx", &COMPILER_RESULT::kmx) + .property("kmxSize", &COMPILER_RESULT::kmxSize) + .property("kvksFilename", &COMPILER_RESULT::kvksFilename) + ; + + emscripten::function("kmcmp_compile", &kmcmp_compile); +} + +#endif + +bool CompileKeyboard(const char* pszInfile, + void* pfkBuffer, bool ASaveDebug, bool ACompilerWarningsAsErrors, + bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, const void* AmsgprocContext, + int Target) { + + FILE* fp_in = NULL; + KMX_CHAR str[260]; + + kmcmp::FSaveDebug = ASaveDebug; // I3681 + kmcmp::FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 + AWarnDeprecatedCode_GLOBAL_LIB = AWarnDeprecatedCode; + + kmcmp::CompileTarget = Target; + + if (!pMsgproc || !pszInfile || !pfkBuffer) { + AddCompileError(CERR_BadCallParams); + return FALSE; + } + + PKMX_STR p; + + if ((p = strrchr_slash((char*)pszInfile)) != nullptr) + { + strncpy(kmcmp::CompileDir, pszInfile, (int)(p - pszInfile + 1)); // I3481 + kmcmp::CompileDir[(int)(p - pszInfile + 1)] = 0; + } + else + kmcmp::CompileDir[0] = 0; + + msgproc = pMsgproc; + msgprocContext = (void*)AmsgprocContext; + kmcmp::currentLine = 0; + kmcmp::nErrors = 0; + + fp_in = Open_File(pszInfile, "rb"); + + if (fp_in == NULL) { + AddCompileError(CERR_InfileNotExist); + return FALSE; + } + + // Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16? + // What about really large files? Transfer to a temp file... + if (!fread(str, 1, 3, fp_in)) { + fclose(fp_in); + AddCompileError(CERR_CannotReadInfile); + return FALSE; + } + + fseek(fp_in, 0, SEEK_SET); + if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2]) + fp_in = UTF16TempFromUTF8(fp_in, TRUE); + else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1]) + fseek(fp_in, 2, SEEK_SET); + else + fp_in = UTF16TempFromUTF8(fp_in, FALSE); + if (fp_in == NULL) { + AddCompileError(CERR_CannotCreateTempfile); + return FALSE; + } + + kmcmp::CodeConstants = new kmcmp::NamedCodeConstants; + bool result = CompileKeyboardHandle(fp_in, static_cast(pfkBuffer)); + delete kmcmp::CodeConstants; + + fclose(fp_in); + + if (kmcmp::nErrors > 0) { + return FALSE; + } + + return result; +} + +EXTERN bool kmcmp_CompileKeyboardFileToBuffer(char* pszInfile, void* pfkBuffer, bool ACompilerWarningsAsErrors, bool AWarnDeprecatedCode, + kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext, int Target) // I4865 // I4866 +{ + if (!pMsgproc || !pszInfile || !pfkBuffer) { + AddCompileError(CERR_BadCallParams); + return FALSE; + } + + return CompileKeyboard(pszInfile, pfkBuffer, TRUE, ACompilerWarningsAsErrors, AWarnDeprecatedCode, + pMsgproc, AmsgprocContext, Target); +} + +EXTERN bool kmcmp_CompileKeyboardFile(char* pszInfile, + char* pszOutfile, bool ASaveDebug, bool ACompilerWarningsAsErrors, + bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext) // I4865 // I4866 +{ + if (!pMsgproc || !pszInfile || !pszOutfile) { + AddCompileError(CERR_BadCallParams); + return FALSE; + } + + FILE_KEYBOARD fk; + if(!CompileKeyboard(pszInfile, &fk, ASaveDebug, ACompilerWarningsAsErrors, AWarnDeprecatedCode, + pMsgproc, AmsgprocContext, CKF_KEYMAN)) { + // any errors will have been reported directly by CompileKeyboard + return FALSE; + } + + FILE* fp_out = Open_File(pszOutfile, "wb"); + if (fp_out == NULL) { + AddCompileError(CERR_CannotCreateOutfile); + return FALSE; + } + + KMX_DWORD msg; + KMX_BYTE* data = nullptr; + size_t dataSize = 0; + if ((msg = WriteCompiledKeyboard(&fk, &data, dataSize)) != CERR_None) { + AddCompileError(msg); + } else { + if(fwrite(data, 1, dataSize, fp_out) != dataSize) { + AddCompileError(CERR_UnableToWriteFully); + } + delete[] data; + } + + fclose(fp_out); + + if (kmcmp::nErrors > 0) { + remove(pszOutfile); + return FALSE; + } + + return TRUE; +} + +bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk) +{ + PKMX_WCHAR str, p; + + KMX_DWORD msg; + + kmcmp::FMnemonicLayout = FALSE; + + if (!fk) { + AddCompileError(CERR_SomewhereIGotItWrong); + return FALSE; + } + + str = new KMX_WCHAR[LINESIZE]; + if (!str) { + AddCompileError(CERR_CannotAllocateMemory); + return FALSE; + } + + fk->KeyboardID = 0; + fk->version = 0; + fk->dpStoreArray = NULL; + fk->dpGroupArray = NULL; + fk->cxStoreArray = 0; + fk->cxGroupArray = 0; + fk->StartGroup[0] = fk->StartGroup[1] = -1; + fk->szName[0] = 0; + fk->szCopyright[0] = 0; + fk->dwFlags = KF_AUTOMATICVERSION; + fk->currentGroup = 0xFFFFFFFF; + fk->currentStore = 0; + fk->cxDeadKeyArray = 0; + fk->dpDeadKeyArray = NULL; + fk->cxVKDictionary = 0; // I3438 + fk->dpVKDictionary = NULL; // I3438 + fk->extra = new FILE_KEYBOARD_EXTRA; + fk->extra->kvksFilename = u""; +/* fk->szMessage[0] = 0; + fk->szLanguageName[0] = 0;*/ + fk->dwBitmapSize = 0; + fk->dwHotKey = 0; + + kmcmp::BeginLine[BEGIN_ANSI] = -1; + kmcmp::BeginLine[BEGIN_UNICODE] = -1; + kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1; + kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1; + + + /* Add a store for the Keyman 6.0 copyright information string */ + + if(kmcmp::FShouldAddCompilerVersion) { + u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0); + AddStore(fk, TSS_KEYMANCOPYRIGHT, str); + } + + /* Add a system store for the Keyman edition number */ + AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0"); + AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman"); + + // must preprocess for group and store names -> this isn't really necessary, but never mind! + while ((msg = ReadLine(fp_in, str, TRUE)) == CERR_None) + { + p = str; + switch (LineTokenType(&p)) + { + case T_VERSION: + *(p + 4) = 0; + if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + case T_GROUP: + if ((msg = ProcessGroupLine(fk, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + case T_STORE: + if ((msg = ProcessStoreLine(fk, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + default: + break; + } + } + + if (msg != CERR_EndOfFile) { + AddCompileError(msg); + return FALSE; + } + + fseek( fp_in,2,SEEK_SET); + kmcmp::currentLine = 0; + + /* Reindex the list of codeconstants after stores added */ + + kmcmp::CodeConstants->reindex(); + + /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */ + while ((msg = ReadLine(fp_in, str, FALSE)) == CERR_None) + { + msg = ParseLine(fk, str); + if (msg != CERR_None) { + AddCompileError(msg); + return FALSE; + } + } + + if (msg != CERR_EndOfFile) { + AddCompileError(msg); + return FALSE; + } + + ProcessGroupFinish(fk); + + if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk); + + /* Add the compiler version as a system store */ + if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + + if ((msg = BuildVKDictionary(fk)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + + if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + + delete str; + + if (!kmcmp::CheckKeyboardFinalVersion(fk)) { + return FALSE; + } + + /* Warn on inconsistent use of NCAPS */ + if (!kmcmp::FMnemonicLayout) { + CheckNCapsConsistency(fk); + } + + /* Flag presence of deprecated features */ + kmcmp::CheckForDeprecatedFeatures(fk); + + return TRUE; +} diff --git a/developer/src/kmcmplib/src/DeprecationChecks.cpp b/developer/src/kmcmplib/src/DeprecationChecks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11042f0c132b69bcb02b0e7a30e481dd4e0d29a8 --- /dev/null +++ b/developer/src/kmcmplib/src/DeprecationChecks.cpp @@ -0,0 +1,53 @@ + +#include "pch.h" + +#include "compfile.h" +#include +#include "kmcmplib.h" +#include "DeprecationChecks.h" + +KMX_BOOL kmcmp::WarnDeprecatedHeader() { // I4866 +if( AWarnDeprecatedCode_GLOBAL_LIB){ + AddWarningBool(CWARN_HeaderStatementIsDeprecated); + } + return TRUE; +} + +/* Flag presence of deprecated features */ +KMX_BOOL kmcmp::CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) { + /* + For Keyman 10, we deprecated: + // < Keyman 7 + #define TSS_LANGUAGE 4 + #define TSS_LAYOUT 5 + #define TSS_LANGUAGENAME 12 + #define TSS_ETHNOLOGUECODE 15 + + // Keyman 7 + #define TSS_WINDOWSLANGUAGES 29 + */ + int oldCurrentLine = kmcmp::currentLine; + KMX_DWORD i; + PFILE_STORE sp; + + if (!AWarnDeprecatedCode_GLOBAL_LIB) { + return TRUE; + } + + if (fk->version >= VERSION_100) { + for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) { + if (sp->dwSystemID == TSS_LANGUAGE || + sp->dwSystemID == TSS_LAYOUT || + sp->dwSystemID == TSS_LANGUAGENAME || + sp->dwSystemID == TSS_ETHNOLOGUECODE || + sp->dwSystemID == TSS_WINDOWSLANGUAGES) { + kmcmp::currentLine = sp->line; + AddWarningBool(CWARN_LanguageHeadersDeprecatedInKeyman10); + } + } + } + + kmcmp::currentLine = oldCurrentLine; + + return TRUE; +} diff --git a/developer/src/kmcmplib/src/DeprecationChecks.h b/developer/src/kmcmplib/src/DeprecationChecks.h new file mode 100644 index 0000000000000000000000000000000000000000..941b87f1a4d5dc367892d7ee8209d27a5fca88b6 --- /dev/null +++ b/developer/src/kmcmplib/src/DeprecationChecks.h @@ -0,0 +1,9 @@ +#pragma once + +#include "compfile.h" +#include "kmcmplib.h" + +namespace kmcmp{ +KMX_BOOL WarnDeprecatedHeader(); +KMX_BOOL CheckForDeprecatedFeatures(PFILE_KEYBOARD fk); +} diff --git a/developer/src/kmcmplib/src/Edition.cpp b/developer/src/kmcmplib/src/Edition.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cdf82ee2f8f3c0da311c853f891b70096089e23e --- /dev/null +++ b/developer/src/kmcmplib/src/Edition.cpp @@ -0,0 +1,8 @@ + +#include "pch.h" +#include "Edition.h" + +int GetEdition() +{ + return ED_STANDARD; +} diff --git a/developer/src/kmcmplib/src/Edition.h b/developer/src/kmcmplib/src/Edition.h new file mode 100644 index 0000000000000000000000000000000000000000..26eb44c61609a1b7352f45b13f178a821ce5c424 --- /dev/null +++ b/developer/src/kmcmplib/src/Edition.h @@ -0,0 +1,12 @@ + +#ifndef _EDITION_H +#define _EDITION_H + +int GetEdition(); + +#define ED_DEMO 0 +#define ED_STANDARD 1 +#define ED_PRO 2 +#define ED_OEM 3 + +#endif diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.cpp b/developer/src/kmcmplib/src/NamedCodeConstants.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6b1ae6d70ce20846ba23d24e0ab0efd05d2120a --- /dev/null +++ b/developer/src/kmcmplib/src/NamedCodeConstants.cpp @@ -0,0 +1,355 @@ +/*2012 + Name: NamedCodeConstants + Copyright: Copyright (C) 2003-2017 SIL International. + Documentation: + Description: + Create Date: 19 Jul 2011 + + Modified Date: 13 Dec 2012 + Authors: mcdurdin + Related Files: + Dependencies: + + Bugs: + Todo: + Notes: + History: 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear + 24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++ + 06 Feb 2012 - mcdurdin - I3056 - If file was not found in first search folder, named code constants failed to compile + 06 Feb 2012 - mcdurdin - I3056 - UTF-8 support for named code constants file + 03 Nov 2012 - mcdurdin - I3512 - V9.0 - Merge of I3056 - If file was not found in first search folder, named code constants failed to compile + 13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs +*/ + +#include "pch.h" +#include +#include "NamedCodeConstants.h" +#include "CheckFilenameConsistency.h" +#include +#include "kmcompx.h" +#include "filesystem.h" + +using namespace kmcmp; + +int IsHangulSyllable(const KMX_WCHAR *codename, int *code); + +namespace kmcmp { + extern KMX_CHAR CompileDir[]; + + int __cdecl sort_entries(const void *elem1, const void *elem2) + { + return u16icmp( + ((NCCENTRY *)elem1)->name, + ((NCCENTRY *)elem2)->name); + } +} + +NamedCodeConstants::NamedCodeConstants() +{ + nEntries = 0; + entries = NULL; + nEntries_file = 0; + entries_file = NULL; + reindex(); +} + +NamedCodeConstants::~NamedCodeConstants() +{ + if(entries) delete entries; + if(entries_file) delete entries_file; +} + +void NamedCodeConstants::AddCode(int n, const KMX_WCHAR *p, KMX_DWORD storeIndex) +{ + if((nEntries_file % ALLOC_SIZE) == 0) + { + NCCENTRY *bn = new NCCENTRY[nEntries_file + ALLOC_SIZE]; + if(nEntries_file > 0) + { + memcpy(bn, entries_file, sizeof(NCCENTRY) * nEntries_file); + delete entries_file; + } + entries_file = bn; + } + + entries_file[nEntries_file].code = n; + u16ncpy(entries_file[nEntries_file].name, p, _countof(entries_file[nEntries_file].name)); // I3481 + entries_file[nEntries_file].name[MAX_ENAME] = 0; + + for (KMX_WCHAR *r = entries_file[nEntries_file].name; *r; r++) + if (iswblank(*r) && *r != '-') *r = '_'; + + entries_file[nEntries_file].storeIndex = storeIndex; + nEntries_file++; +} + +void NamedCodeConstants::AddCode_IncludedCodes(int n, const KMX_WCHAR *p) +{ + if((nEntries % ALLOC_SIZE) == 0) + { + NCCENTRY *bn = new NCCENTRY[nEntries + ALLOC_SIZE]; + if(nEntries > 0) + { + memcpy(bn, entries, sizeof(NCCENTRY) * nEntries); + delete entries; + } + entries = bn; + } + + entries[nEntries].code = n; + u16ncpy(entries[nEntries].name, p, MAX_ENAME); // I3481 + entries[nEntries].name[MAX_ENAME] = 0; + for (KMX_WCHAR *r = entries[nEntries].name; *r; r++) + if (iswblank(*r)) *r = '_'; + + entries[nEntries].storeIndex = 0xFFFFFFFFL; + nEntries++; +} + +// int __cdecl sort_entries(const void *elem1, const void *elem2) moved into namespace further up + +char *kmc_strupr(char *s) { + char *p = s; + while (*p) { + *p = ::toupper(*p); + p++; + } + return s; +} + +KMX_BOOL NamedCodeConstants::IntLoadFile(const KMX_CHAR *filename) +{ + const int str_size = 256; + FILE *fp = NULL; + + if (CheckFilenameConsistency(filename, FALSE) != 0) { + return FALSE; + } + + fp = Open_File(filename, "rt"); + if(fp == NULL) { + return FALSE; // I3481 + } + + KMX_CHAR str[str_size], *p, *q, *context = NULL; + KMX_BOOL isEol , first = TRUE; + + while(fgets(str, str_size, fp)) + { + isEol = *(strchr(str, 0) - 1) == '\n'; + p = strtok_r(str, ";", &context); // I3481 + q = strtok_r(NULL, ";\n", &context); + if(p && q) + { + if(first && *p == (KMX_CHAR)0xEF && *(p+1) == (KMX_CHAR)0xBB && *(p+2) == (KMX_CHAR)0xBF) p += 3; // I3056 UTF-8 // I3512 + first = FALSE; + kmc_strupr(q); // I3481 // I3641 + long n = strtol(p, NULL, 16); + if (*q != '<') { + PKMX_WCHAR q0 = strtowstr(q); + AddCode_IncludedCodes((int)n, q0); + delete[] q0; + } + } + if(!isEol ) + { + while(fgets(str, str_size, fp)) if(*(strchr(str, 0)-1) == '\n') break; + } + } + + fclose(fp); + + return TRUE; +} + +KMX_BOOL NamedCodeConstants::LoadFile(const KMX_CHAR *filename) +{ + const int buf_size = 260; + KMX_CHAR buf[buf_size]; + // Look in current directory first -- REMOVED AS DANGEROUS + /* strncpy(buf, filename, (buf_size-1)); buf[buf_size-1] = 0; // I3481 + if(kmcmp_FileExists(buf)) + return IntLoadFile(buf); + */ + // Then look in keyboard file directory (CompileDir) + strncpy(buf, CompileDir, (buf_size-1)); buf[buf_size-1] = 0; // I3481 + strncat(buf, filename, (buf_size-1)-strlen(CompileDir)); buf[buf_size-1] = 0; + if(kmcmp_FileExists(buf)) + return IntLoadFile(buf); + + //TODO: sort out how to find common includes in non-Windows platforms: + #ifdef _WINDOWS_ + // Finally look in kmcmpdll.dll directory + GetModuleFileName(0, buf, buf_size); + + KMX_CHAR *p = strrchr_slash(buf); + if(p) + p++; + else + p = buf; + *p = 0; + strncat_s(buf, _countof(buf), filename, (buf_size-1)-strlen(buf)); buf[buf_size-1] = 0; // I3481 // I3641 + if(kmcmp_FileExists(buf)) + return IntLoadFile(buf); + #endif + + reindex(); + + return FALSE; +} + +void NamedCodeConstants::reindex() +{ + if (entries != NULL) { + qsort(entries, nEntries, sizeof(NCCENTRY), sort_entries); + } + + KMX_WCHAR c = u'.', d; + int i; + + for(i = 0; i < 128; i++) chrindexes[i] = -1; + + if (entries != NULL) { + for (i = 0; i < nEntries; i++) + { + d = towupper(entries[i].name[0]); + if (d != c && d >= 32 && d <= 127) + chrindexes[c = d] = i; + } + } +} + +int NamedCodeConstants::GetCode(const KMX_WCHAR *codename, KMX_DWORD *storeIndex) +{ + *storeIndex = 0xFFFFFFFFL; // I2993 + int code = GetCode_IncludedCodes(codename); + if(code) return code; + for(int i = 0; i < nEntries_file; i++) { + if(!u16icmp(entries_file[i].name, codename)) { + *storeIndex = entries_file[i].storeIndex; + return entries_file[i].code; + } + } + return 0; +} + +int NamedCodeConstants::GetCode_IncludedCodes(const KMX_WCHAR *codename) +{ + KMX_WCHAR c = towupper(*codename); + int code; + + if(IsHangulSyllable(codename, &code)) return code; + + if(c < 32 || c > 127 || chrindexes[c] < 0) return 0; + for(int n = chrindexes[c]; n < nEntries && towupper(entries[n].name[0]) == c; n++) + { + int cmp = u16icmp(codename, entries[n].name); + if(cmp == 0) return entries[n].code; + if(cmp < 0) break; + } + return 0; +} + +/* + + Hangul Syllables + +*/ + +const int + HangulSBase = 0xAC00, + //HangulLBase = 0x1100, + //HangulVBase = 0x1161, + //HangulTBase = 0x11A7, + HangulLCount = 19, + HangulVCount = 21, + HangulTCount = 28; + //HangulNCount = HangulVCount * HangulTCount; // 588 + //HangulSCount = HangulLCount * HangulNCount; // 11172 + +const KMX_WCHAR * + Hangul_JAMO_L_TABLE[] = { + u"G", u"GG", u"N", u"D", u"DD", u"R", u"M", u"B", u"BB", + u"S", u"SS", u"", u"J", u"JJ", u"C", u"K", u"T", u"P", u"H" }; + +const KMX_WCHAR * + Hangul_JAMO_V_TABLE[] = { + u"A", u"AE", u"YA", u"YAE", u"EO", u"E", u"YEO", u"YE", u"O", + u"WA", u"WAE", u"OE", u"YO", u"U", u"WEO", u"WE", u"WI", + u"YU", u"EU", u"YI", u"I" }; + + +const KMX_WCHAR * + Hangul_JAMO_T_TABLE[] = { + u"", u"G", u"GG", u"GS", u"N", u"NJ", u"NH", u"D", u"u", u"LG", u"LM", + u"LB", u"LS", u"LT", u"LP", u"LH", u"M", u"B", u"BS", + u"S", u"SS", u"NG", u"J", u"C", u"K", u"T", u"P", u"H" }; + + +int IsHangulSyllable(const KMX_WCHAR *codename, int *code) +{ + if(u16nicmp(codename, u"HANGUL_SYLLABLE_", 16)) return 0; + codename += 16; + if(!*codename) return 0; + + int i, LIndex, VIndex, TIndex; + + /* Find initial */ + + int ch = towupper(*codename); + if(strchr("GNDRMBSJCKTPH", ch)) + { + /* Has an initial syllable */ + int isDoubled = towupper(*(codename+1)) == ch; + + LIndex = -1; + for(i = 0; i < HangulLCount; i++) { + if(Hangul_JAMO_L_TABLE[i][0] == ch && (!isDoubled || (Hangul_JAMO_L_TABLE[i][1] == ch && isDoubled))) { + LIndex = i; + break; + } + } + if(LIndex == -1) return 0; + codename++; + if(isDoubled) codename++; + } + else LIndex = 11; /* no initial */ + + /* Find vowel */ + + KMX_WCHAR V[4] = u""; + V[0] = *codename; + if(V[0] && strchr("AEIOUWY", towupper(*(codename+1)))) V[1] = *(codename+1); + if(V[1] && strchr("AEIOUWY", towupper(*(codename+2)))) V[2] = *(codename+2); + + VIndex = -1; + for(i = 0; i < HangulVCount; i++) { + if(!u16icmp(Hangul_JAMO_V_TABLE[i], V)) { + VIndex = i; + break; + } + } + + if(VIndex == -1) return 0; + + codename += u16len(V); + + /* Find final */ + + TIndex = -1; + + for(i = 0; i < HangulTCount; i++) { + if(!u16icmp(Hangul_JAMO_T_TABLE[i], codename)) { + TIndex = i; + break; + } + } + + if(TIndex == -1) return 0; + + /* Composition */ + + *code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex; + + return 1; +} diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.h b/developer/src/kmcmplib/src/NamedCodeConstants.h new file mode 100644 index 0000000000000000000000000000000000000000..83d707d092cc4535a24cf3676aaa3d2a44d7b859 --- /dev/null +++ b/developer/src/kmcmplib/src/NamedCodeConstants.h @@ -0,0 +1,37 @@ + +#ifndef NAMEDCODECONSTANTS_H +#define NAMEDCODECONSTANTS_H + +#define MAX_ENAME 128 +#define ALLOC_SIZE 256 + +struct NCCENTRY +{ + KMX_WCHAR name[MAX_ENAME+1]; + int code; + KMX_DWORD storeIndex; +}; + +namespace kmcmp{ + class NamedCodeConstants + { + private: + NCCENTRY *entries; // entries from &includecodes + NCCENTRY *entries_file; // entries from store(myconst) x + int nEntries, nEntries_file; + int chrindexes[128]; // A-Z, 0-9, -, _; simple index + + int GetCode_IncludedCodes(const KMX_WCHAR *codename); + void AddCode_IncludedCodes(int n, const KMX_WCHAR *p); + KMX_BOOL IntLoadFile(const KMX_CHAR *filename); + public: + NamedCodeConstants(); + ~NamedCodeConstants(); + + void reindex(); + void AddCode(int n, const KMX_WCHAR *p, KMX_DWORD storeIndex); + KMX_BOOL LoadFile(const KMX_CHAR *filename); + int GetCode(const KMX_WCHAR *codename, KMX_DWORD *storeIndex); + }; +} +#endif //NAMEDCODECONSTANTS_H diff --git a/developer/src/kmcmplib/src/UnreachableRules.cpp b/developer/src/kmcmplib/src/UnreachableRules.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c6aaa23adb32e2e7162794bda48f19d9d182ee8 --- /dev/null +++ b/developer/src/kmcmplib/src/UnreachableRules.cpp @@ -0,0 +1,57 @@ + +#include "pch.h" + +#include "compfile.h" +#include +#include "../../../../common/windows/cpp/include/vkeys.h" +#include "kmcmplib.h" + +#include +#include +#include +#include + +#include "UnreachableRules.h" + +namespace kmcmp { + std::wstring MakeHashKeyFromFileKey(PFILE_KEY kp) { + std::wstringstream key; + key << kp->Key << "," << kp->ShiftFlags << ","; + if (kp->dpContext) { + std::wstring Context_ws = u16fmt((const PKMX_WCHAR) kp->dpContext); + key << Context_ws; + } + return key.str(); + } + +} +KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp) { + PFILE_KEY kp = gp->dpKeyArray; + KMX_DWORD i; + + int oldCurrentLine = kmcmp::currentLine; + + std::unordered_map map; + std::unordered_set reportedLines; + + for (i = 0; i < gp->cxKeyArray; i++, kp++) { + std::wstring key = kmcmp::MakeHashKeyFromFileKey(kp); + if (map.count(key) > 0) { + FILE_KEY const & k1 = map.at(key); + if (kp->Line != k1.Line && reportedLines.count(kp->Line) == 0) { + reportedLines.insert(kp->Line); + kmcmp::currentLine = kp->Line; + u16sprintf(ErrExtraW, ERR_EXTRA_W_LEN, L" Overridden by rule on line %d", k1.Line); + strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + AddWarning(CHINT_UnreachableRule); + } + } + else { + map.insert({ key, *kp }); + } + } + + kmcmp::currentLine = oldCurrentLine; + + return CERR_None; +} diff --git a/developer/src/kmcmplib/src/UnreachableRules.h b/developer/src/kmcmplib/src/UnreachableRules.h new file mode 100644 index 0000000000000000000000000000000000000000..d9eac65ce3cbec70fa2a88795db96fd28e0e6bf0 --- /dev/null +++ b/developer/src/kmcmplib/src/UnreachableRules.h @@ -0,0 +1,3 @@ +#pragma once + +KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp); diff --git a/developer/src/kmcmplib/src/compfile.h b/developer/src/kmcmplib/src/compfile.h new file mode 100644 index 0000000000000000000000000000000000000000..595d1fececb2cf8b01b25e49a026fda1c7d103ac --- /dev/null +++ b/developer/src/kmcmplib/src/compfile.h @@ -0,0 +1,229 @@ +/* + Name: Compfile + Copyright: Copyright (C) SIL International. + Documentation: + Description: + Create Date: 25 Jan 2007 + + Modified Date: 25 May 2010 + Authors: mcdurdin + Related Files: + Dependencies: + + Bugs: + Todo: + Notes: + History: 25 Jan 2007 - mcdurdin - Add GLOBAL_BUFSIZE, enlarge LINESIZE + 25 May 2010 - mcdurdin - I1632 - Keyboard Options +*/ + +#ifndef _COMPFILE_H +#define _COMPFILE_H + +#include "kmcompx.h" +#include +#include + +#define LINESIZE 8192 +#define GLOBAL_BUFSIZE 4096 + +#define SZMAX_STORENAME 80 +#define SZMAX_GROUPNAME 80 +#define SZMAX_DEADKEYNAME 80 +#define SZMAX_ERRORTEXT 512 +#define SZMAX_VKDICTIONARYNAME 80 + +#define MAX_WARNINGS 100 + +#define T_COMMENT 1 // A comment line +#define T_KEYTOKEY 2 // A rule line +#define T_BLANK 3 // A blank line + +#define T_W_START 4 // Start of easily matched line types +#define T_STORE 4 // A store line +#define T_VERSION 5 // A 'VERSION 3.2/3.1/3.0' line +#define T_NAME 6 // A 'NAME "xxxx"' line +#define T_BITMAP 7 // A 'BITMAP bmpfile' line +#define T_HOTKEY 8 // A 'HOTKEY "^%+C"' line +#define T_BEGIN 9 // A 'begin > use(xxxx)' line +#define T_GROUP 10 // A 'group(xxxx)' line +#define T_MATCH 11 // A 'match > "XXXX"' line +#define T_NOMATCH 12 // A 'nomatch > "XXXX"' line +#define T_SHIFT 13 // A 'SHIFT FREES CAPS' line +#define T_CAPSON 14 // A 'CAPS ON ONLY' line +#define T_CAPSOFF 15 // A 'CAPS ALWAYS OFF' line +#define T_LANGUAGE 16 // A 'LANGUAGE xxxh, xxh' line +#define T_LAYOUT 17 // A 'LAYOUT xxxxh' line +#define T_COPYRIGHT 18 // A 'COPYRIGHT "so and so"' line +#define T_MESSAGE 19 // A 'MESSAGE "so and so"' line +#define T_LANGUAGENAME 20 // A 'LANGUAGENAME "xxxx"' line +#define T_BITMAPS 21 // An unused 'BITMAPS' line (version 3.x) +#define T_W_END 21 // End of easily matched rule types + +#define T_UNKNOWN 99 // Unrecognized line type (illegal line) + +#define GDS_CUTLEAD 0x01 // GetDelimitedString: cut leading spaces +#define GDS_CUTFOLL 0x02 // GetDelimitedString: cut following spaces + +#define GXS_LINE 0x00 // GetExtendedString error: in a "line" +#define GXS_RULE 0x01 // GetExtendedString error: in a "rule" +#define GXS_STORE 0x02 // GetExtendedString error: in a "store" + +enum FileStoreType { FST_STORE, FST_OPTION, FST_RESERVED }; + +struct FILE_STORE { + KMX_DWORD dwSystemID; + KMX_WCHAR szName[SZMAX_STORENAME]; // the name of the store + PKMX_WCHAR dpString; // from start of store structure + //FileStoreType fstType; + KMX_BOOL fIsStore; + KMX_BOOL fIsReserved; + KMX_BOOL fIsOption; + KMX_BOOL fIsDebug; + KMX_BOOL fIsCall; + int line; +}; + +typedef FILE_STORE *PFILE_STORE; + +struct FILE_KEY { + KMX_WCHAR Key; // WCHAR for consistency; only a byte used however + KMX_WORD LineStoreIndex; + KMX_DWORD Line; + KMX_DWORD ShiftFlags; + PKMX_WCHAR dpOutput; // from start of key structure + PKMX_WCHAR dpContext; // from start of key structure +}; +typedef FILE_KEY *PFILE_KEY; + +struct FILE_GROUP { + KMX_WCHAR szName[SZMAX_GROUPNAME]; + PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure + PKMX_WCHAR dpMatch; // from start of group structure + PKMX_WCHAR dpNoMatch; // from start of group structure + KMX_DWORD cxKeyArray; // in array items + KMX_BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not + KMX_BOOL fReadOnly; // group(xx) [readonly] <-- specified or not + KMX_DWORD Line; +}; +typedef FILE_GROUP *PFILE_GROUP; + +struct FILE_DEADKEY { + KMX_WCHAR szName[SZMAX_DEADKEYNAME]; +}; + +typedef FILE_DEADKEY *PFILE_DEADKEY; + +struct FILE_VKDICTIONARY { + KMX_WCHAR szName[SZMAX_VKDICTIONARYNAME]; +}; +typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY; + +/** + * Extra metadata for API consumers + */ +struct FILE_KEYBOARD_EXTRA { + std::u16string kvksFilename; // original TSS_VISUALKEYBOARD value +}; + +typedef struct FILE_KEYBOARD_EXTRA* PFILE_KEYBOARD_EXTRA; + +struct FILE_KEYBOARD { + KMX_DWORD KeyboardID; // deprecated, unused + + KMX_DWORD version; // keyboard file version with VERSION keyword + + PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure + PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure + + KMX_DWORD cxStoreArray; // in number of items + KMX_DWORD cxGroupArray; // in number of items + KMX_DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] + + KMX_DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) + + KMX_WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name + KMX_WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name + KMX_WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information + KMX_WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard + PKMX_BYTE lpBitmap; + KMX_DWORD dwBitmapSize; + KMX_DWORD dwFlags; // Flags for the keyboard file + + KMX_DWORD currentGroup; // temp - current processing group + KMX_DWORD currentStore; // temp - current processing store + KMX_DWORD cxDeadKeyArray; + PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array + KMX_DWORD cxVKDictionary; + PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary + + PFILE_KEYBOARD_EXTRA extra; +}; + +typedef FILE_KEYBOARD *PFILE_KEYBOARD; + +/* +These size values are used in unit tests to ensure +that the structure sizes correspond precisely across +compilers (pas and c++). +*/ + +const KMX_DWORD sz_FILE_STORE = sizeof(FILE_STORE); +const KMX_DWORD sz_FILE_KEY = sizeof(FILE_KEY); +const KMX_DWORD sz_FILE_GROUP = sizeof(FILE_GROUP); +const KMX_DWORD sz_FILE_DEADKEY = sizeof(FILE_DEADKEY); +const KMX_DWORD sz_FILE_VKDICTIONARY = sizeof(FILE_VKDICTIONARY); +const KMX_DWORD sz_FILE_KEYBOARD = sizeof(FILE_KEYBOARD); + +struct COMPMSG { + KMX_CHAR szText[SZMAX_ERRORTEXT]; + KMX_DWORD Line; + KMX_DWORD dwMsgCode; +}; + +typedef COMPMSG *PCOMPMSG; + +struct COMPILEMESSAGES { + int nMessages; + int nErrors; + + PCOMPMSG cm; + + KMX_DWORD fatalCode; + KMX_CHAR szFatalText[SZMAX_ERRORTEXT]; + + KMX_DWORD currentLine; +}; + +typedef COMPILEMESSAGES *PCOMPILEMESSAGES; + +/* +struct TVersion +{ + //int MinVersion; // 0x0500 usually + //int CompilerVersion[4]; + //int MinCompilerVersion[4]; + int KeyboardVersion; // 0x0500 usually +}; + +extern TVersion FVersionInfo; +*/ + +/* +#define bstrcpy(c,d) (LPBYTE)strcpy((LPSTR)(c),(LPSTR)(d)) +#define bstrlen(c) strlen((LPSTR)(c)) +#define bstrcmp(c,d) strcmp((LPSTR)(c),(LPSTR)(d)) +#define bstrncmp(c,d,n) strncmp((LPSTR)(c),(LPSTR)(d),(n)) +#define bstrnicmp(c,d,n) strnicmp((LPSTR)(c),(LPSTR)(d),(n)) +#define bstricmp(c,d) stricmp((LPSTR)(c),(LPSTR)(d)) +#define bstrchr(c,ch) (LPBYTE)strchr((LPSTR)(c),(char)ch) +#define bstrncpy(c,d,n) (LPBYTE)strncpy((LPSTR)(c),(LPSTR)(d),(n)) +#define bstrtok(c,d) (LPBYTE)strtok((LPSTR)(c),(LPSTR)(d)) +#define bstrcat(c,d) (LPBYTE)strcat((LPSTR)(c),(LPSTR)(d)) +#define bstrncat(c,d,n) (LPBYTE)strncat((LPSTR)(c),(LPSTR)(d),(n)) +#define bstrrev(c) (LPBYTE)strrev((LPSTR)(c)) +#define batoi(c) atoi((LPSTR)(c)) +#define bstrtol(c,d,n) strtol((LPSTR)(c),(LPSTR *)(d),(n)) +*/ + +#endif // _COMPFILE_H diff --git a/developer/src/kmcmplib/src/debugstore.h b/developer/src/kmcmplib/src/debugstore.h new file mode 100644 index 0000000000000000000000000000000000000000..c54e3b45c343cb1eb3fdf006134253d706c8d915 --- /dev/null +++ b/developer/src/kmcmplib/src/debugstore.h @@ -0,0 +1,20 @@ + +#ifndef DEBUGSTORE_H +#define DEBUGSTORE_H + +#define DEBUGSTORE_BEGIN u"B" +#define DEBUGSTORE_BEGIN_C u'B' + +#define DEBUGSTORE_MATCH u"M" +#define DEBUGSTORE_MATCH_C u'M' + +#define DEBUGSTORE_NOMATCH u"N" +#define DEBUGSTORE_NOMATCH_C u'N' + +#define DEBUGSTORE_GROUP u"G" +#define DEBUGSTORE_GROUP_C u'G' + +#define DEBUGSTORE_DEADKEY u"D" +#define DEBUGSTORE_DEADKEY_C u'D' + +#endif /* DEBUGSTORE_H */ diff --git a/developer/src/kmcmplib/src/filesystem.cpp b/developer/src/kmcmplib/src/filesystem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a343641acc128ed5537e0e1835d45a2253c29f2e --- /dev/null +++ b/developer/src/kmcmplib/src/filesystem.cpp @@ -0,0 +1,188 @@ + +#ifdef __EMSCRIPTEN__ +#include +#include +#endif + +#include +#include +#include +#include "filesystem.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif + +//#define _DEBUG_FOPEN 1 + +#ifdef __EMSCRIPTEN__ + +const std::string get_wasm_file_path(const std::string& filename) { + // Verify that we are passing a fully-qualified path + // TODO: we need to support relative paths based on CWD +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path ENTER (" << filename << ")" << std::endl; +#endif + + assert( + (filename.length() > 0 && filename.at(0) == '/') || + (filename.length() > 1 && filename.at(1) == ':') + ); + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path assert passed " << std::endl; +#endif + + EM_ASM_({ + //console.log('EM_ASM enter'); + let path = Module.UTF8ToString($0); + const isWin32Path = path.match(/^[a-zA-Z]:/); + + //console.log('EM_ASM path = '+path); + let root = '/nodefs-mount'; + if(!FS.analyzePath(root).exists) { + //console.log('EM_ASM mkdir '+root); + FS.mkdir(root); + if(!isWin32Path) { + //console.log('EM_ASM mount '+root); + FS.mount(NODEFS, {root : '/'}, root); + } + } + + if(isWin32Path) { + // Win32 path, one mount per drive + root += "/" + path.charAt(0); + if(!FS.analyzePath(root).exists) { + //console.log('EM_ASM mkdir '+root); + FS.mkdir(root); + //console.log('EM_ASM mount '+root); + FS.mount(NODEFS, {root : path.substr(0,3) }, root); + } + } + }, filename.c_str()); + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path EM_ASM_ passed " << std::endl; +#endif + + std::string f = std::string("/nodefs-mount"); + + if(filename.length() > 2 && filename.at(1) == ':') { + f += std::string("/") + filename.at(0) + filename.substr(2); + } else { + f += filename; + } + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path opening virtual path: " << f << std::endl; +#endif + + return f; +} + +FILE* fopen_wasm(const std::string& filename, const std::string& mode) { + std::string f = get_wasm_file_path(filename); + FILE* fp = fopen(f.c_str(), mode.c_str()); + +#if _DEBUG_FOPEN + std::cout << "get_wasm_file_path fopen called, returned " << fp << std::endl; +#endif + + return fp; +} + +#endif + +#ifndef _MSC_VER +#ifdef __EMSCRIPTEN__ +#define fopen_wrapper fopen_wasm +#else +#define fopen_wrapper fopen +#endif +#endif + +FILE* Open_File(const KMX_CHAR* Filename, const KMX_CHAR* mode) { +#ifdef _MSC_VER + std::string cpath = Filename; //, cmode = mode; + std::replace(cpath.begin(), cpath.end(), '/', '\\'); + return fopen(cpath.c_str(), (const KMX_CHAR*) mode); +#else + return fopen_wrapper(Filename, mode); + std::string cpath, cmode; + cpath = (const KMX_CHAR*) Filename; + cmode = (const KMX_CHAR*) mode; + return fopen_wrapper(cpath.c_str(), cmode.c_str()); +#endif +}; + +FILE* Open_File(const KMX_WCHART* Filename, const KMX_WCHART* mode) { +#ifdef _MSC_VER + std::wstring cpath = Filename; //, cmode = mode; + std::replace(cpath.begin(), cpath.end(), '/', '\\'); + return _wfsopen(cpath.c_str(), mode, _SH_DENYWR); +#else + std::string cpath, cmode; + cpath = string_from_wstring(Filename); + cmode = string_from_wstring(mode); + return fopen_wrapper(cpath.c_str(), cmode.c_str()); +#endif +}; + +FILE* Open_File(const KMX_WCHAR* Filename, const KMX_WCHAR* mode) { +#ifdef _MSC_VER + std::wstring cpath = convert_pchar16T_To_wstr((KMX_WCHAR*) Filename); + std::wstring cmode = convert_pchar16T_To_wstr((KMX_WCHAR*) mode); + std::replace(cpath.begin(), cpath.end(), '/', '\\'); + return _wfsopen(cpath.c_str(), cmode.c_str(), _SH_DENYWR); +#else + std::string cpath, cmode; + cpath = string_from_u16string(Filename); + cmode = string_from_u16string(mode); + return fopen_wrapper(cpath.c_str(), cmode.c_str()); +#endif +}; + +KMX_BOOL kmcmp_FileExists(const KMX_CHAR *filename) { + +#ifdef _MSC_VER + intptr_t n; + _finddata_t fi; + if((n = _findfirst(filename, &fi)) != -1) { + _findclose(n); + return TRUE; + } +#else //!_MSC_VER + +#ifdef __EMSCRIPTEN__ + std::string f = get_wasm_file_path(filename); +#else //!__EMSCRIPTEN__ + std::string f = filename; +#endif // !__EMSCRIPTEN__ + + if(access(f.c_str(), F_OK) != -1) { + return TRUE; + } + +#endif // !_MSC_VER + + return FALSE; +} + +KMX_BOOL kmcmp_FileExists(const KMX_WCHAR* filename) { +#ifdef _MSC_VER + intptr_t n; + _wfinddata_t fi; + if((n = _wfindfirst((wchar_t*) filename, &fi)) != -1) { + _findclose(n); + return TRUE; + } +#else + std::string cpath; + cpath = string_from_u16string(filename); + return kmcmp_FileExists(cpath.c_str()); +#endif + + return FALSE; +}; diff --git a/developer/src/kmcmplib/src/filesystem.h b/developer/src/kmcmplib/src/filesystem.h new file mode 100644 index 0000000000000000000000000000000000000000..786b7268ac0c09bdda634296445e7e448404cc1e --- /dev/null +++ b/developer/src/kmcmplib/src/filesystem.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "kmx_u16.h" + +// Opens files on windows and non-windows platforms. Datatypes for Filename and mode must be the same. +// returns FILE* if file could be opened; FILE needs to be closed in calling function +FILE* Open_File(const KMX_CHAR* Filename, const KMX_CHAR* mode); +FILE* Open_File(const KMX_WCHART* Filename, const KMX_WCHART* mode); +FILE* Open_File(const KMX_WCHAR* Filename, const KMX_WCHAR* mode); +KMX_BOOL kmcmp_FileExists(const KMX_CHAR *filename); +KMX_BOOL kmcmp_FileExists(const KMX_WCHAR *filename); diff --git a/developer/src/kmcmplib/src/json-validation.cpp b/developer/src/kmcmplib/src/json-validation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f58d8c950534943427285a4f400fb39c190217ff --- /dev/null +++ b/developer/src/kmcmplib/src/json-validation.cpp @@ -0,0 +1,68 @@ + +#include +#include + +#include +#include "kmcompx.h" + +using nlohmann::json; +using nlohmann::json_uri; +using nlohmann::json_schema_draft4::json_validator; + +static void loader(const json_uri &uri, json &schema) +{ + std::fstream lf("." + uri.path()); + if (!lf.good()) + throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path()); + + lf >> schema; +} + +EXTERN bool kmcmp_ValidateJsonFile(std::fstream& f, std::fstream& fd, kmcmp_ValidateJsonMessageProc MessageProc, void* context) { + if (!f.good()) { + MessageProc(-1, "Schema file could not be loaded.", context); + return FALSE; + } + + // 1) Read the schema for the document you want to validate + json schema; + try { + f >> schema; + } + catch (std::exception &e) { + MessageProc(f.tellp(), e.what(), context); + return FALSE; + } + + // 2) create the validator and + json_validator validator(loader, [](const std::string &, const std::string &) {}); + + try { + // insert this schema as the root to the validator + // this resolves remote-schemas, sub-schemas and references via the given loader-function + validator.set_root_schema(schema); + } + catch (std::exception &e) { + MessageProc(-2, e.what(), context); + return FALSE; + } + + // 3) do the actual validation of the document + json document; + + if (!fd.good()) { + MessageProc(-3, "Json file could not be loaded.", context); + return FALSE; + } + + try { + fd >> document; + validator.validate(document); + } + catch (std::exception &e) { + MessageProc(fd.tellp(), e.what(), context); + return FALSE; + } + + return TRUE; +} \ No newline at end of file diff --git a/developer/src/kmcmplib/src/kmcmplib.h b/developer/src/kmcmplib/src/kmcmplib.h new file mode 100644 index 0000000000000000000000000000000000000000..78e945ee0d866af69158dd7a9b1e04e3916a914a --- /dev/null +++ b/developer/src/kmcmplib/src/kmcmplib.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include "compfile.h" +#include "NamedCodeConstants.h" + +namespace kmcmp { + KMX_BOOL AddCompileWarning(char* buf); + extern int currentLine; + extern KMX_BOOL FShouldAddCompilerVersion; + extern KMX_BOOL FSaveDebug, FCompilerWarningsAsErrors; // I4865 // I4866 + extern int ErrChr; + extern int nErrors; + extern KMX_BOOL FMnemonicLayout; + extern KMX_BOOL FOldCharPosMatching; + extern int CompileTarget; + extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer + extern int BeginLine[4]; + extern int currentLine; + extern NamedCodeConstants *CodeConstants; + + void RecordDeadkeyNames(PFILE_KEYBOARD fk); + KMX_DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk); +} + +extern kmcmp_CompilerMessageProc msgproc; +extern void* msgprocContext; + +extern KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB; +#define ERR_EXTRA_LIB_LEN 256 +#define ERR_EXTRA_W_LEN 256 +extern char ErrExtraLIB[ERR_EXTRA_LIB_LEN]; +extern KMX_WCHAR ErrExtraW[ERR_EXTRA_W_LEN]; +KMX_BOOL AddCompileError(KMX_DWORD msg); + +/// Use AddWarningBool for functions that return bool or KMX_BOOL +#define AddWarningBool(warn) { if(AddCompileError(warn)) return FALSE; } +/// Use AddWarning for functions that return KMX_DWORD +#define AddWarning(warn) { if(AddCompileError(warn)) return CERR_Break; } + +PKMX_WCHAR strtowstr(PKMX_STR in); +PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID); +FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble); +KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize); +KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID= NULL); +KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess); +KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str); +KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p); +KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk); +KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p); +int LineTokenType(PKMX_WCHAR *str); +KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk); // I3438 diff --git a/developer/src/kmcmplib/src/kmx_u16.cpp b/developer/src/kmcmplib/src/kmx_u16.cpp new file mode 100644 index 0000000000000000000000000000000000000000..582ef1ad97c905c2c31f8764a781b6eaad3446c3 --- /dev/null +++ b/developer/src/kmcmplib/src/kmx_u16.cpp @@ -0,0 +1,296 @@ + +//#include "../../kmcompx/include/kmcompx.h" +#include +#include "kmx_u16.h" + +#include +#include +#include +#include +#include +#include + +//String <- wstring +std::string string_from_wstring(std::wstring const str) { + std::wstring_convert, wchar_t> converter; + return converter.to_bytes(str); +} +//wstring <- string +std::wstring wstring_from_string(std::string const str) { + std::wstring_convert, wchar_t> converter; + return converter.from_bytes(str); +} + +//u16String <- string +std::u16string u16string_from_string(std::string const str) { + std::wstring_convert, char16_t> converter; + return converter.from_bytes(str); +} + +//string <- u16string +std::string string_from_u16string(std::u16string const str) { + std::wstring_convert, char16_t> converter; + return converter.to_bytes(str); +} + +// often used with c_str() e.g. u16fmt( DEBUGSTORE_MATCH).c_str() +// UTF16 (= const char16_t*) -> UTF8 (= std::string) -> UTF16 ( = std::wstring 16 bit) +std::wstring u16fmt(const KMX_WCHAR * str) { + std::wstring_convert, wchar_t> convert_wstring; + std::wstring_convert, char16_t> convert; + + // UTF16 (= const char16_t*) -> UTF8 (= std::string) -> UTF16 ( = std::wstring 16 bit) + std::string utf8str = convert.to_bytes(str); // UTF16 (= const char16_t*) -> UTF8 (= std::string) + std::wstring wstr = convert_wstring.from_bytes(utf8str); // UTF8 (= std::string) -> UTF16 ( = std::wstring 16 bit) + return wstr; +} + +void u16sprintf(KMX_WCHAR * dst, const size_t sz, const wchar_t* fmt, ...) { + // UTF16 (=const wchar_t*) -> -> std::string -> std::u16string -> UTF16 ( = char16_t*) + wchar_t* wbuf = new wchar_t[sz]; + va_list args; + va_start(args, fmt); + vswprintf(wbuf, sz, fmt, args); + va_end(args); + + std::wstring_convert, wchar_t> convert_wstring; + std::wstring_convert, char16_t> convert; + + // UTF16 (=const wchar_t*) -> -> std::string -> std::u16string -> UTF16 ( = char16_t*) + std::string utf8str = convert_wstring.to_bytes(wbuf); // UTF16 ( = const wchar_t*) -> std::string + std::u16string u16str = convert.from_bytes(utf8str); // std::string -> std::u16string + u16ncpy(dst, u16str.c_str(), sz); // std::u16string.c_str() -> char16_t* + delete[] wbuf; +} + + std::wstring convert_pchar16T_To_wstr(KMX_WCHAR *Name){ + // convert char16_t* -> std::u16string -> std::string -> std::wstring + // char16_t* -> std::u16string + std::u16string u16str(Name); + // std::u16string -> std::string + std::string stri = string_from_u16string(u16str); + // std::string -> std::wstring + std::wstring wstr = wstring_from_string(stri); + return wstr; + } + +long int u16tol(const KMX_WCHAR* str, KMX_WCHAR** endptr, int base) +{ + auto s = string_from_u16string(str); + char* t; + long int result = strtol(s.c_str(), &t, base); + if (endptr != nullptr) *endptr = (KMX_WCHAR*)str + (t - s.c_str()); + return result; +} + +std::string toHex(int num1) { + if (num1 == 0) + return "0"; + int num = num1; + std::string s = ""; + while (num) { + int temp = num % 16; + if (temp <= 9) + s += (48 + temp); + else + s += (87 + temp); + num = num / 16; + } + reverse(s.begin(), s.end()); + return s; +} + +const KMX_WCHAR * u16ncat(KMX_WCHAR *dst, const KMX_WCHAR *src, size_t max) { + KMX_WCHAR* o = dst; + dst = (KMX_WCHAR*) u16chr(dst, 0); + //max -= (dst-o); + while (*src && max > 0) { + *dst++ = *src++; + max--; + } + if(max > 0) + *dst = 0; + return o; +} + +const KMX_WCHAR* u16rchr_slash(KMX_WCHAR const* Name) +{ + const KMX_WCHAR* cp = NULL; + cp = u16rchr(Name, '\\'); + if (cp == NULL) + cp = u16rchr(Name, '/'); + return cp; +} + +KMX_CHAR* strrchr_slash(KMX_CHAR* Name) +{ + KMX_CHAR* cp = NULL; + cp = strrchr(Name, '\\'); + if (cp == NULL) + cp = strrchr(Name, '/'); + return cp; +} + +// u16rchr returns last occurence of ch in p; It returns NULL if ch = '\0' and NULL if ch is not found +const KMX_WCHAR* u16rchr(const KMX_WCHAR* p, KMX_WCHAR ch) { + const KMX_WCHAR* p_end = p + u16len(p) - 1; + + if (ch == '\0') return p_end + 1; + while (p_end >= p) { + if (*p_end == ch) return p_end; + p_end--; + } + return NULL; +} + +const KMX_WCHAR * u16chr(const KMX_WCHAR *p, KMX_WCHAR ch) { + while (*p) { + if (*p == ch) return p; + p++; + } + return ch == 0 ? p : NULL; +} + +const KMX_WCHAR * u16cpy(KMX_WCHAR *dst, const KMX_WCHAR *src) { + KMX_WCHAR *o = dst; + while (*src) { + *dst++ = *src++; + } + *dst = 0; + return o; +} + +const KMX_WCHAR * u16ncpy(KMX_WCHAR *dst, const KMX_WCHAR *src, size_t max) { + KMX_WCHAR *o = dst; + while (*src && max > 0) { + *dst++ = *src++; + max--; + } + while(max > 0) { + *dst++ = 0; + max--; + } + return o; +} + +size_t u16len(const KMX_WCHAR *p) { + int i = 0; + while (*p) { + p++; + i++; + } + return i; +} + +int u16cmp(const KMX_WCHAR *p, const KMX_WCHAR *q) { + while (*p && *q) { + if (*p != *q) return *p - *q; + p++; + q++; + } + return *p - *q; +} + +int u16nicmp(const KMX_WCHAR *p, const KMX_WCHAR *q, size_t count) { + while (*p && *q && count) { + if (toupper(*p) != toupper(*q)) return *p - *q; + p++; + q++; + count--; + } + if (count) + return *p - *q; + return 0; +} + +int u16icmp(const KMX_WCHAR *p, const KMX_WCHAR *q) { + while (*p && *q) { + if (toupper(*p) != toupper(*q)) return *p - *q; + p++; + q++; + } + return *p - *q; +} + +int u16ncmp(const KMX_WCHAR *p, const KMX_WCHAR *q, size_t count) { + while (*p && *q && count) { + if (*p != *q) return *p - *q; + p++; + q++; + count--; + } + if (count) + return *p - *q; + return 0; +} + +KMX_WCHAR * u16tok(KMX_WCHAR *p, KMX_WCHAR ch, KMX_WCHAR **ctx) { + if (!p) { + p = *ctx; + if (!p) return NULL; + } + + KMX_WCHAR *q = p; + while (*q && *q != ch) { + q++; + } + if (*q) { + *q = 0; + q++; + while (*q == ch) q++; + *ctx = q; + } + else { + *ctx = NULL; + } + return p; +} + +KMX_WCHAR * u16tok(KMX_WCHAR* p, KMX_WCHAR* delim, KMX_WCHAR** ctx) { + if (!p) { + p = *ctx; + if (!p) return NULL; + } + + KMX_WCHAR * q = p; + while (*q && !u16chr(delim, *q)) { + q++; + } + if (*q) { + *q = 0; + q++; + while (u16chr(delim, *q)) q++; + *ctx = q; + } + else { + *ctx = NULL; + } + return p; +} + +double u16tof( KMX_WCHAR* str) +{ + double val = 0; + int offsetdot=0; + char digit; + + PKMX_WCHAR q = (PKMX_WCHAR)u16chr(str, '.'); + size_t pos_dot = q-str ; + + if (pos_dot < 0) + pos_dot = u16len(str); + + for (size_t i = 0; i < u16len(str); i++) + { + digit = static_cast(towupper(*str)); + + if (i > pos_dot - 1) + offsetdot = 1; + + if (digit != '.') + val =val+ ((int(digit)) - 48) * pow(10, (pos_dot - 1- i + offsetdot)); + + str++; + } + return val; +} diff --git a/developer/src/kmcmplib/src/kmx_u16.h b/developer/src/kmcmplib/src/kmx_u16.h new file mode 100644 index 0000000000000000000000000000000000000000..99ad2006bbbe57c57734a1a57c71e4538e318972 --- /dev/null +++ b/developer/src/kmcmplib/src/kmx_u16.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include "kmcompx.h" + +std::string string_from_wstring(std::wstring const str); +std::wstring wstring_from_string(std::string const str); +std::u16string u16string_from_string(std::string const str); +std::string string_from_u16string(std::u16string const str); + +std::wstring u16fmt(const KMX_WCHAR * str); +void u16sprintf(KMX_WCHAR * dst, const size_t sz, const wchar_t* fmt, ...) ; + +std::wstring convert_pchar16T_To_wstr(KMX_WCHAR *Name); + +size_t u16len(const KMX_WCHAR *p); +int u16cmp(const KMX_WCHAR *p, const KMX_WCHAR *q); +int u16icmp(const KMX_WCHAR *p, const KMX_WCHAR *q); +int u16ncmp(const KMX_WCHAR *p, const KMX_WCHAR *q, size_t count); +int u16nicmp(const KMX_WCHAR *p, const KMX_WCHAR *q, size_t count) ; +const KMX_WCHAR * u16ncpy(KMX_WCHAR *dst, const KMX_WCHAR *src, size_t max); +const KMX_WCHAR * u16cpy(KMX_WCHAR *dst, const KMX_WCHAR *src); +const KMX_WCHAR * u16rchr(const KMX_WCHAR *p, KMX_WCHAR ch) ; +const KMX_WCHAR * u16chr(const KMX_WCHAR *p, KMX_WCHAR ch) ; +const KMX_WCHAR * u16ncat(KMX_WCHAR *dst, const KMX_WCHAR *src, size_t max); +KMX_WCHAR * u16tok(KMX_WCHAR *p, KMX_WCHAR ch, KMX_WCHAR **ctx) ; +KMX_WCHAR * u16tok(KMX_WCHAR* p, KMX_WCHAR* ch, KMX_WCHAR** ctx) ; +long int u16tol(const KMX_WCHAR* str, KMX_WCHAR** endptr, int base) ; +double u16tof( KMX_WCHAR* str); + +KMX_CHAR* strrchr_slash(KMX_CHAR* Name); +const KMX_WCHAR* u16rchr_slash(KMX_WCHAR const* Name); + +std::string toHex(int num1); diff --git a/developer/src/kmcmplib/src/meson.build b/developer/src/kmcmplib/src/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..292b158ce276abf20060f38c8770cdbf1f0dcb84 --- /dev/null +++ b/developer/src/kmcmplib/src/meson.build @@ -0,0 +1,88 @@ +# +# Keyman is copyright (C) SIL International. MIT License. +# +# Cross platform build script to compile kmcompx +# + +# TODO: is this required? It should be Keyman Core only +defns += ['-DKMN_KBP_EXPORTING'] +version_res = [] + +if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' + warns += [ + '-Wall', + '-Wextra' + ] +endif + +if cpp_compiler.get_id() == 'msvc' + # /n = append null + # /c65001 = utf-8 file format + version_res += import('windows').compile_resources('version.rc', args:['/n','/c65001']) +endif + +name_suffix = [] + +if cpp_compiler.get_id() == 'emscripten' + links += ['-lnodefs.js', '-sMODULARIZE', '-sEXPORT_ES6', '--whole-archive', '--bind', '-sEXPORTED_RUNTIME_METHODS=[\'cwrap\', \'UTF8ToString\']'] + # tests are building as ES6 so we need to declare the file extension + # note that meson currently struggles with the sanitycheckc_cross.exe + # program, because it has a hard coded extension (.exe) which is not + # valid for node programs in module mode. + name_suffix = '.mjs' +endif + +icu = subproject('icu-for-uset', default_options: [ 'default_library=static', 'cpp_std=c++17', 'warning_level=0', 'werror=false']) +icuuc_dep = icu.get_variable('icuuc_dep') + +lib = library('kmcmplib', + 'CasedKeys.cpp', + 'CharToKeyConversion.cpp', + 'Compiler.cpp', + 'CompilerInterfaces.cpp', + 'DeprecationChecks.cpp', + 'Edition.cpp', + 'filesystem.cpp', + 'json-validation.cpp', + 'NamedCodeConstants.cpp', + 'versioning.cpp', + 'virtualcharkeys.cpp', + 'xstring.cpp', + 'CharToKeyConversion.cpp', + 'CheckFilenameConsistency.cpp', + 'CheckForDuplicates.cpp', + 'CheckNCapsConsistency.cpp', + 'UnreachableRules.cpp', + 'kmx_u16.cpp', + 'CompMsg.cpp', + 'uset-api.cpp', + + '../../../../common/windows/cpp/src/ConvertUTF.c', + '../../../../common/windows/cpp/src/crc32.cpp', + '../../../../common/windows/cpp/src/vkeys.cpp', + 'xstring.cpp', + + '../../ext/json-schema-validator/json-schema-draft4.json.cpp', + '../../ext/json-schema-validator/json-uri.cpp', + '../../ext/json-schema-validator/json-validator.cpp', + version_res, + cpp_args: defns + warns + flags, + link_args: links, + version: meson.project_version(), + include_directories: inc, + install: true, + dependencies: icuuc_dep) + +kmcmplib = declare_dependency(link_with: lib, include_directories: inc) + + +if cpp_compiler.get_id() == 'emscripten' + # Build an executable + host = executable('wasm-host', #'wasm-host.cpp', + cpp_args: defns, + include_directories: inc, + link_args: links, + objects: lib.extract_all_objects(), + dependencies: icuuc_dep) +endif + diff --git a/developer/src/kmcmplib/src/pch.cpp b/developer/src/kmcmplib/src/pch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17305716aacd2a39a54423c3fb2000dc2266794a --- /dev/null +++ b/developer/src/kmcmplib/src/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/developer/src/kmcmplib/src/pch.h b/developer/src/kmcmplib/src/pch.h new file mode 100644 index 0000000000000000000000000000000000000000..f878ad11be4f892ad4e9f0ece5864712c6af5c6b --- /dev/null +++ b/developer/src/kmcmplib/src/pch.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define USE_CHAR16_T +#include +#include "kmx_u16.h" +#include + +#include "../../../../common/windows/cpp/include/crc32.h" + +#include +#include + +#include diff --git a/developer/src/kmcmplib/src/uset-api.cpp b/developer/src/kmcmplib/src/uset-api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1aa22477b2b63465a8cb0db8b7d624409d9ce083 --- /dev/null +++ b/developer/src/kmcmplib/src/uset-api.cpp @@ -0,0 +1,55 @@ +#include +#include "kmcompx.h" + +#include "unicode/uniset.h" +#include "unicode/unistr.h" + +EXTERN int kmcmp_ParseUnicodeSet( + const char* szText, + uint32_t* outputBuffer, + uint32_t outputBufferSize +) { + if (szText == nullptr) { + // null string coming in + return KMCMP_ERROR_SYNTAX_ERR; + } + const icu::UnicodeString str = icu::UnicodeString::fromUTF8(szText); + if (str.isBogus() || str.isEmpty()) { + // empty string + return KMCMP_ERROR_SYNTAX_ERR; + } + UErrorCode status = U_ZERO_ERROR; + + icu::UnicodeSet uset(str, status); + // TODO-LDML: check for properties + + if (U_FAILURE(status)) { + if (status == U_MISSING_RESOURCE_ERROR) { + // Our special ICU returns this + return KMCMP_ERROR_UNSUPPORTED_PROPERTY; + } else { + // Any other error. + return KMCMP_ERROR_SYNTAX_ERR; + } + } else if (uset.hasStrings()) { + // Error, strings are not allowed + return KMCMP_ERROR_HAS_STRINGS; + } else if (outputBuffer == nullptr) { + // fail if nullptr passed + return KMCMP_FATAL_OUT_OF_RANGE; + } + + const int32_t count = uset.getRangeCount(); + if ((count * 2L) > outputBufferSize) { + // output buffer too small + return KMCMP_FATAL_OUT_OF_RANGE; + } + + // set all ranges + for (int32_t i=0; i +#include "kmcmplib.h" +#include "versioning.h" + +KMX_BOOL kmcmp::CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) { + KMX_CHAR buf[128]; + + if (fk->dwFlags & KF_AUTOMATICVERSION) { + if (fk->version <= 0) { + fk->version = VERSION_60; // minimum version that we can be safe with + } + + snprintf(buf, 128, "The compiler has assigned a minimum engine version of %d.%d based on features used in this keyboard", (int)((fk->version & 0xFF00) >> 8), (int)(fk->version & 0xFF)); + AddCompileWarning(buf); + } + + return TRUE; +} + +// Note: max is not a standard c api function or macro +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +KMX_BOOL VerifyKeyboardVersion(PFILE_KEYBOARD fk, KMX_DWORD ver) { + if (fk->dwFlags & KF_AUTOMATICVERSION) { + fk->version = max(fk->version, ver); + return TRUE; + } + + return fk->version >= ver; +} diff --git a/developer/src/kmcmplib/src/versioning.h b/developer/src/kmcmplib/src/versioning.h new file mode 100644 index 0000000000000000000000000000000000000000..a3a1c597bf668930e76dc4989ced254ad62ea0f3 --- /dev/null +++ b/developer/src/kmcmplib/src/versioning.h @@ -0,0 +1,15 @@ +#pragma once + +#include "compfile.h" +#include "../../../../common/include/km_types.h" + +#define VERIFY_KEYBOARD_VERSION(fk, ver, err) { \ + if(!VerifyKeyboardVersion((fk), (ver))) \ + return (err); \ +} + +namespace kmcmp { + KMX_BOOL CheckKeyboardFinalVersion(PFILE_KEYBOARD fk); +} + +KMX_BOOL VerifyKeyboardVersion(PFILE_KEYBOARD fk, KMX_DWORD ver); diff --git a/developer/src/kmcmplib/src/virtualcharkeys.cpp b/developer/src/kmcmplib/src/virtualcharkeys.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eaef45aac4eb3bef64e47d7abb4a8b39e906bcf0 --- /dev/null +++ b/developer/src/kmcmplib/src/virtualcharkeys.cpp @@ -0,0 +1,274 @@ +#include "pch.h" +#include "virtualcharkeys.h" + +KMX_BOOL VKeyMayBeVCKey[256] = { + FALSE, // L"K_?00", // &H0 + FALSE, // L"K_LBUTTON", // &H1 + FALSE, // L"K_RBUTTON", // &H2 + FALSE, // L"K_CANCEL", // &H3 + FALSE, // L"K_MBUTTON", // &H4 + FALSE, // L"K_?05", // &H5 + FALSE, // L"K_?06", // &H6 + FALSE, // L"K_?07", // &H7 + FALSE, // L"K_BKSP", // &H8 + FALSE, // L"K_TAB", // &H9 + FALSE, // L"K_?0A", // &HA + FALSE, // L"K_?0B", // &HB + FALSE, // L"K_KP5", // &HC + FALSE, // L"K_ENTER", // &HD + FALSE, // L"K_?0E", // &HE + FALSE, // L"K_?0F", // &HF + FALSE, // L"K_SHIFT", // &H10 + FALSE, // L"K_CONTROL", // &H11 + FALSE, // L"K_ALT", // &H12 + FALSE, // L"K_PAUSE", // &H13 + FALSE, // L"K_CAPS", // &H14 + FALSE, // L"K_KANJI?15", // &H15 + FALSE, // L"K_KANJI?16", // &H16 + FALSE, // L"K_KANJI?17", // &H17 + FALSE, // L"K_KANJI?18", // &H18 + FALSE, // L"K_KANJI?19", // &H19 + FALSE, // L"K_?1A", // &H1A + FALSE, // L"K_ESC", // &H1B + FALSE, // L"K_KANJI?1C", // &H1C + FALSE, // L"K_KANJI?1D", // &H1D + FALSE, // L"K_KANJI?1E", // &H1E + FALSE, // L"K_KANJI?1F", // &H1F + TRUE, // L"K_SPACE", // &H20 + FALSE, // L"K_PGUP", // &H21 + FALSE, // L"K_PGDN", // &H22 + FALSE, // L"K_END", // &H23 + FALSE, // L"K_HOME", // &H24 + FALSE, // L"K_LEFT", // &H25 + FALSE, // L"K_UP", // &H26 + FALSE, // L"K_RIGHT", // &H27 + FALSE, // L"K_DOWN", // &H28 + FALSE, // L"K_SEL", // &H29 + FALSE, // L"K_PRINT", // &H2A + FALSE, // L"K_EXEC", // &H2B + FALSE, // L"K_PRTSCN", // &H2C + FALSE, // L"K_INS", // &H2D + FALSE, // L"K_DEL", // &H2E + FALSE, // L"K_HELP", // &H2F + TRUE, // L"K_0", // &H30 + TRUE, // L"K_1", // &H31 + TRUE, // L"K_2", // &H32 + TRUE, // L"K_3", // &H33 + TRUE, // L"K_4", // &H34 + TRUE, // L"K_5", // &H35 + TRUE, // L"K_6", // &H36 + TRUE, // L"K_7", // &H37 + TRUE, // L"K_8", // &H38 + TRUE, // L"K_9", // &H39 + FALSE, // L"K_?3A", // &H3A + FALSE, // L"K_?3B", // &H3B + FALSE, // L"K_?3C", // &H3C + FALSE, // L"K_?3D", // &H3D + FALSE, // L"K_?3E", // &H3E + FALSE, // L"K_?3F", // &H3F + FALSE, // L"K_?40", // &H40 + + TRUE, // L"K_A", // &H41 + TRUE, // L"K_B", // &H42 + TRUE, // L"K_C", // &H43 + TRUE, // L"K_D", // &H44 + TRUE, // L"K_E", // &H45 + TRUE, // L"K_F", // &H46 + TRUE, // L"K_G", // &H47 + TRUE, // L"K_H", // &H48 + TRUE, // L"K_I", // &H49 + TRUE, // L"K_J", // &H4A + TRUE, // L"K_K", // &H4B + TRUE, // L"K_L", // &H4C + TRUE, // L"K_M", // &H4D + TRUE, // L"K_N", // &H4E + TRUE, // L"K_O", // &H4F + TRUE, // L"K_P", // &H50 + TRUE, // L"K_Q", // &H51 + TRUE, // L"K_R", // &H52 + TRUE, // L"K_S", // &H53 + TRUE, // L"K_T", // &H54 + TRUE, // L"K_U", // &H55 + TRUE, // L"K_V", // &H56 + TRUE, // L"K_W", // &H57 + TRUE, // L"K_X", // &H58 + TRUE, // L"K_Y", // &H59 + TRUE, // L"K_Z", // &H5A + FALSE, // L"K_?5B", // &H5B + FALSE, // L"K_?5C", // &H5C + FALSE, // L"K_?5D", // &H5D + FALSE, // L"K_?5E", // &H5E + FALSE, // L"K_?5F", // &H5F + FALSE, // L"K_NP0", // &H60 + FALSE, // L"K_NP1", // &H61 + FALSE, // L"K_NP2", // &H62 + FALSE, // L"K_NP3", // &H63 + FALSE, // L"K_NP4", // &H64 + FALSE, // L"K_NP5", // &H65 + FALSE, // L"K_NP6", // &H66 + FALSE, // L"K_NP7", // &H67 + FALSE, // L"K_NP8", // &H68 + FALSE, // L"K_NP9", // &H69 + FALSE, // L"K_NPSTAR", // &H6A + FALSE, // L"K_NPPLUS", // &H6B + FALSE, // L"K_SEPARATOR", // &H6C + FALSE, // L"K_NPMINUS", // &H6D + FALSE, // L"K_NPDOT", // &H6E + FALSE, // L"K_NPSLASH", // &H6F + FALSE, // L"K_F1", // &H70 + FALSE, // L"K_F2", // &H71 + FALSE, // L"K_F3", // &H72 + FALSE, // L"K_F4", // &H73 + FALSE, // L"K_F5", // &H74 + FALSE, // L"K_F6", // &H75 + FALSE, // L"K_F7", // &H76 + FALSE, // L"K_F8", // &H77 + FALSE, // L"K_F9", // &H78 + FALSE, // L"K_F10", // &H79 + FALSE, // L"K_F11", // &H7A + FALSE, // L"K_F12", // &H7B + FALSE, // L"K_F13", // &H7C + FALSE, // L"K_F14", // &H7D + FALSE, // L"K_F15", // &H7E + FALSE, // L"K_F16", // &H7F + FALSE, // L"K_F17", // &H80 + FALSE, // L"K_F18", // &H81 + FALSE, // L"K_F19", // &H82 + FALSE, // L"K_F20", // &H83 + FALSE, // L"K_F21", // &H84 + FALSE, // L"K_F22", // &H85 + FALSE, // L"K_F23", // &H86 + FALSE, // L"K_F24", // &H87 + + FALSE, // L"K_?88", // &H88 + FALSE, // L"K_?89", // &H89 + FALSE, // L"K_?8A", // &H8A + FALSE, // L"K_?8B", // &H8B + FALSE, // L"K_?8C", // &H8C + FALSE, // L"K_?8D", // &H8D + FALSE, // L"K_?8E", // &H8E + FALSE, // L"K_?8F", // &H8F + + FALSE, // L"K_NUMLOCK", // &H90 + FALSE, // L"K_SCROLL", // &H91 + + FALSE, // L"K_?92", // &H92 + FALSE, // L"K_?93", // &H93 + FALSE, // L"K_?94", // &H94 + FALSE, // L"K_?95", // &H95 + FALSE, // L"K_?96", // &H96 + FALSE, // L"K_?97", // &H97 + FALSE, // L"K_?98", // &H98 + FALSE, // L"K_?99", // &H99 + FALSE, // L"K_?9A", // &H9A + FALSE, // L"K_?9B", // &H9B + FALSE, // L"K_?9C", // &H9C + FALSE, // L"K_?9D", // &H9D + FALSE, // L"K_?9E", // &H9E + FALSE, // L"K_?9F", // &H9F + FALSE, // L"K_?A0", // &HA0 + FALSE, // L"K_?A1", // &HA1 + FALSE, // L"K_?A2", // &HA2 + FALSE, // L"K_?A3", // &HA3 + FALSE, // L"K_?A4", // &HA4 + FALSE, // L"K_?A5", // &HA5 + FALSE, // L"K_?A6", // &HA6 + FALSE, // L"K_?A7", // &HA7 + FALSE, // L"K_?A8", // &HA8 + FALSE, // L"K_?A9", // &HA9 + FALSE, // L"K_?AA", // &HAA + FALSE, // L"K_?AB", // &HAB + FALSE, // L"K_?AC", // &HAC + FALSE, // L"K_?AD", // &HAD + FALSE, // L"K_?AE", // &HAE + FALSE, // L"K_?AF", // &HAF + FALSE, // L"K_?B0", // &HB0 + FALSE, // L"K_?B1", // &HB1 + FALSE, // L"K_?B2", // &HB2 + FALSE, // L"K_?B3", // &HB3 + FALSE, // L"K_?B4", // &HB4 + FALSE, // L"K_?B5", // &HB5 + FALSE, // L"K_?B6", // &HB6 + FALSE, // L"K_?B7", // &HB7 + FALSE, // L"K_?B8", // &HB8 + FALSE, // L"K_?B9", // &HB9 + + TRUE, // L"K_COLON", // &HBA + TRUE, // L"K_EQUAL", // &HBB + TRUE, // L"K_COMMA", // &HBC + TRUE, // L"K_HYPHEN", // &HBD + TRUE, // L"K_PERIOD", // &HBE + TRUE, // L"K_SLASH", // &HBF + TRUE, // L"K_BKQUOTE", // &HC0 + + TRUE, // L"K_?C1", // &HC1 -- 103rd key on Brazilian - /? + FALSE, // L"K_?C2", // &HC2 -- 104th key on Brazilian - numpad . + FALSE, // L"K_?C3", // &HC3 + FALSE, // L"K_?C4", // &HC4 + FALSE, // L"K_?C5", // &HC5 + FALSE, // L"K_?C6", // &HC6 + FALSE, // L"K_?C7", // &HC7 + FALSE, // L"K_?C8", // &HC8 + FALSE, // L"K_?C9", // &HC9 + FALSE, // L"K_?CA", // &HCA + FALSE, // L"K_?CB", // &HCB + FALSE, // L"K_?CC", // &HCC + FALSE, // L"K_?CD", // &HCD + FALSE, // L"K_?CE", // &HCE + FALSE, // L"K_?CF", // &HCF + FALSE, // L"K_?D0", // &HD0 + FALSE, // L"K_?D1", // &HD1 + FALSE, // L"K_?D2", // &HD2 + FALSE, // L"K_?D3", // &HD3 + FALSE, // L"K_?D4", // &HD4 + FALSE, // L"K_?D5", // &HD5 + FALSE, // L"K_?D6", // &HD6 + FALSE, // L"K_?D7", // &HD7 + FALSE, // L"K_?D8", // &HD8 + FALSE, // L"K_?D9", // &HD9 + FALSE, // L"K_?DA", // &HDA + + TRUE, // L"K_LBRKT", // &HDB + TRUE, // L"K_BKSLASH", // &HDC + TRUE, // L"K_RBRKT", // &HDD + TRUE, // L"K_QUOTE", // &HDE + TRUE, // L"K_oDF", // &HDF + TRUE, // L"K_oE0", // &HE0 + TRUE, // L"K_oE1", // &HE1 + TRUE, // L"K_oE2", // &HE2 + TRUE, // L"K_oE3", // &HE3 + TRUE, // L"K_oE4", // &HE4 + + FALSE, // L"K_?E5", // &HE5 + + TRUE, // L"K_oE6", // &HE6 + + FALSE, // L"K_?E7", // &HE7 + FALSE, // L"K_?E8", // &HE8 + + TRUE, // L"K_oE9", // &HE9 + TRUE, // L"K_oEA", // &HEA + TRUE, // L"K_oEB", // &HEB + TRUE, // L"K_oEC", // &HEC + TRUE, // L"K_oED", // &HED + TRUE, // L"K_oEE", // &HEE + TRUE, // L"K_oEF", // &HEF + TRUE, // L"K_oF0", // &HF0 + TRUE, // L"K_oF1", // &HF1 + TRUE, // L"K_oF2", // &HF2 + TRUE, // L"K_oF3", // &HF3 + TRUE, // L"K_oF4", // &HF4 + TRUE, // L"K_oF5", // &HF5 + + FALSE, // L"K_?F6", // &HF6 + FALSE, // L"K_?F7", // &HF7 + FALSE, // L"K_?F8", // &HF8 + FALSE, // L"K_?F9", // &HF9 + FALSE, // L"K_?FA", // &HFA + FALSE, // L"K_?FB", // &HFB + FALSE, // L"K_?FC", // &HFC + FALSE, // L"K_?FD", // &HFD + FALSE, // L"K_?FE", // &HFE + FALSE, // L"K_?FF" // &HFF + }; + diff --git a/developer/src/kmcmplib/src/virtualcharkeys.h b/developer/src/kmcmplib/src/virtualcharkeys.h new file mode 100644 index 0000000000000000000000000000000000000000..539c5c5d4075e19e95f38ecee901bb409634bbd6 --- /dev/null +++ b/developer/src/kmcmplib/src/virtualcharkeys.h @@ -0,0 +1,7 @@ +#ifndef _VIRTUALCHARKEYS_H +#define _VIRTUALCHARKEYS_H + +extern KMX_BOOL VKeyMayBeVCKey[256]; + +#endif + diff --git a/developer/src/kmcmplib/src/xstring.cpp b/developer/src/kmcmplib/src/xstring.cpp new file mode 100644 index 0000000000000000000000000000000000000000..870e991656c08d95dd461fab4613ea61eb62aee5 --- /dev/null +++ b/developer/src/kmcmplib/src/xstring.cpp @@ -0,0 +1,152 @@ + +#include "pch.h" +#include "xstring.h" +// used to be windows/src/global/vc/xstring.cpp +/* +* int xstrlen( PKMX_BYTE p ); +* +* Parameters: p Pointer to string to get length of +* +* Returns: length of string +* +* Called by: various functions +* +* xstrlen calculates the length of a string, ignoring some special chars. +*/ +PKMX_WCHAR incxstr(PKMX_WCHAR p) { + + if (*p == 0) + return p; + if (*p != UC_SENTINEL) { + if (*p >= 0xD800 && *p <= 0xDBFF && *(p + 1) >= 0xDC00 && *(p + 1) <= 0xDFFF) + return p + 2; + return p + 1; + } + // UC_SENTINEL(FFFF) with UC_SENTINEL_EXTENDEDEND(0x10) == variable length + if (*(p + 1) == CODE_EXTENDED) { + p += 2; + while (*p && *p != UC_SENTINEL_EXTENDEDEND) + p++; + + if (*p == 0) return p; + return p + 1; + } + + if (*(p + 1) > CODE_LASTCODE || CODE__SIZE[*(p + 1)] == -1) { + return p + 1; + } + + int deltaptr = 2 + CODE__SIZE[*(p + 1)]; + + // check for \0 between UC_SENTINEL(FFFF) and next printable character + for (int i = 0; i < deltaptr; i++) { + if (*p == 0) + return p; + p++; + } + return p; +} + +PKMX_WCHAR decxstr(PKMX_WCHAR p, PKMX_WCHAR pStart) +{ + PKMX_WCHAR q; + + if(p <= pStart) { + return NULL; + } + + p--; + if(*p == UC_SENTINEL_EXTENDEDEND) { + int n = 0; + while (p > pStart && *p != UC_SENTINEL && n < 10) { + p--; + n++; + } + + return p; + } + + if (p == pStart) { + // Don't allow test before pStart + return p; + } + + if(*p >= 0xDC00 && *p <= 0xDFFF && *(p-1) >= 0xD800 && *(p-1) <= 0xDBFF) { + return p-1; + } + + // Look for a UC_SENTINEL to jump to + // note: If we are pointing to the middle of a UC_SENTINEL CODE_x, then we won't treat it as valid, + // and will just go back a single wchar + q = p; + for (int i = 0; i < CODE__SIZE_MAX && q >= pStart; i++, q--) { + // *q == UC_SENTINEL && *(q + 1) is within CODE__SIZE && next CODE_ right of UC_SENTINEL ( looked up in CODE__SIZE+1) has value i + if (*q == UC_SENTINEL && *(q + 1) <= CODE_LASTCODE && CODE__SIZE[*(q + 1)] + 1 == i) + return q; + } + + return p; +} + +int xstrlen_ignoreifopt(PKMX_WCHAR p) +{ + int i; + for(i = 0; *p; i++, p=incxstr(p)) + { + if(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) i--; // I3432 + } + return i; +} + +int xstrlen(PKMX_WCHAR p) +{ + int i; + for(i = 0; *p; i++, p=incxstr(p)); + return i; +} + +int xstrpos(PKMX_WCHAR p1, PKMX_WCHAR p) +{ + int i; + for(i = 0; p < p1; p = incxstr(p), i++); + return i; +} + +PKMX_WCHAR xstrchr(PKMX_WCHAR buf, PKMX_WCHAR chr) +{ + for(PKMX_WCHAR q = incxstr(buf); *buf; buf = q, q = incxstr(buf)) + if(!u16ncmp(buf, chr, (intptr_t)(q-buf))) + return buf; + return NULL; +} + +const int CODE__SIZE[] = { + -1, // undefined 0x00 + 1, // CODE_ANY 0x01 + 2, // CODE_INDEX 0x02 + 0, // CODE_CONTEXT 0x03 + 0, // CODE_NUL 0x04 + 1, // CODE_USE 0x05 + 0, // CODE_RETURN 0x06 + 0, // CODE_BEEP 0x07 + 1, // CODE_DEADKEY 0x08 + -1, // unused 0x09 + 2, // CODE_EXTENDED 0x0A + -1, // CODE_EXTENDEDEND 0x0B (unused) + 1, // CODE_SWITCH 0x0C + -1, // CODE_KEY 0x0D (never used) + 0, // CODE_CLEARCONTEXT 0x0E + 1, // CODE_CALL 0x0F + -1, // UC_SENTINEL_EXTENDEDEND 0x10 (not valid with UC_SENTINEL) + 1, // CODE_CONTEXTEX 0x11 + 1, // CODE_NOTANY 0x12 + 2, // CODE_SETOPT 0x13 + 3, // CODE_IFOPT 0x14 + 1, // CODE_SAVEOPT 0x15 + 1, // CODE_RESETOPT 0x16 + 3, // CODE_IFSYSTEMSTORE 0x17 + 2 // CODE_SETSYSTEMSTORE 0x18 +}; + +// Ensure that all CODE_### sizes are defined +static_assert(sizeof(CODE__SIZE) / sizeof(CODE__SIZE[0]) == (CODE_LASTCODE + 1), "Size of array CODE__SIZE not correct"); diff --git a/developer/src/kmcmplib/src/xstring.h b/developer/src/kmcmplib/src/xstring.h new file mode 100644 index 0000000000000000000000000000000000000000..ae08cfe64a2ef22a941fe67c5ba1047551735cef --- /dev/null +++ b/developer/src/kmcmplib/src/xstring.h @@ -0,0 +1,15 @@ +// TODO: merge with xstring.h from common +#pragma once +#ifndef XSTRING_H +#define SXTRING_H +#include + +KMX_UCHAR* incxstr(KMX_UCHAR* p); +KMX_UCHAR* decxstr(KMX_UCHAR* p, KMX_UCHAR* pStart); +int xstrlen(KMX_UCHAR* p); +int xstrlen_ignoreifopt(KMX_UCHAR* p); +int xstrpos(KMX_UCHAR* p1, KMX_UCHAR* p); +KMX_UCHAR* xstrchr(KMX_UCHAR* buf, KMX_UCHAR* chr); +int xchrcmp(KMX_UCHAR* ch1, KMX_UCHAR* ch2); + +#endif // XSTRING_H diff --git a/developer/src/kmcmplib/subprojects/.gitignore b/developer/src/kmcmplib/subprojects/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..99479aa952cd339f9d98c7bab6dd21ad9700355e --- /dev/null +++ b/developer/src/kmcmplib/subprojects/.gitignore @@ -0,0 +1,4 @@ +/icu +/*.zip +/*.tgz +/packagecache diff --git a/developer/src/kmcmplib/subprojects/README.md b/developer/src/kmcmplib/subprojects/README.md new file mode 100644 index 0000000000000000000000000000000000000000..188cbe6c26257ab3d9338f71971a954ac7cb60de --- /dev/null +++ b/developer/src/kmcmplib/subprojects/README.md @@ -0,0 +1,11 @@ +# developer/src/kmcmplib/subprojects/README.md + +## What's this? What _is_ this? + +- These are subprojects accessible to the `kmcmplib` meson project. + +## icu + +- ICU is used for the UnicodeSet parser, only. +- Patches for icu (i.e. meson build files) are in [`packagefiles/icu`](./packagefiles/icu/) +- ICU is lightly patched to remove support for UnicodeSet properties. diff --git a/developer/src/kmcmplib/subprojects/icu-for-uset.wrap b/developer/src/kmcmplib/subprojects/icu-for-uset.wrap new file mode 100644 index 0000000000000000000000000000000000000000..7c2dcddb4620473bac382807936fd56505b68eb7 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/icu-for-uset.wrap @@ -0,0 +1,13 @@ +# ICU, only for UnicodeSet + +[wrap-file] +directory = icu +source_url = https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.tgz +source_filename = icu4c-73_1-src.tgz +source_hash = a457431de164b4aa7eca00ed134d00dfbf88a77c6986a10ae7774fc076bb8c45 +patch_directory = icu + +# Based on: wrapdb_version = 72.1-3 + +[provide] +icu-uc = icuuc_dep diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.icu b/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.icu new file mode 100644 index 0000000000000000000000000000000000000000..22472dc2ec219cd7d4d97a66e9a3cce6e2a4acc1 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.icu @@ -0,0 +1,519 @@ +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +---------------------------------------------------------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +---------------------------------------------------------------------- + +ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +---------------------------------------------------------------------- + +Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # Redistributions in binary form must reproduce the above + # copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided with + # the distribution. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +---------------------------------------------------------------------- + +Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 2016 and later: Unicode, Inc. and others. + # License & terms of use: http://www.unicode.org/copyright.html + # Copyright (c) 2015 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. Redistributions in binary + # form must reproduce the above copyright notice, this list of conditions and + # the following disclaimer in the documentation and/or other materials + # provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. Redistributions in binary form must reproduce the + # above copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided + # with the distribution. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +---------------------------------------------------------------------- + +Google double-conversion + +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- + +File: aclocal.m4 (only for ICU4C) +Section: pkg.m4 - Macros to locate and utilise pkg-config. + + +Copyright © 2004 Scott James Remnant . +Copyright © 2012-2015 Dan Nicholson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: config.guess (only for ICU4C) + + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. This Exception is an additional permission under section 7 +of the GNU General Public License, version 3 ("GPLv3"). + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: install-sh (only for ICU4C) + + +Copyright 1991 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.meson b/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.meson new file mode 100644 index 0000000000000000000000000000000000000000..b59833dedbe2e4a84304431f4acca5dc4599cec7 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/LICENSE.meson @@ -0,0 +1,19 @@ +Copyright (c) 2021 The Meson development team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/README-keyman.md b/developer/src/kmcmplib/subprojects/packagefiles/icu/README-keyman.md new file mode 100644 index 0000000000000000000000000000000000000000..50d90c5f1b223aec565dc76ab50f74dcee096c98 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/README-keyman.md @@ -0,0 +1,12 @@ +# ICU4C, for kmpcmplib + +Enclosed herein find: +- a subset of [ICU4C 73.1](https://icu.unicode.org), overlaid with +- a subset of [mesonbuild/wrapdb ](https://github.com/mesonbuild/wrapdb) build files + +This subset is used to provide UnicodeSet operations to the developer side. + +## Licenses + +- ICU4C [LICENSE.icu](./LICENSE.icu) - SPDX: `Unicode-DFS-2016` +- Wrapdb [LICENSE.meson](./LICENSE.meson) - SPDX: `MIT` diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/meson.build b/developer/src/kmcmplib/subprojects/packagefiles/icu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..dd8a2c4c3ae0c4e9451c020a0163a1a744c393c8 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/meson.build @@ -0,0 +1,31 @@ +project( + 'icu', + 'c', + 'cpp', + version: '73.1', + meson_version: '>=0.57.0', + default_options: [ 'cpp_std=c++17' ], +) + +U_ICU_VERSION = meson.project_version() +PACKAGE_ICU_DESCRIPTION = 'International Components for Unicode' +PACKAGE_ICU_URL = 'http://icu-project.org' + +if host_machine.system() == 'windows' + add_project_arguments('-DU_PLATFORM_USES_ONLY_WIN32_API', language: 'cpp') + add_project_arguments('-DWIN32', '-DWIN64', '-D_MBCS', language: 'cpp') + add_project_arguments('-DU_PLATFORM_USES_ONLY_WIN32_API', language: 'c') + add_project_arguments('-DWIN32', '-DWIN64', '-D_MBCS', language: 'c') +endif + +usage_args = [ + '-DU_STATIC_IMPLEMENTATION', + '-DU_ATTRIBUTE_DEPRECATED=', + '-DUCONFIG_USE_LOCAL=1', + '-DU_NOEXCEPT=' +] + +library_prefix = '' +library_suffix = '' + +subdir('source') diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/meson.build b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..efde33baf3881600a0d067aa22f80caf0d1afe88 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/meson.build @@ -0,0 +1,340 @@ +sources = files( + 'appendable.cpp', + 'bmpset.cpp', + # 'brkeng.cpp', + # 'brkiter.cpp', + 'bytesinkutil.cpp', + 'bytestream.cpp', + 'bytestrie.cpp', + 'bytestriebuilder.cpp', + 'bytestrieiterator.cpp', + # 'caniter.cpp', + 'characterproperties.cpp', + 'chariter.cpp', + 'charstr.cpp', + 'cmemory.cpp', + 'cstr.cpp', + 'cstring.cpp', + # 'cwchar.cpp', + # 'dictbe.cpp', + # 'dictionarydata.cpp', + 'dtintrv.cpp', + 'edits.cpp', + 'emojiprops.cpp', + 'errorcode.cpp', + # 'filteredbrk.cpp', + # 'filterednormalizer2.cpp', + 'icudataver.cpp', + # 'icuplug.cpp', + # 'loadednormalizer2impl.cpp', + 'localebuilder.cpp', + 'locavailable.cpp', + 'locbased.cpp', + 'locdispnames.cpp', + # 'locdspnm.cpp', + 'locid.cpp', + 'loclikely.cpp', + 'locmap.cpp', + 'locresdata.cpp', + # 'locutil.cpp', + # 'lstmbe.cpp', + # 'messagepattern.cpp', + # 'normalizer2.cpp', + # 'normalizer2impl.cpp', + # 'normlzr.cpp', + 'parsepos.cpp', + 'patternprops.cpp', + 'propname.cpp', + 'propsvec.cpp', + # 'punycode.cpp', + 'putil.cpp', + # 'rbbi.cpp', + # 'rbbi_cache.cpp', + # 'rbbidata.cpp', + # 'rbbinode.cpp', + # 'rbbirb.cpp', + # 'rbbiscan.cpp', + # 'rbbisetb.cpp', + # 'rbbistbl.cpp', + # 'rbbitblb.cpp', + 'resbund.cpp', + 'resbund_cnv.cpp', + 'resource.cpp', + 'ruleiter.cpp', + 'schriter.cpp', + # 'serv.cpp', + # 'servlk.cpp', + # 'servlkf.cpp', + # 'servls.cpp', + # 'servnotf.cpp', + # 'servrbf.cpp', + # 'servslkf.cpp', + 'sharedobject.cpp', + 'simpleformatter.cpp', + # 'static_unicode_sets.cpp', + 'stringpiece.cpp', + 'stringtriebuilder.cpp', + 'uarrsort.cpp', + 'ubidi.cpp', + 'ubidi_props.cpp', + 'ubidiln.cpp', + 'ubidiwrt.cpp', + # 'ubrk.cpp', + 'ucase.cpp', + 'ucasemap.cpp', + # 'ucasemap_titlecase_brkiter.cpp', + 'ucat.cpp', + # 'uchar.cpp', + 'ucharstrie.cpp', + 'ucharstriebuilder.cpp', + 'ucharstrieiterator.cpp', + 'uchriter.cpp', + 'ucln_cmn.cpp', + 'ucmndata.cpp', + 'ucnv.cpp', + # 'ucnv2022.cpp', + 'ucnv_bld.cpp', + 'ucnv_cb.cpp', + 'ucnv_cnv.cpp', + # 'ucnv_ct.cpp', + 'ucnv_err.cpp', + # 'ucnv_ext.cpp', + 'ucnv_io.cpp', + # 'ucnv_lmb.cpp', + 'ucnv_set.cpp', + 'ucnv_u16.cpp', + 'ucnv_u32.cpp', + 'ucnv_u7.cpp', + 'ucnv_u8.cpp', + 'ucnvbocu.cpp', + 'ucnvdisp.cpp', + # 'ucnvhz.cpp', + # 'ucnvisci.cpp', + 'ucnvlat1.cpp', + # 'ucnvmbcs.cpp', + 'ucnvscsu.cpp', + 'ucnvsel.cpp', + # 'ucol_swp.cpp', + 'ucptrie.cpp', + # 'ucurr.cpp', + 'udata.cpp', + 'udatamem.cpp', + 'udataswp.cpp', + 'uenum.cpp', + 'uhash.cpp', + 'uhash_us.cpp', + # 'uidna.cpp', + 'uinit.cpp', + 'uinvchar.cpp', + 'uiter.cpp', + 'ulist.cpp', + 'uloc.cpp', + 'uloc_keytype.cpp', + 'uloc_tag.cpp', + 'umapfile.cpp', + 'umath.cpp', + 'umutablecptrie.cpp', + 'umutex.cpp', + 'unames.cpp', + 'unifiedcache.cpp', + 'unifilt.cpp', + 'unifunct.cpp', + 'uniset.cpp', + 'uniset_closure.cpp', + 'uniset_props.cpp', + 'unisetspan.cpp', + 'unistr.cpp', + 'unistr_case.cpp', + 'unistr_case_locale.cpp', + 'unistr_cnv.cpp', + 'unistr_props.cpp', + # 'unistr_titlecase_brkiter.cpp', + # 'unorm.cpp', + # 'unormcmp.cpp', + 'uobject.cpp', + 'uprops.cpp', + 'ures_cnv.cpp', + 'uresbund.cpp', + 'uresdata.cpp', + 'usc_impl.cpp', + 'uscript.cpp', + 'uscript_props.cpp', + 'uset.cpp', + 'uset_props.cpp', + 'usetiter.cpp', + 'ushape.cpp', + # 'usprep.cpp', + 'ustack.cpp', + 'ustr_cnv.cpp', + # 'ustr_titlecase_brkiter.cpp', + 'ustr_wcs.cpp', + 'ustrcase.cpp', + 'ustrcase_locale.cpp', + 'ustrenum.cpp', + 'ustrfmt.cpp', + 'ustring.cpp', + 'ustrtrns.cpp', + 'utext.cpp', + 'utf_impl.cpp', + 'util.cpp', + 'util_props.cpp', + 'utrace.cpp', + 'utrie.cpp', + 'utrie2.cpp', + 'utrie2_builder.cpp', + 'utrie_swap.cpp', + # 'uts46.cpp', + 'utypes.cpp', + 'uvector.cpp', + 'uvectr32.cpp', + 'uvectr64.cpp', + 'wintz.cpp', +) + +icuuc_name = '@0@icuuc@1@'.format(library_prefix, library_suffix) + +common_lib = static_library( + icuuc_name, + sources, + include_directories: [incdir,'../..'], + c_args: ['-DU_COMMON_IMPLEMENTATION'] + usage_args, + cpp_args: ['-DU_COMMON_IMPLEMENTATION'] + usage_args, + link_with: stubdata_lib, + # dependencies: dl_dep, + # version: U_ICU_VERSION, + # install: true, +) + + +uconfig = configuration_data() + +uconfig.set('U_ENABLE_DYLOAD', 0) # no DLL +uconfig.set('U_CHECK_DYLOAD', 0) # no DLL +uconfig.set('UCONFIG_NO_FILE_IO', 1) +uconfig.set('UCONFIG_NO_LEGACY_CONVERSION', 1) # turn off file based codepage conversion +uconfig.set('UCONFIG_NO_NORMALIZATION', 1) # TODO-LDML: may want this +uconfig.set('UCONFIG_NO_BREAK_ITERATION', 1) # TODO-LDML: may want this +uconfig.set('UCONFIG_NO_IDNA', 1) +uconfig.set('UCONFIG_NO_COLLATION', 1) +uconfig.set('UCONFIG_NO_FORMATTING', 1) +uconfig.set('UCONFIG_NO_TRANSLITERATION', 1) +uconfig.set('UCONFIG_NO_REGULAR_EXPRESSIONS', 1) # TODO-LDML: Will probably want this for transforms #7375 +uconfig.set('UCONFIG_NO_SERVICE', 1) + +# This next one is custom to this patch: +uconfig.set('UCONFIG_NO_PROPERTIES', 1) + +uconfig_local = configure_file( + configuration : uconfig, + output : 'uconfig_local.h', +) + + +icuuc_dep = declare_dependency( + link_with: common_lib, + compile_args: usage_args, + include_directories: incdir, + # dependencies: stubdata_dep, +) + +headers = files( + 'unicode/appendable.h', + 'unicode/brkiter.h', + 'unicode/bytestream.h', + 'unicode/bytestrie.h', + 'unicode/bytestriebuilder.h', + 'unicode/caniter.h', + 'unicode/casemap.h', + 'unicode/char16ptr.h', + 'unicode/chariter.h', + 'unicode/dbbi.h', + 'unicode/docmain.h', + 'unicode/dtintrv.h', + 'unicode/edits.h', + 'unicode/enumset.h', + 'unicode/errorcode.h', + 'unicode/filteredbrk.h', + 'unicode/icudataver.h', + 'unicode/icuplug.h', + 'unicode/idna.h', + 'unicode/localebuilder.h', + 'unicode/localematcher.h', + 'unicode/localpointer.h', + 'unicode/locdspnm.h', + 'unicode/locid.h', + 'unicode/messagepattern.h', + 'unicode/normalizer2.h', + 'unicode/normlzr.h', + 'unicode/parseerr.h', + 'unicode/parsepos.h', + 'unicode/platform.h', + 'unicode/ptypes.h', + 'unicode/putil.h', + 'unicode/rbbi.h', + 'unicode/rep.h', + 'unicode/resbund.h', + 'unicode/schriter.h', + 'unicode/simpleformatter.h', + 'unicode/std_string.h', + 'unicode/strenum.h', + 'unicode/stringoptions.h', + 'unicode/stringpiece.h', + 'unicode/stringtriebuilder.h', + 'unicode/symtable.h', + 'unicode/ubidi.h', + 'unicode/ubiditransform.h', + 'unicode/ubrk.h', + 'unicode/ucasemap.h', + 'unicode/ucat.h', + 'unicode/uchar.h', + 'unicode/ucharstrie.h', + 'unicode/ucharstriebuilder.h', + 'unicode/uchriter.h', + 'unicode/uclean.h', + 'unicode/ucnv.h', + 'unicode/ucnv_cb.h', + 'unicode/ucnv_err.h', + 'unicode/ucnvsel.h', + 'unicode/uconfig.h', + 'unicode/ucpmap.h', + 'unicode/ucptrie.h', + 'unicode/ucurr.h', + 'unicode/udata.h', + 'unicode/udisplaycontext.h', + 'unicode/uenum.h', + 'unicode/uidna.h', + 'unicode/uiter.h', + 'unicode/uldnames.h', + 'unicode/uloc.h', + 'unicode/umachine.h', + 'unicode/umisc.h', + 'unicode/umutablecptrie.h', + 'unicode/unifilt.h', + 'unicode/unifunct.h', + 'unicode/unimatch.h', + 'unicode/uniset.h', + 'unicode/unistr.h', + 'unicode/unorm.h', + 'unicode/unorm2.h', + 'unicode/uobject.h', + 'unicode/urename.h', + 'unicode/urep.h', + 'unicode/ures.h', + 'unicode/uscript.h', + 'unicode/uset.h', + 'unicode/usetiter.h', + 'unicode/ushape.h', + 'unicode/usprep.h', + 'unicode/ustring.h', + 'unicode/ustringtrie.h', + 'unicode/utext.h', + 'unicode/utf.h', + 'unicode/utf16.h', + 'unicode/utf32.h', + 'unicode/utf8.h', + 'unicode/utf_old.h', + 'unicode/utrace.h', + 'unicode/utypes.h', + 'unicode/uvernum.h', + 'unicode/uversion.h', +) diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uniset_props.cpp b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uniset_props.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e960c0e9654bd9a72b38d625c2cae724f3fe2ab6 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uniset_props.cpp @@ -0,0 +1,1161 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uniset_props.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug25 +* created by: Markus W. Scherer +* +* Character property dependent functions moved here from uniset.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/uniset.h" +#include "unicode/parsepos.h" +#if !defined(UCONFIG_NO_PROPERTIES) +#include "unicode/uchar.h" +#include "unicode/uscript.h" +#endif +#include "unicode/symtable.h" +#include "unicode/uset.h" +#include "unicode/locid.h" +#include "unicode/brkiter.h" +#include "uset_imp.h" +#include "ruleiter.h" +#include "cmemory.h" +#include "ucln_cmn.h" +#include "util.h" +#if !defined(UCONFIG_NO_PROPERTIES) +#include "uvector.h" +#include "uprops.h" +#include "propname.h" +#include "normalizer2impl.h" +#endif +#include "uinvchar.h" +#if !defined(UCONFIG_NO_PROPERTIES) +#include "uprops.h" +#endif +#include "charstr.h" +#include "cstring.h" +#include "mutex.h" +#include "umutex.h" +#include "uassert.h" +#include "hash.h" + +U_NAMESPACE_USE + +// Special property set IDs +#if !defined(UCONFIG_NO_PROPERTIES) +static const char ANY[] = "ANY"; // [\u0000-\U0010FFFF] +static const char ASCII[] = "ASCII"; // [\u0000-\u007F] +static const char ASSIGNED[] = "Assigned"; // [:^Cn:] +#endif + + +// Unicode name property alias +#define NAME_PROP "na" +#define NAME_PROP_LENGTH 2 + +// Cached sets ------------------------------------------------------------- *** + +U_CDECL_BEGIN +static UBool U_CALLCONV uset_cleanup(); + +static UnicodeSet *uni32Singleton; +static icu::UInitOnce uni32InitOnce {}; + +/** + * Cleanup function for UnicodeSet + */ +static UBool U_CALLCONV uset_cleanup() { + delete uni32Singleton; + uni32Singleton = nullptr; + uni32InitOnce.reset(); + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +namespace { + +// Cache some sets for other services -------------------------------------- *** +void U_CALLCONV createUni32Set(UErrorCode &errorCode) { + U_ASSERT(uni32Singleton == nullptr); + uni32Singleton = new UnicodeSet(UNICODE_STRING_SIMPLE("[:age=3.2:]"), errorCode); + if(uni32Singleton==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else { + uni32Singleton->freeze(); + } + ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup); +} + + +U_CFUNC UnicodeSet * +uniset_getUnicode32Instance(UErrorCode &errorCode) { + umtx_initOnce(uni32InitOnce, &createUni32Set, errorCode); + return uni32Singleton; +} + +// helper functions for matching of pattern syntax pieces ------------------ *** +// these functions are parallel to the PERL_OPEN etc. strings above + +// using these functions is not only faster than UnicodeString::compare() and +// caseCompare(), but they also make UnicodeSet work for simple patterns when +// no Unicode properties data is available - when caseCompare() fails + +static inline UBool +isPerlOpen(const UnicodeString &pattern, int32_t pos) { + char16_t c; + return pattern.charAt(pos)==u'\\' && ((c=pattern.charAt(pos+1))==u'p' || c==u'P'); +} + +/*static inline UBool +isPerlClose(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'}'; +}*/ + +static inline UBool +isNameOpen(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'\\' && pattern.charAt(pos+1)==u'N'; +} + +static inline UBool +isPOSIXOpen(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'[' && pattern.charAt(pos+1)==u':'; +} + +/*static inline UBool +isPOSIXClose(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u':' && pattern.charAt(pos+1)==u']'; +}*/ + +// TODO memory debugging provided inside uniset.cpp +// could be made available here but probably obsolete with use of modern +// memory leak checker tools +#define _dbgct(me) + +} // namespace + +//---------------------------------------------------------------- +// Constructors &c +//---------------------------------------------------------------- + +/** + * Constructs a set from the given pattern, optionally ignoring + * white space. See the class description for the syntax of the + * pattern language. + * @param pattern a string specifying what characters are in the set + */ +UnicodeSet::UnicodeSet(const UnicodeString& pattern, + UErrorCode& status) { + applyPattern(pattern, status); + _dbgct(this); +} + +//---------------------------------------------------------------- +// Public API +//---------------------------------------------------------------- + +UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, + UErrorCode& status) { + // Equivalent to + // return applyPattern(pattern, USET_IGNORE_SPACE, nullptr, status); + // but without dependency on closeOver(). + ParsePosition pos(0); + applyPatternIgnoreSpace(pattern, pos, nullptr, status); + if (U_FAILURE(status)) return *this; + + int32_t i = pos.getIndex(); + // Skip over trailing whitespace + ICU_Utility::skipWhitespace(pattern, i, true); + if (i != pattern.length()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; +} + +void +UnicodeSet::applyPatternIgnoreSpace(const UnicodeString& pattern, + ParsePosition& pos, + const SymbolTable* symbols, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (isFrozen()) { + status = U_NO_WRITE_PERMISSION; + return; + } + // Need to build the pattern in a temporary string because + // _applyPattern calls add() etc., which set pat to empty. + UnicodeString rebuiltPat; + RuleCharacterIterator chars(pattern, symbols, pos); + applyPattern(chars, symbols, rebuiltPat, USET_IGNORE_SPACE, nullptr, 0, status); + if (U_FAILURE(status)) return; + if (chars.inVariable()) { + // syntaxError(chars, "Extra chars in variable value"); + status = U_MALFORMED_SET; + return; + } + setPattern(rebuiltPat); +} + +/** + * Return true if the given position, in the given pattern, appears + * to be the start of a UnicodeSet pattern. + */ +UBool UnicodeSet::resemblesPattern(const UnicodeString& pattern, int32_t pos) { + return ((pos+1) < pattern.length() && + pattern.charAt(pos) == (char16_t)91/*[*/) || + resemblesPropertyPattern(pattern, pos); +} + +//---------------------------------------------------------------- +// Implementation: Pattern parsing +//---------------------------------------------------------------- + +namespace { + +/** + * A small all-inline class to manage a UnicodeSet pointer. Add + * operator->() etc. as needed. + */ +class UnicodeSetPointer { + UnicodeSet* p; +public: + inline UnicodeSetPointer() : p(0) {} + inline ~UnicodeSetPointer() { delete p; } + inline UnicodeSet* pointer() { return p; } + inline UBool allocate() { + if (p == 0) { + p = new UnicodeSet(); + } + return p != 0; + } +}; + +constexpr int32_t MAX_DEPTH = 100; + +} // namespace + +/** + * Parse the pattern from the given RuleCharacterIterator. The + * iterator is advanced over the parsed pattern. + * @param chars iterator over the pattern characters. Upon return + * it will be advanced to the first character after the parsed + * pattern, or the end of the iteration if all characters are + * parsed. + * @param symbols symbol table to use to parse and dereference + * variables, or null if none. + * @param rebuiltPat the pattern that was parsed, rebuilt or + * copied from the input pattern, as appropriate. + * @param options a bit mask of zero or more of the following: + * IGNORE_SPACE, CASE. + */ +void UnicodeSet::applyPattern(RuleCharacterIterator& chars, + const SymbolTable* symbols, + UnicodeString& rebuiltPat, + uint32_t options, + UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute), + int32_t depth, + UErrorCode& ec) { + if (U_FAILURE(ec)) return; + if (depth > MAX_DEPTH) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Syntax characters: [ ] ^ - & { } + + // Recognized special forms for chars, sets: c-c s-s s&s + + int32_t opts = RuleCharacterIterator::PARSE_VARIABLES | + RuleCharacterIterator::PARSE_ESCAPES; + if ((options & USET_IGNORE_SPACE) != 0) { + opts |= RuleCharacterIterator::SKIP_WHITESPACE; + } + + UnicodeString patLocal, buf; + UBool usePat = false; + UnicodeSetPointer scratch; + RuleCharacterIterator::Pos backup; + + // mode: 0=before [, 1=between [...], 2=after ] + // lastItem: 0=none, 1=char, 2=set + int8_t lastItem = 0, mode = 0; + UChar32 lastChar = 0; + char16_t op = 0; + + UBool invert = false; + + clear(); + + while (mode != 2 && !chars.atEnd()) { + U_ASSERT((lastItem == 0 && op == 0) || + (lastItem == 1 && (op == 0 || op == u'-')) || + (lastItem == 2 && (op == 0 || op == u'-' || op == u'&'))); + + UChar32 c = 0; + UBool literal = false; + UnicodeSet* nested = 0; // alias - do not delete + + // -------- Check for property pattern + + // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed + int8_t setMode = 0; + if (resemblesPropertyPattern(chars, opts)) { + setMode = 2; + } + + // -------- Parse '[' of opening delimiter OR nested set. + // If there is a nested set, use `setMode' to define how + // the set should be parsed. If the '[' is part of the + // opening delimiter for this pattern, parse special + // strings "[", "[^", "[-", and "[^-". Check for stand-in + // characters representing a nested set in the symbol + // table. + + else { + // Prepare to backup if necessary + chars.getPos(backup); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + + if (c == u'[' && !literal) { + if (mode == 1) { + chars.setPos(backup); // backup + setMode = 1; + } else { + // Handle opening '[' delimiter + mode = 1; + patLocal.append(u'['); + chars.getPos(backup); // prepare to backup + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u'^' && !literal) { + invert = true; + patLocal.append(u'^'); + chars.getPos(backup); // prepare to backup + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + } + // Fall through to handle special leading '-'; + // otherwise restart loop for nested [], \p{}, etc. + if (c == u'-') { + literal = true; + // Fall through to handle literal '-' below + } else { + chars.setPos(backup); // backup + continue; + } + } + } else if (symbols != 0) { + const UnicodeFunctor *m = symbols->lookupMatcher(c); + if (m != 0) { + const UnicodeSet *ms = dynamic_cast(m); + if (ms == nullptr) { + ec = U_MALFORMED_SET; + return; + } + // casting away const, but `nested' won't be modified + // (important not to modify stored set) + nested = const_cast(ms); + setMode = 3; + } + } + } + + // -------- Handle a nested set. This either is inline in + // the pattern or represented by a stand-in that has + // previously been parsed and was looked up in the symbol + // table. + + if (setMode != 0) { + if (lastItem == 1) { + if (op != 0) { + // syntaxError(chars, "Char expected after operator"); + ec = U_MALFORMED_SET; + return; + } + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + lastItem = 0; + op = 0; + } + + if (op == u'-' || op == u'&') { + patLocal.append(op); + } + + if (nested == 0) { + // lazy allocation + if (!scratch.allocate()) { + ec = U_MEMORY_ALLOCATION_ERROR; + return; + } + nested = scratch.pointer(); + } + switch (setMode) { + case 1: + nested->applyPattern(chars, symbols, patLocal, options, caseClosure, depth + 1, ec); + break; + case 2: + chars.skipIgnored(opts); + nested->applyPropertyPattern(chars, patLocal, ec); + if (U_FAILURE(ec)) return; + break; + case 3: // `nested' already parsed + nested->_toPattern(patLocal, false); + break; + } + + usePat = true; + + if (mode == 0) { + // Entire pattern is a category; leave parse loop + *this = *nested; + mode = 2; + break; + } + + switch (op) { + case u'-': + removeAll(*nested); + break; + case u'&': + retainAll(*nested); + break; + case 0: + addAll(*nested); + break; + } + + op = 0; + lastItem = 2; + + continue; + } + + if (mode == 0) { + // syntaxError(chars, "Missing '['"); + ec = U_MALFORMED_SET; + return; + } + + // -------- Parse special (syntax) characters. If the + // current character is not special, or if it is escaped, + // then fall through and handle it below. + + if (!literal) { + switch (c) { + case u']': + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + // Treat final trailing '-' as a literal + if (op == u'-') { + add(op, op); + patLocal.append(op); + } else if (op == u'&') { + // syntaxError(chars, "Trailing '&'"); + ec = U_MALFORMED_SET; + return; + } + patLocal.append(u']'); + mode = 2; + continue; + case u'-': + if (op == 0) { + if (lastItem != 0) { + op = (char16_t) c; + continue; + } else { + // Treat final trailing '-' as a literal + add(c, c); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u']' && !literal) { + patLocal.append(u"-]", 2); + mode = 2; + continue; + } + } + } + // syntaxError(chars, "'-' not after char or set"); + ec = U_MALFORMED_SET; + return; + case u'&': + if (lastItem == 2 && op == 0) { + op = (char16_t) c; + continue; + } + // syntaxError(chars, "'&' not after set"); + ec = U_MALFORMED_SET; + return; + case u'^': + // syntaxError(chars, "'^' not after '['"); + ec = U_MALFORMED_SET; + return; + case u'{': + if (op != 0) { + // syntaxError(chars, "Missing operand after operator"); + ec = U_MALFORMED_SET; + return; + } + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + lastItem = 0; + buf.truncate(0); + { + UBool ok = false; + while (!chars.atEnd()) { + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u'}' && !literal) { + ok = true; + break; + } + buf.append(c); + } + if (!ok) { + // syntaxError(chars, "Invalid multicharacter string"); + ec = U_MALFORMED_SET; + return; + } + } + // We have new string. Add it to set and continue; + // we don't need to drop through to the further + // processing + add(buf); + patLocal.append(u'{'); + _appendToPat(patLocal, buf, false); + patLocal.append(u'}'); + continue; + case SymbolTable::SYMBOL_REF: + // symbols nosymbols + // [a-$] error error (ambiguous) + // [a$] anchor anchor + // [a-$x] var "x"* literal '$' + // [a-$.] error literal '$' + // *We won't get here in the case of var "x" + { + chars.getPos(backup); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + UBool anchor = (c == u']' && !literal); + if (symbols == 0 && !anchor) { + c = SymbolTable::SYMBOL_REF; + chars.setPos(backup); + break; // literal '$' + } + if (anchor && op == 0) { + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + add(U_ETHER); + usePat = true; + patLocal.append((char16_t) SymbolTable::SYMBOL_REF); + patLocal.append(u']'); + mode = 2; + continue; + } + // syntaxError(chars, "Unquoted '$'"); + ec = U_MALFORMED_SET; + return; + } + default: + break; + } + } + + // -------- Parse literal characters. This includes both + // escaped chars ("\u4E01") and non-syntax characters + // ("a"). + + switch (lastItem) { + case 0: + lastItem = 1; + lastChar = c; + break; + case 1: + if (op == u'-') { + if (lastChar >= c) { + // Don't allow redundant (a-a) or empty (b-a) ranges; + // these are most likely typos. + // syntaxError(chars, "Invalid range"); + ec = U_MALFORMED_SET; + return; + } + add(lastChar, c); + _appendToPat(patLocal, lastChar, false); + patLocal.append(op); + _appendToPat(patLocal, c, false); + lastItem = 0; + op = 0; + } else { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + lastChar = c; + } + break; + case 2: + if (op != 0) { + // syntaxError(chars, "Set expected after operator"); + ec = U_MALFORMED_SET; + return; + } + lastChar = c; + lastItem = 1; + break; + } + } + + if (mode != 2) { + // syntaxError(chars, "Missing ']'"); + ec = U_MALFORMED_SET; + return; + } + + chars.skipIgnored(opts); + + /** + * Handle global flags (invert, case insensitivity). If this + * pattern should be compiled case-insensitive, then we need + * to close over case BEFORE COMPLEMENTING. This makes + * patterns like /[^abc]/i work. + */ + if ((options & USET_CASE_MASK) != 0) { + (this->*caseClosure)(options); + } + if (invert) { + complement().removeAllStrings(); // code point complement + } + + // Use the rebuilt pattern (patLocal) only if necessary. Prefer the + // generated pattern. + if (usePat) { + rebuiltPat.append(patLocal); + } else { + _generatePattern(rebuiltPat, false); + } + if (isBogus() && U_SUCCESS(ec)) { + // We likely ran out of memory. AHHH! + ec = U_MEMORY_ALLOCATION_ERROR; + } +} + +//---------------------------------------------------------------- +// Property set implementation +//---------------------------------------------------------------- + +namespace { + +#if !defined(UCONFIG_NO_PROPERTIES) +static UBool numericValueFilter(UChar32 ch, void* context) { + return u_getNumericValue(ch) == *(double*)context; +} + +static UBool generalCategoryMaskFilter(UChar32 ch, void* context) { + int32_t value = *(int32_t*)context; + return (U_GET_GC_MASK((UChar32) ch) & value) != 0; +} + +static UBool versionFilter(UChar32 ch, void* context) { + static const UVersionInfo none = { 0, 0, 0, 0 }; + UVersionInfo v; + u_charAge(ch, v); + UVersionInfo* version = (UVersionInfo*)context; + return uprv_memcmp(&v, &none, sizeof(v)) > 0 && uprv_memcmp(&v, version, sizeof(v)) <= 0; +} + +typedef struct { + UProperty prop; + int32_t value; +} IntPropertyContext; + +static UBool intPropertyFilter(UChar32 ch, void* context) { + IntPropertyContext* c = (IntPropertyContext*)context; + return u_getIntPropertyValue((UChar32) ch, c->prop) == c->value; +} + +static UBool scriptExtensionsFilter(UChar32 ch, void* context) { + return uscript_hasScript(ch, *(UScriptCode*)context); +} +#endif + +} // namespace + +/** + * Generic filter-based scanning code for UCD property UnicodeSets. + */ +void UnicodeSet::applyFilter(UnicodeSet::Filter filter, + void* context, + const UnicodeSet* inclusions, + UErrorCode &status) { + if (U_FAILURE(status)) return; + + // Logically, walk through all Unicode characters, noting the start + // and end of each range for which filter.contain(c) is + // true. Add each range to a set. + // + // To improve performance, use an inclusions set which + // encodes information about character ranges that are known + // to have identical properties. + // inclusions contains the first characters of + // same-value ranges for the given property. + + clear(); + + UChar32 startHasProperty = -1; + int32_t limitRange = inclusions->getRangeCount(); + + for (int j=0; jgetRangeStart(j); + UChar32 end = inclusions->getRangeEnd(j); + + // for all the code points in the range, process + for (UChar32 ch = start; ch <= end; ++ch) { + // only add to this UnicodeSet on inflection points -- + // where the hasProperty value changes to false + if ((*filter)(ch, context)) { + if (startHasProperty < 0) { + startHasProperty = ch; + } + } else if (startHasProperty >= 0) { + add(startHasProperty, ch-1); + startHasProperty = -1; + } + } + } + if (startHasProperty >= 0) { + add((UChar32)startHasProperty, (UChar32)0x10FFFF); + } + if (isBogus() && U_SUCCESS(status)) { + // We likely ran out of memory. AHHH! + status = U_MEMORY_ALLOCATION_ERROR; + } +} + +namespace { + +#if !defined(UCONFIG_NO_PROPERTIES) +static UBool mungeCharName(char* dst, const char* src, int32_t dstCapacity) { + /* Note: we use ' ' in compiler code page */ + int32_t j = 0; + char ch; + --dstCapacity; /* make room for term. zero */ + while ((ch = *src++) != 0) { + if (ch == ' ' && (j==0 || (j>0 && dst[j-1]==' '))) { + continue; + } + if (j >= dstCapacity) return false; + dst[j++] = ch; + } + if (j > 0 && dst[j-1] == ' ') --j; + dst[j] = 0; + return true; +} +#endif + +} // namespace + +//---------------------------------------------------------------- +// Property set API +//---------------------------------------------------------------- + +#define FAIL(ec) UPRV_BLOCK_MACRO_BEGIN { \ + ec=U_ILLEGAL_ARGUMENT_ERROR; \ + return *this; \ +} UPRV_BLOCK_MACRO_END + +UnicodeSet& +UnicodeSet::applyIntPropertyValue(UProperty prop, int32_t value, UErrorCode& ec) { + if (U_FAILURE(ec) || isFrozen()) { return *this; } +#if defined(UCONFIG_NO_PROPERTIES) + ec = U_MISSING_RESOURCE_ERROR; +#else + if (prop == UCHAR_GENERAL_CATEGORY_MASK) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + applyFilter(generalCategoryMaskFilter, &value, inclusions, ec); + } else if (prop == UCHAR_SCRIPT_EXTENSIONS) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + UScriptCode script = (UScriptCode)value; + applyFilter(scriptExtensionsFilter, &script, inclusions, ec); + } else if (0 <= prop && prop < UCHAR_BINARY_LIMIT) { + if (value == 0 || value == 1) { + const USet *set = u_getBinaryPropertySet(prop, &ec); + if (U_FAILURE(ec)) { return *this; } + copyFrom(*UnicodeSet::fromUSet(set), true); + if (value == 0) { + complement().removeAllStrings(); // code point complement + } + } else { + clear(); + } + } else if (UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + IntPropertyContext c = {prop, value}; + applyFilter(intPropertyFilter, &c, inclusions, ec); + } else { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } +#endif + return *this; +} + +UnicodeSet& +UnicodeSet::applyPropertyAlias(const UnicodeString& prop, + const UnicodeString& value, + UErrorCode& ec) { + if (U_FAILURE(ec) || isFrozen()) return *this; +#if !defined(UCONFIG_NO_PROPERTIES) + + // prop and value used to be converted to char * using the default + // converter instead of the invariant conversion. + // This should not be necessary because all Unicode property and value + // names use only invariant characters. + // If there are any variant characters, then we won't find them anyway. + // Checking first avoids assertion failures in the conversion. + if( !uprv_isInvariantUString(prop.getBuffer(), prop.length()) || + !uprv_isInvariantUString(value.getBuffer(), value.length()) + ) { + FAIL(ec); + } + CharString pname, vname; + pname.appendInvariantChars(prop, ec); + vname.appendInvariantChars(value, ec); + if (U_FAILURE(ec)) return *this; + + UProperty p; + int32_t v; + UBool invert = false; + + if (value.length() > 0) { + p = u_getPropertyEnum(pname.data()); + if (p == UCHAR_INVALID_CODE) FAIL(ec); + + // Treat gc as gcm + if (p == UCHAR_GENERAL_CATEGORY) { + p = UCHAR_GENERAL_CATEGORY_MASK; + } + + if ((p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) || + (p >= UCHAR_INT_START && p < UCHAR_INT_LIMIT) || + (p >= UCHAR_MASK_START && p < UCHAR_MASK_LIMIT)) { + v = u_getPropertyValueEnum(p, vname.data()); + if (v == UCHAR_INVALID_CODE) { + // Handle numeric CCC + if (p == UCHAR_CANONICAL_COMBINING_CLASS || + p == UCHAR_TRAIL_CANONICAL_COMBINING_CLASS || + p == UCHAR_LEAD_CANONICAL_COMBINING_CLASS) { + char* end; + double val = uprv_strtod(vname.data(), &end); + // Anything between 0 and 255 is valid even if unused. + // Cast double->int only after range check. + // We catch NaN here because comparing it with both 0 and 255 will be false + // (as are all comparisons with NaN). + if (*end != 0 || !(0 <= val && val <= 255) || + (v = (int32_t)val) != val) { + // non-integral value or outside 0..255, or trailing junk + FAIL(ec); + } + } else { + FAIL(ec); + } + } + } + + else { + + switch (p) { + case UCHAR_NUMERIC_VALUE: + { + char* end; + double val = uprv_strtod(vname.data(), &end); + if (*end != 0) { + FAIL(ec); + } + applyFilter(numericValueFilter, &val, + CharacterProperties::getInclusionsForProperty(p, ec), ec); + return *this; + } + case UCHAR_NAME: + { + // Must munge name, since u_charFromName() does not do + // 'loose' matching. + char buf[128]; // it suffices that this be > uprv_getMaxCharNameLength + if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); + UChar32 ch = u_charFromName(U_EXTENDED_CHAR_NAME, buf, &ec); + if (U_SUCCESS(ec)) { + clear(); + add(ch); + return *this; + } else { + FAIL(ec); + } + } + case UCHAR_UNICODE_1_NAME: + // ICU 49 deprecates the Unicode_1_Name property APIs. + FAIL(ec); + case UCHAR_AGE: + { + // Must munge name, since u_versionFromString() does not do + // 'loose' matching. + char buf[128]; + if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); + UVersionInfo version; + u_versionFromString(version, buf); + applyFilter(versionFilter, &version, + CharacterProperties::getInclusionsForProperty(p, ec), ec); + return *this; + } + case UCHAR_SCRIPT_EXTENSIONS: + v = u_getPropertyValueEnum(UCHAR_SCRIPT, vname.data()); + if (v == UCHAR_INVALID_CODE) { + FAIL(ec); + } + // fall through to calling applyIntPropertyValue() + break; + default: + // p is a non-binary, non-enumerated property that we + // don't support (yet). + FAIL(ec); + } + } + } + + else { + // value is empty. Interpret as General Category, Script, or + // Binary property. + p = UCHAR_GENERAL_CATEGORY_MASK; + v = u_getPropertyValueEnum(p, pname.data()); + if (v == UCHAR_INVALID_CODE) { + p = UCHAR_SCRIPT; + v = u_getPropertyValueEnum(p, pname.data()); + if (v == UCHAR_INVALID_CODE) { + p = u_getPropertyEnum(pname.data()); + if (p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) { + v = 1; + } else if (0 == uprv_comparePropertyNames(ANY, pname.data())) { + set(MIN_VALUE, MAX_VALUE); + return *this; + } else if (0 == uprv_comparePropertyNames(ASCII, pname.data())) { + set(0, 0x7F); + return *this; + } else if (0 == uprv_comparePropertyNames(ASSIGNED, pname.data())) { + // [:Assigned:]=[:^Cn:] + p = UCHAR_GENERAL_CATEGORY_MASK; + v = U_GC_CN_MASK; + invert = true; + } else { + FAIL(ec); + } + } + } + } + + applyIntPropertyValue(p, v, ec); + if(invert) { + complement().removeAllStrings(); // code point complement + } + + if (isBogus() && U_SUCCESS(ec)) { + // We likely ran out of memory. AHHH! + ec = U_MEMORY_ALLOCATION_ERROR; + } +#else + ec = U_MISSING_RESOURCE_ERROR; + return *this; +#endif + return *this; +} + +//---------------------------------------------------------------- +// Property set patterns +//---------------------------------------------------------------- + +/** + * Return true if the given position, in the given pattern, appears + * to be the start of a property set pattern. + */ +UBool UnicodeSet::resemblesPropertyPattern(const UnicodeString& pattern, + int32_t pos) { + // Patterns are at least 5 characters long + if ((pos+5) > pattern.length()) { + return false; + } + + // Look for an opening [:, [:^, \p, or \P + return isPOSIXOpen(pattern, pos) || isPerlOpen(pattern, pos) || isNameOpen(pattern, pos); +} + +/** + * Return true if the given iterator appears to point at a + * property pattern. Regardless of the result, return with the + * iterator unchanged. + * @param chars iterator over the pattern characters. Upon return + * it will be unchanged. + * @param iterOpts RuleCharacterIterator options + */ +UBool UnicodeSet::resemblesPropertyPattern(RuleCharacterIterator& chars, + int32_t iterOpts) { + // NOTE: literal will always be false, because we don't parse escapes. + UBool result = false, literal; + UErrorCode ec = U_ZERO_ERROR; + iterOpts &= ~RuleCharacterIterator::PARSE_ESCAPES; + RuleCharacterIterator::Pos pos; + chars.getPos(pos); + UChar32 c = chars.next(iterOpts, literal, ec); + if (c == u'[' || c == u'\\') { + UChar32 d = chars.next(iterOpts & ~RuleCharacterIterator::SKIP_WHITESPACE, + literal, ec); + result = (c == u'[') ? (d == u':') : + (d == u'N' || d == u'p' || d == u'P'); + } + chars.setPos(pos); + return result && U_SUCCESS(ec); +} + +/** + * Parse the given property pattern at the given parse position. + */ +UnicodeSet& UnicodeSet::applyPropertyPattern(const UnicodeString& pattern, + ParsePosition& ppos, + UErrorCode &ec) { + int32_t pos = ppos.getIndex(); + + UBool posix = false; // true for [:pat:], false for \p{pat} \P{pat} \N{pat} + UBool isName = false; // true for \N{pat}, o/w false + UBool invert = false; + + if (U_FAILURE(ec)) return *this; + + // Minimum length is 5 characters, e.g. \p{L} + if ((pos+5) > pattern.length()) { + FAIL(ec); + } + + // On entry, ppos should point to one of the following locations: + // Look for an opening [:, [:^, \p, or \P + if (isPOSIXOpen(pattern, pos)) { + posix = true; + pos += 2; + pos = ICU_Utility::skipWhitespace(pattern, pos); + if (pos < pattern.length() && pattern.charAt(pos) == u'^') { + ++pos; + invert = true; + } + } else if (isPerlOpen(pattern, pos) || isNameOpen(pattern, pos)) { + char16_t c = pattern.charAt(pos+1); + invert = (c == u'P'); + isName = (c == u'N'); + pos += 2; + pos = ICU_Utility::skipWhitespace(pattern, pos); + if (pos == pattern.length() || pattern.charAt(pos++) != u'{') { + // Syntax error; "\p" or "\P" not followed by "{" + FAIL(ec); + } + } else { + // Open delimiter not seen + FAIL(ec); + } + + // Look for the matching close delimiter, either :] or } + int32_t close; + if (posix) { + close = pattern.indexOf(u":]", 2, pos); + } else { + close = pattern.indexOf(u'}', pos); + } + if (close < 0) { + // Syntax error; close delimiter missing + FAIL(ec); + } + + // Look for an '=' sign. If this is present, we will parse a + // medium \p{gc=Cf} or long \p{GeneralCategory=Format} + // pattern. + int32_t equals = pattern.indexOf(u'=', pos); + UnicodeString propName, valueName; + if (equals >= 0 && equals < close && !isName) { + // Equals seen; parse medium/long pattern + pattern.extractBetween(pos, equals, propName); + pattern.extractBetween(equals+1, close, valueName); + } + + else { + // Handle case where no '=' is seen, and \N{} + pattern.extractBetween(pos, close, propName); + + // Handle \N{name} + if (isName) { + // This is a little inefficient since it means we have to + // parse NAME_PROP back to UCHAR_NAME even though we already + // know it's UCHAR_NAME. If we refactor the API to + // support args of (UProperty, char*) then we can remove + // NAME_PROP and make this a little more efficient. + valueName = propName; + propName = UnicodeString(NAME_PROP, NAME_PROP_LENGTH, US_INV); + } + } + + applyPropertyAlias(propName, valueName, ec); + + if (U_SUCCESS(ec)) { + if (invert) { + complement().removeAllStrings(); // code point complement + } + + // Move to the limit position after the close delimiter if the + // parse succeeded. + ppos.setIndex(close + (posix ? 2 : 1)); + } + + return *this; +} + +/** + * Parse a property pattern. + * @param chars iterator over the pattern characters. Upon return + * it will be advanced to the first character after the parsed + * pattern, or the end of the iteration if all characters are + * parsed. + * @param rebuiltPat the pattern that was parsed, rebuilt or + * copied from the input pattern, as appropriate. + */ +void UnicodeSet::applyPropertyPattern(RuleCharacterIterator& chars, + UnicodeString& rebuiltPat, + UErrorCode& ec) { + if (U_FAILURE(ec)) return; + UnicodeString pattern; + chars.lookahead(pattern); + ParsePosition pos(0); + applyPropertyPattern(pattern, pos, ec); + if (U_FAILURE(ec)) return; + if (pos.getIndex() == 0) { + // syntaxError(chars, "Invalid property pattern"); + ec = U_MALFORMED_SET; + return; + } + chars.jumpahead(pos.getIndex()); + rebuiltPat.append(pattern, 0, pos.getIndex()); +} + +U_NAMESPACE_END diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uresdata.cpp b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uresdata.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36c9e7510a5525e5c424b0374e36d682580f4789 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/common/uresdata.cpp @@ -0,0 +1,1513 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1999-2016, International Business Machines Corporation +* and others. All Rights Reserved. +******************************************************************************* +* file name: uresdata.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999dec08 +* created by: Markus W. Scherer +* Modification History: +* +* Date Name Description +* 06/20/2000 helena OS/400 port changes; mostly typecast. +* 06/24/02 weiv Added support for resource sharing +*/ + +#include "unicode/utypes.h" +#include "unicode/udata.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "cstring.h" +#include "resource.h" +#include "uarrsort.h" +#include "uassert.h" +#include "ucol_swp.h" +#include "udataswp.h" +#include "uinvchar.h" +#include "uresdata.h" +#include "uresimp.h" +#include "utracimp.h" + +/* + * Resource access helpers + */ + +/* get a const char* pointer to the key with the keyOffset byte offset from pRoot */ +#define RES_GET_KEY16(pResData, keyOffset) \ + ((keyOffset)<(pResData)->localKeyLimit ? \ + (const char *)(pResData)->pRoot+(keyOffset) : \ + (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit) + +#define RES_GET_KEY32(pResData, keyOffset) \ + ((keyOffset)>=0 ? \ + (const char *)(pResData)->pRoot+(keyOffset) : \ + (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff)) + +#define URESDATA_ITEM_NOT_FOUND -1 + +/* empty resources, returned when the resource offset is 0 */ +static const uint16_t gEmpty16=0; + +static const struct { + int32_t length; + int32_t res; +} gEmpty32={ 0, 0 }; + +static const struct { + int32_t length; + char16_t nul; + char16_t pad; +} gEmptyString={ 0, 0, 0 }; + +/* + * All the type-access functions assume that + * the resource is of the expected type. + */ + +static int32_t +_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length, + const char *key, const char **realKey) { + const char *tableKey; + int32_t mid, start, limit; + int result; + + /* do a binary search for the key */ + start=0; + limit=length; + while(startuseNativeStrcmp) { + result = uprv_strcmp(key, tableKey); + } else { + result = uprv_compareInvCharsAsAscii(key, tableKey); + } + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid + 1; + } else { + /* We found it! */ + *realKey=tableKey; + return mid; + } + } + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ +} + +static int32_t +_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length, + const char *key, const char **realKey) { + const char *tableKey; + int32_t mid, start, limit; + int result; + + /* do a binary search for the key */ + start=0; + limit=length; + while(startuseNativeStrcmp) { + result = uprv_strcmp(key, tableKey); + } else { + result = uprv_compareInvCharsAsAscii(key, tableKey); + } + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid + 1; + } else { + /* We found it! */ + *realKey=tableKey; + return mid; + } + } + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ +} + +/* helper for res_load() ---------------------------------------------------- */ + +static UBool U_CALLCONV +isAcceptable(void *context, + const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + uprv_memcpy(context, pInfo->formatVersion, 4); + return (UBool)( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->sizeofUChar==U_SIZEOF_UCHAR && + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ + pInfo->dataFormat[1]==0x65 && + pInfo->dataFormat[2]==0x73 && + pInfo->dataFormat[3]==0x42 && + (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3)); +} + +/* semi-public functions ---------------------------------------------------- */ + +static void +res_init(ResourceData *pResData, + UVersionInfo formatVersion, const void *inBytes, int32_t length, + UErrorCode *errorCode) { + UResType rootType; + + /* get the root resource */ + pResData->pRoot=(const int32_t *)inBytes; + pResData->rootRes=(Resource)*pResData->pRoot; + pResData->p16BitUnits=&gEmpty16; + + /* formatVersion 1.1 must have a root item and at least 5 indexes */ + if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + + /* currently, we accept only resources that have a Table as their roots */ + rootType=(UResType)RES_GET_TYPE(pResData->rootRes); + if(!URES_IS_TABLE(rootType)) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + + if(formatVersion[0]==1 && formatVersion[1]==0) { + pResData->localKeyLimit=0x10000; /* greater than any 16-bit key string offset */ + } else { + /* bundles with formatVersion 1.1 and later contain an indexes[] array */ + const int32_t *indexes=pResData->pRoot+1; + int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff; + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if( length>=0 && + (length<((1+indexLength)<<2) || + length<(indexes[URES_INDEX_BUNDLE_TOP]<<2)) + ) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) { + pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2; + } + if(formatVersion[0]>=3) { + // In formatVersion 1, the indexLength took up this whole int. + // In version 2, bits 31..8 were reserved and always 0. + // In version 3, they contain bits 23..0 of the poolStringIndexLimit. + // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12. + pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_INDEX_LENGTH]>>8); + } + if(indexLength>URES_INDEX_ATTRIBUTES) { + int32_t att=indexes[URES_INDEX_ATTRIBUTES]; + pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK); + pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0); + pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0); + pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 -> 27..24 + pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16); + } + if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if( indexLength>URES_INDEX_16BIT_TOP && + indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP] + ) { + pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[URES_INDEX_KEYS_TOP]); + } + } + + if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) { + /* + * formatVersion 1: compare key strings in native-charset order + * formatVersion 2 and up: compare key strings in ASCII order + */ + pResData->useNativeStrcmp=true; + } +} + +U_CAPI void U_EXPORT2 +res_read(ResourceData *pResData, + const UDataInfo *pInfo, const void *inBytes, int32_t length, + UErrorCode *errorCode) { + UVersionInfo formatVersion; + + uprv_memset(pResData, 0, sizeof(ResourceData)); + if(U_FAILURE(*errorCode)) { + return; + } + if(!isAcceptable(formatVersion, nullptr, nullptr, pInfo)) { + *errorCode=U_INVALID_FORMAT_ERROR; + return; + } + res_init(pResData, formatVersion, inBytes, length, errorCode); +} + +U_CFUNC void +res_load(ResourceData *pResData, + const char *path, const char *name, UErrorCode *errorCode) { + UVersionInfo formatVersion; + + uprv_memset(pResData, 0, sizeof(ResourceData)); + + /* load the ResourceBundle file */ + pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode); + if(U_FAILURE(*errorCode)) { + return; + } + + /* get its memory and initialize *pResData */ + res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode); +} + +U_CFUNC void +res_unload(ResourceData *pResData) { + if(pResData->data!=nullptr) { + udata_close(pResData->data); + pResData->data=nullptr; + } +} + +static const int8_t gPublicTypes[URES_LIMIT] = { + URES_STRING, + URES_BINARY, + URES_TABLE, + URES_ALIAS, + + URES_TABLE, /* URES_TABLE32 */ + URES_TABLE, /* URES_TABLE16 */ + URES_STRING, /* URES_STRING_V2 */ + URES_INT, + + URES_ARRAY, + URES_ARRAY, /* URES_ARRAY16 */ + URES_NONE, + URES_NONE, + + URES_NONE, + URES_NONE, + URES_INT_VECTOR, + URES_NONE +}; + +U_CAPI UResType U_EXPORT2 +res_getPublicType(Resource res) { + return (UResType)gPublicTypes[RES_GET_TYPE(res)]; +} + +U_CAPI const char16_t * U_EXPORT2 +res_getStringNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const char16_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_STRING_V2) { + int32_t first; + if((int32_t)offsetpoolStringIndexLimit) { + p=(const char16_t *)pResData->poolBundleStrings+offset; + } else { + p=(const char16_t *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); + } + first=*p; + if(!U16_IS_TRAIL(first)) { + length=u_strlen(p); + } else if(first<0xdfef) { + length=first&0x3ff; + ++p; + } else if(first<0xdfff) { + length=((first-0xdfef)<<16)|p[1]; + p+=2; + } else { + length=((int32_t)p[1]<<16)|p[2]; + p+=3; + } + } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ { + const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res; + length=*p32++; + p=(const char16_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +namespace { + +/** + * CLDR string value (three empty-set symbols)=={2205, 2205, 2205} + * prevents fallback to the parent bundle. + * TODO: combine with other code that handles this marker, use EMPTY_SET constant. + * TODO: maybe move to uresbund.cpp? + */ +UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) { + uint32_t offset=RES_GET_OFFSET(res); + if (offset == 0) { + // empty string + } else if (res == offset) { + const int32_t *p32=pResData->pRoot+res; + int32_t length=*p32; + const char16_t *p=(const char16_t *)p32; + return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205; + } else if (RES_GET_TYPE(res) == URES_STRING_V2) { + const char16_t *p; + if((int32_t)offsetpoolStringIndexLimit) { + p=(const char16_t *)pResData->poolBundleStrings+offset; + } else { + p=(const char16_t *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); + } + int32_t first=*p; + if (first == 0x2205) { // implicit length + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0; + } else if (first == 0xdc03) { // explicit length 3 (should not occur) + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205; + } else { + // Assume that the string has not been stored with more length units than necessary. + return false; + } + } + return false; +} + +int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array, + icu::UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == nullptr ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = array.getSize(); + if(length == 0) { + return 0; + } + if(length > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return length; + } + for(int32_t i = 0; i < length; ++i) { + int32_t sLength; + // No tracing: handled by the caller + const char16_t *s = res_getStringNoTrace(pResData, array.internalGetResource(pResData, i), &sLength); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; + } + dest[i].setTo(true, s, sLength); + } + return length; +} + +} // namespace + +U_CAPI const char16_t * U_EXPORT2 +res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) { + const char16_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_ALIAS) { + const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset; + length=*p32++; + p=(const char16_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +U_CAPI const uint8_t * U_EXPORT2 +res_getBinaryNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const uint8_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_BINARY) { + const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset; + length=*p32++; + p=(const uint8_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + + +U_CAPI const int32_t * U_EXPORT2 +res_getIntVectorNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const int32_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_INT_VECTOR) { + p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset; + length=*p++; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +U_CAPI int32_t U_EXPORT2 +res_countArrayItems(const ResourceData *pResData, Resource res) { + uint32_t offset=RES_GET_OFFSET(res); + switch(RES_GET_TYPE(res)) { + case URES_STRING: + case URES_STRING_V2: + case URES_BINARY: + case URES_ALIAS: + case URES_INT: + case URES_INT_VECTOR: + return 1; + case URES_ARRAY: + case URES_TABLE32: + return offset==0 ? 0 : *(pResData->pRoot+offset); + case URES_TABLE: + return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset)); + case URES_ARRAY16: + case URES_TABLE16: + return pResData->p16BitUnits[offset]; + default: + return 0; + } +} + +U_NAMESPACE_BEGIN + +ResourceDataValue::~ResourceDataValue() {} + +UResType ResourceDataValue::getType() const { + return res_getPublicType(res); +} + +const char16_t *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &length); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return s; +} + +const char16_t *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const char16_t *s = res_getAlias(&getData(), res, &length); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return s; +} + +int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return 0; + } + if(RES_GET_TYPE(res) != URES_INT) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return res_getInt(fTraceInfo, res); +} + +uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return 0; + } + if(RES_GET_TYPE(res) != URES_INT) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return res_getUInt(fTraceInfo, res); +} + +const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const int32_t *iv = res_getIntVector(fTraceInfo, &getData(), res, &length); + if(iv == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return iv; +} + +const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const uint8_t *b = res_getBinary(fTraceInfo, &getData(), res, &length); + if(b == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return b; +} + +ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceArray(); + } + const uint16_t *items16 = nullptr; + const Resource *items32 = nullptr; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_ARRAY: + if (offset!=0) { // empty if offset==0 + items32 = (const Resource *)getData().pRoot+offset; + length = *items32++; + } + break; + case URES_ARRAY16: + items16 = getData().p16BitUnits+offset; + length = *items16++; + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceArray(); + } + return ResourceArray(items16, items32, length, fTraceInfo); +} + +ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceTable(); + } + const uint16_t *keys16 = nullptr; + const int32_t *keys32 = nullptr; + const uint16_t *items16 = nullptr; + const Resource *items32 = nullptr; + uint32_t offset = RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_TABLE: + if (offset != 0) { // empty if offset==0 + keys16 = (const uint16_t *)(getData().pRoot+offset); + length = *keys16++; + items32 = (const Resource *)(keys16+length+(~length&1)); + } + break; + case URES_TABLE16: + keys16 = getData().p16BitUnits+offset; + length = *keys16++; + items16 = keys16 + length; + break; + case URES_TABLE32: + if (offset != 0) { // empty if offset==0 + keys32 = getData().pRoot+offset; + length = *keys32++; + items32 = (const Resource *)keys32 + length; + } + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceTable(); + } + return ResourceTable(keys16, keys32, items16, items32, length, fTraceInfo); +} + +UBool ResourceDataValue::isNoInheritanceMarker() const { + return ::isNoInheritanceMarker(&getData(), res); +} + +int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); +} + +int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + if(URES_IS_ARRAY(res)) { + return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); + } + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == nullptr ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(capacity < 1) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 1; + } + int32_t sLength; + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength); + if(s != nullptr) { + dest[0].setTo(true, s, sLength); + return 1; + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; +} + +UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const { + UnicodeString us; + if(U_FAILURE(errorCode)) { + return us; + } + int32_t sLength; + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength); + if(s != nullptr) { + us.setTo(true, s, sLength); + return us; + } + ResourceArray array = getArray(errorCode); + if(U_FAILURE(errorCode)) { + return us; + } + if(array.getSize() > 0) { + // Tracing is already performed above (unimportant for trace that this is an array) + s = res_getStringNoTrace(&getData(), array.internalGetResource(&getData(), 0), &sLength); + if(s != nullptr) { + us.setTo(true, s, sLength); + return us; + } + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return us; +} + +U_NAMESPACE_END + +static Resource +makeResourceFrom16(const ResourceData *pResData, int32_t res16) { + if(res16poolStringIndex16Limit) { + // Pool string, nothing to do. + } else { + // Local string, adjust the 16-bit offset to a regular one, + // with a larger pool string index limit. + res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit; + } + return URES_MAKE_RESOURCE(URES_STRING_V2, res16); +} + +U_CAPI Resource U_EXPORT2 +res_getTableItemByKey(const ResourceData *pResData, Resource table, + int32_t *indexR, const char **key) { + uint32_t offset=RES_GET_OFFSET(table); + int32_t length; + int32_t idx; + if(key == nullptr || *key == nullptr) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(table)) { + case URES_TABLE: { + if (offset!=0) { /* empty if offset==0 */ + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); + length=*p++; + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); + if(idx>=0) { + const Resource *p32=(const Resource *)(p+length+(~length&1)); + return p32[idx]; + } + } + break; + } + case URES_TABLE16: { + const uint16_t *p=pResData->p16BitUnits+offset; + length=*p++; + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); + if(idx>=0) { + return makeResourceFrom16(pResData, p[length+idx]); + } + break; + } + case URES_TABLE32: { + if (offset!=0) { /* empty if offset==0 */ + const int32_t *p= pResData->pRoot+offset; + length=*p++; + *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key); + if(idx>=0) { + return (Resource)p[length+idx]; + } + } + break; + } + default: + break; + } + return RES_BOGUS; +} + +U_CAPI Resource U_EXPORT2 +res_getTableItemByIndex(const ResourceData *pResData, Resource table, + int32_t indexR, const char **key) { + uint32_t offset=RES_GET_OFFSET(table); + int32_t length; + if (indexR < 0) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(table)) { + case URES_TABLE: { + if (offset != 0) { /* empty if offset==0 */ + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); + length=*p++; + if(indexRp16BitUnits+offset; + length=*p++; + if(indexRpRoot+offset; + length=*p++; + if(indexRrootRes, &idx, &realKey); +} + + +UBool icu::ResourceTable::getKeyAndValue(int32_t i, + const char *&key, icu::ResourceValue &value) const { + if(0 <= i && i < length) { + icu::ResourceDataValue &rdValue = static_cast(value); + if (keys16 != nullptr) { + key = RES_GET_KEY16(&rdValue.getData(), keys16[i]); + } else { + key = RES_GET_KEY32(&rdValue.getData(), keys32[i]); + } + Resource res; + if (items16 != nullptr) { + res = makeResourceFrom16(&rdValue.getData(), items16[i]); + } else { + res = items32[i]; + } + // Note: the ResourceTracer keeps a reference to the field of this + // ResourceTable. This is OK because the ResourceTable should remain + // alive for the duration that fields are being read from it + // (including nested fields). + rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); + return true; + } + return false; +} + +UBool icu::ResourceTable::findValue(const char *key, ResourceValue &value) const { + icu::ResourceDataValue &rdValue = static_cast(value); + const char *realKey = nullptr; + int32_t i; + if (keys16 != nullptr) { + i = _res_findTableItem(&rdValue.getData(), keys16, length, key, &realKey); + } else { + i = _res_findTable32Item(&rdValue.getData(), keys32, length, key, &realKey); + } + if (i >= 0) { + Resource res; + if (items16 != nullptr) { + res = makeResourceFrom16(&rdValue.getData(), items16[i]); + } else { + res = items32[i]; + } + // Same note about lifetime as in getKeyAndValue(). + rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); + return true; + } + return false; +} + +U_CAPI Resource U_EXPORT2 +res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) { + uint32_t offset=RES_GET_OFFSET(array); + if (indexR < 0) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(array)) { + case URES_ARRAY: { + if (offset!=0) { /* empty if offset==0 */ + const int32_t *p= pResData->pRoot+offset; + if(indexR<*p) { + return (Resource)p[1+indexR]; + } + } + break; + } + case URES_ARRAY16: { + const uint16_t *p=pResData->p16BitUnits+offset; + if(indexR<*p) { + return makeResourceFrom16(pResData, p[1+indexR]); + } + break; + } + default: + break; + } + return RES_BOGUS; +} + +uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const { + if (items16 != nullptr) { + return makeResourceFrom16(pResData, items16[i]); + } else { + return items32[i]; + } +} + +UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const { + if(0 <= i && i < length) { + icu::ResourceDataValue &rdValue = static_cast(value); + // Note: the ResourceTracer keeps a reference to the field of this + // ResourceArray. This is OK because the ResourceArray should remain + // alive for the duration that fields are being read from it + // (including nested fields). + rdValue.setResource( + internalGetResource(&rdValue.getData(), i), + ResourceTracer(fTraceInfo, i)); + return true; + } + return false; +} + +U_CFUNC Resource +res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) { + char *pathP = *path, *nextSepP = *path; + char *closeIndex = nullptr; + Resource t1 = r; + Resource t2; + int32_t indexR = 0; + UResType type = (UResType)RES_GET_TYPE(t1); + + /* if you come in with an empty path, you'll be getting back the same resource */ + if(!uprv_strlen(pathP)) { + return r; + } + + /* one needs to have an aggregate resource in order to search in it */ + if(!URES_IS_CONTAINER(type)) { + return RES_BOGUS; + } + + while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) { + /* Iteration stops if: the path has been consumed, we found a non-existing + * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias) + */ + nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR); + /* if there are more separators, terminate string + * and set path to the remaining part of the string + */ + if(nextSepP != nullptr) { + if(nextSepP == pathP) { + // Empty key string. + return RES_BOGUS; + } + *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */ + *path = nextSepP+1; + } else { + *path = uprv_strchr(pathP, 0); + } + + /* if the resource is a table */ + /* try the key based access */ + if(URES_IS_TABLE(type)) { + *key = pathP; + t2 = res_getTableItemByKey(pResData, t1, &indexR, key); + } else if(URES_IS_ARRAY(type)) { + indexR = uprv_strtol(pathP, &closeIndex, 10); + if(indexR >= 0 && *closeIndex == 0) { + t2 = res_getArrayItem(pResData, t1, indexR); + } else { + t2 = RES_BOGUS; /* have an array, but don't have a valid index */ + } + *key = nullptr; + } else { /* can't do much here, except setting t2 to bogus */ + t2 = RES_BOGUS; + } + t1 = t2; + type = (UResType)RES_GET_TYPE(t1); + /* position pathP to next resource key/index */ + pathP = *path; + } + + return t1; +} + +/* resource bundle swapping ------------------------------------------------- */ + +/* + * Need to always enumerate the entire item tree, + * track the lowest address of any item to use as the limit for char keys[], + * track the highest address of any item to return the size of the data. + * + * We should have thought of storing those in the data... + * It is possible to extend the data structure by putting additional values + * in places that are inaccessible by ordinary enumeration of the item tree. + * For example, additional integers could be stored at the beginning or + * end of the key strings; this could be indicated by a minor version number, + * and the data swapping would have to know about these values. + * + * The data structure does not forbid keys to be shared, so we must swap + * all keys once instead of each key when it is referenced. + * + * These swapping functions assume that a resource bundle always has a length + * that is a multiple of 4 bytes. + * Currently, this is trivially true because genrb writes bundle tree leaves + * physically first, before their branches, so that the root table with its + * array of resource items (uint32_t values) is always last. + */ + +/* definitions for table sorting ------------------------ */ + +/* + * row of a temporary array + * + * gets platform-endian key string indexes and sorting indexes; + * after sorting this array by keys, the actual key/value arrays are permutated + * according to the sorting indexes + */ +typedef struct Row { + int32_t keyIndex, sortIndex; +} Row; + +static int32_t U_CALLCONV +ures_compareRows(const void *context, const void *left, const void *right) { + const char *keyChars=(const char *)context; + return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex, + keyChars+((const Row *)right)->keyIndex); +} + +typedef struct TempTable { + const char *keyChars; + Row *rows; + int32_t *resort; + uint32_t *resFlags; + int32_t localKeyLimit; + uint8_t majorFormatVersion; +} TempTable; + +enum { + STACK_ROW_CAPACITY=200 +}; + +/* The table item key string is not locally available. */ +static const char *const gUnknownKey=""; + +// TODO-LDML: #if due to https://unicode-org.atlassian.net/browse/ICU-22374 +#if !UCONFIG_NO_COLLATION +/* resource table key for collation binaries: "%%CollationBin" */ +static const char16_t gCollationBinKey[]={ + 0x25, 0x25, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x69, 0x6e, + 0 +}; +#endif + +/* + * swap one resource item + */ +static void +ures_swapResource(const UDataSwapper *ds, + const Resource *inBundle, Resource *outBundle, + Resource res, /* caller swaps res itself */ + const char *key, + TempTable *pTempTable, + UErrorCode *pErrorCode) { + const Resource *p; + Resource *q; + int32_t offset, count; + + switch(RES_GET_TYPE(res)) { + case URES_TABLE16: + case URES_STRING_V2: + case URES_INT: + case URES_ARRAY16: + /* integer, or points to 16-bit units, nothing to do here */ + return; + default: + break; + } + + /* all other types use an offset to point to their data */ + offset=(int32_t)RES_GET_OFFSET(res); + if(offset==0) { + /* special offset indicating an empty item */ + return; + } + if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) { + /* we already swapped this resource item */ + return; + } else { + /* mark it as swapped now */ + pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f)); + } + + p=inBundle+offset; + q=outBundle+offset; + + switch(RES_GET_TYPE(res)) { + case URES_ALIAS: + /* physically same value layout as string, fall through */ + U_FALLTHROUGH; + case URES_STRING: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p, 4, q, pErrorCode); + /* swap each char16_t (the terminating NUL would not change) */ + ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode); + break; + case URES_BINARY: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p, 4, q, pErrorCode); + /* no need to swap or copy bytes - ures_swap() copied them all */ + + /* swap known formats */ +#if !UCONFIG_NO_COLLATION + if( key!=nullptr && /* the binary is in a table */ + (key!=gUnknownKey ? + /* its table key string is "%%CollationBin" */ + 0==ds->compareInvChars(ds, key, -1, + gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) : + /* its table key string is unknown but it looks like a collation binary */ + ucol_looksLikeCollationBinary(ds, p+1, count)) + ) { + ucol_swap(ds, p+1, count, q+1, pErrorCode); + } +#endif + break; + case URES_TABLE: + case URES_TABLE32: + { + const uint16_t *pKey16; + uint16_t *qKey16; + + const int32_t *pKey32; + int32_t *qKey32; + + Resource item; + int32_t i, oldIndex; + + if(RES_GET_TYPE(res)==URES_TABLE) { + /* get table item count */ + pKey16=(const uint16_t *)p; + qKey16=(uint16_t *)q; + count=ds->readUInt16(*pKey16); + + pKey32=qKey32=nullptr; + + /* swap count */ + ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode); + + offset+=((1+count)+1)/2; + } else { + /* get table item count */ + pKey32=(const int32_t *)p; + qKey32=(int32_t *)q; + count=udata_readInt32(ds, *pKey32); + + pKey16=qKey16=nullptr; + + /* swap count */ + ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode); + + offset+=1+count; + } + + if(count==0) { + break; + } + + p=inBundle+offset; /* pointer to table resources */ + q=outBundle+offset; + + /* recurse */ + for(i=0; ireadUInt16(pKey16[i]); + if(keyOffsetlocalKeyLimit) { + itemKey=(const char *)outBundle+keyOffset; + } + } else { + int32_t keyOffset=udata_readInt32(ds, pKey32[i]); + if(keyOffset>=0) { + itemKey=(const char *)outBundle+keyOffset; + } + } + item=ds->readUInt32(p[i]); + ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n", + res, i, item); + return; + } + } + + if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) { + /* no need to sort, just swap the offset/value arrays */ + if(pKey16!=nullptr) { + ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode); + ds->swapArray32(ds, p, count*4, q, pErrorCode); + } else { + /* swap key offsets and items as one array */ + ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode); + } + break; + } + + /* + * We need to sort tables by outCharset key strings because they + * sort differently for different charset families. + * ures_swap() already set pTempTable->keyChars appropriately. + * First we set up a temporary table with the key indexes and + * sorting indexes and sort that. + * Then we permutate and copy/swap the actual values. + */ + if(pKey16!=nullptr) { + for(i=0; irows[i].keyIndex=ds->readUInt16(pKey16[i]); + pTempTable->rows[i].sortIndex=i; + } + } else { + for(i=0; irows[i].keyIndex=udata_readInt32(ds, pKey32[i]); + pTempTable->rows[i].sortIndex=i; + } + } + uprv_sortArray(pTempTable->rows, count, sizeof(Row), + ures_compareRows, pTempTable->keyChars, + false, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n", + res, count); + return; + } + + /* + * copy/swap/permutate items + * + * If we swap in-place, then the permutation must use another + * temporary array (pTempTable->resort) + * before the results are copied to the outBundle. + */ + /* keys */ + if(pKey16!=nullptr) { + uint16_t *rKey16; + + if(pKey16!=qKey16) { + rKey16=qKey16; + } else { + rKey16=(uint16_t *)pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode); + } + if(qKey16!=rKey16) { + uprv_memcpy(qKey16, rKey16, 2*count); + } + } else { + int32_t *rKey32; + + if(pKey32!=qKey32) { + rKey32=qKey32; + } else { + rKey32=pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode); + } + if(qKey32!=rKey32) { + uprv_memcpy(qKey32, rKey32, 4*count); + } + } + + /* resources */ + { + Resource *r; + + + if(p!=q) { + r=q; + } else { + r=(Resource *)pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode); + } + if(q!=r) { + uprv_memcpy(q, r, 4*count); + } + } + } + break; + case URES_ARRAY: + { + Resource item; + int32_t i; + + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p++, 4, q++, pErrorCode); + + /* recurse */ + for(i=0; ireadUInt32(p[i]); + ures_swapResource(ds, inBundle, outBundle, item, nullptr, pTempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n", + res, i, item); + return; + } + } + + /* swap items */ + ds->swapArray32(ds, p, 4*count, q, pErrorCode); + } + break; + case URES_INT_VECTOR: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length and each integer */ + ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode); + break; + default: + /* also catches RES_BOGUS */ + *pErrorCode=U_UNSUPPORTED_ERROR; + break; + } +} + +U_CAPI int32_t U_EXPORT2 +ures_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + const Resource *inBundle; + Resource rootRes; + int32_t headerSize, maxTableLength; + + Row rows[STACK_ROW_CAPACITY]; + int32_t resort[STACK_ROW_CAPACITY]; + TempTable tempTable; + + const int32_t *inIndexes; + + /* the following integers count Resource item offsets (4 bytes each), not bytes */ + int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ + pInfo->dataFormat[1]==0x65 && + pInfo->dataFormat[2]==0x73 && + pInfo->dataFormat[3]==0x42 && + /* formatVersion 1.1+ or 2.x or 3.x */ + ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) || + pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3) + )) { + udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0], pInfo->formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + tempTable.majorFormatVersion=pInfo->formatVersion[0]; + + /* a resource bundle must contain at least one resource item */ + if(length<0) { + bundleLength=-1; + } else { + bundleLength=(length-headerSize)/4; + + /* formatVersion 1.1 must have a root item and at least 5 indexes */ + if(bundleLength<(1+5)) { + udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n", + length-headerSize); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + inBundle=(const Resource *)((const char *)inData+headerSize); + rootRes=ds->readUInt32(*inBundle); + + /* formatVersion 1.1 adds the indexes[] array */ + inIndexes=(const int32_t *)(inBundle+1); + + indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff; + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { + udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n"); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + keysBottom=1+indexLength; + keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]); + if(indexLength>URES_INDEX_16BIT_TOP) { + resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]); + } else { + resBottom=keysTop; + } + top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]); + maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]); + + if(0<=bundleLength && bundleLength(1+indexLength)) { + tempTable.localKeyLimit=keysTop<<2; + } else { + tempTable.localKeyLimit=0; + } + + if(length>=0) { + Resource *outBundle=(Resource *)((char *)outData+headerSize); + + /* track which resources we have already swapped */ + uint32_t stackResFlags[STACK_ROW_CAPACITY]; + int32_t resFlagsLength; + + /* + * We need one bit per 4 resource bundle bytes so that we can track + * every possible Resource for whether we have swapped it already. + * Multiple Resource words can refer to the same bundle offsets + * for sharing identical values. + * We could optimize this by allocating only for locations above + * where Resource values are stored (above keys & strings). + */ + resFlagsLength=(length+31)>>5; /* number of bytes needed */ + resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint32_t */ + if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) { + tempTable.resFlags=stackResFlags; + } else { + tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength); + if(tempTable.resFlags==nullptr) { + udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n"); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + } + uprv_memset(tempTable.resFlags, 0, resFlagsLength); + + /* copy the bundle for binary and inaccessible data */ + if(inData!=outData) { + uprv_memcpy(outBundle, inBundle, 4*top); + } + + /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */ + udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom), + outBundle+keysBottom, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom)); + return 0; + } + + /* swap the 16-bit units (strings, table16, array16) */ + if(keysTopswapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop)); + return 0; + } + } + + /* allocate the temporary table for sorting resource tables */ + tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */ + if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) { + tempTable.rows=rows; + tempTable.resort=resort; + } else { + tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4); + if(tempTable.rows==nullptr) { + udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n", + maxTableLength); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + if(tempTable.resFlags!=stackResFlags) { + uprv_free(tempTable.resFlags); + } + return 0; + } + tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength); + } + + /* swap the resources */ + ures_swapResource(ds, inBundle, outBundle, rootRes, nullptr, &tempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(root res=%08x) failed\n", + rootRes); + } + + if(tempTable.rows!=rows) { + uprv_free(tempTable.rows); + } + if(tempTable.resFlags!=stackResFlags) { + uprv_free(tempTable.resFlags); + } + + /* swap the root resource and indexes */ + ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode); + } + + return headerSize+4*top; +} diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/source/meson.build b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..406b88ad94bbf943a2f20e964a83ddf1cabc3786 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/meson.build @@ -0,0 +1,22 @@ +incdir = include_directories('common') + +# if host_machine.system() == 'windows' +# icudata_name = '@0@icudt@1@'.format(library_prefix, library_suffix) +# else + icudata_name = '@0@icudata@1@'.format(library_prefix, library_suffix) +# endif + +subdir('stubdata') +subdir('common') + +# TODO-LDML: Not used now, will need for regex +# subdir('i18n') + +## Note: The following subdirs are not used by Keyman (at present) + +# subdir('data') +# subdir('io') +# subdir('layout') +# subdir('layoutex') +# subdir('test') +# subdir('tools') diff --git a/developer/src/kmcmplib/subprojects/packagefiles/icu/source/stubdata/meson.build b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/stubdata/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..ee116a9bf02def142bef739a3f294bf0f403c003 --- /dev/null +++ b/developer/src/kmcmplib/subprojects/packagefiles/icu/source/stubdata/meson.build @@ -0,0 +1,31 @@ +sources = files( + 'stubdata.cpp', +) + +stubdata_lib = static_library( + icudata_name, + sources, + c_args: ['-DSTUBDATA_BUILD'] + usage_args, + cpp_args:[ '-DSTUBDATA_BUILD' ]+ usage_args, + include_directories: incdir, + # version: U_ICU_VERSION, +) + +stubdata_dep = declare_dependency( + link_with: stubdata_lib, +) + + +# if meson.can_run_host_binaries() + stubdata_native_lib = stubdata_lib +# else +# stubdata_native_lib = library( +# '@0@-native'.format(icudata_name), +# sources, +# c_args: '-DSTUBDATA_BUILD', +# cpp_args: '-DSTUBDATA_BUILD', +# include_directories: incdir, +# version: U_ICU_VERSION, +# native: true, +# ) +# endif diff --git a/developer/src/kmcmplib/tests/README.md b/developer/src/kmcmplib/tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5d3b2804d41554d2a1e79883de4c39f612a83f67 --- /dev/null +++ b/developer/src/kmcmplib/tests/README.md @@ -0,0 +1,33 @@ +# Unit tests for kmcmplib + +**Note:** the keyboards repository tests only run if --full-test is passed to +build.sh. + +kmcmplib tests include building all keyboards in the keyboards repository and +comparing against a 'known good' legacy compiler set. This is trickier than it +sounds because the legacy compiler only runs under Windows/WINE and we don't +want to introduce a dependency on that for running the tests. + +Instead we've gone the route of adding a pre-compiled set of .kmx fixtures, +alongside a commit ref in keyboards_commit_ref.txt. The test script will +checkout that specific ref in its own private clone of the keyboards repository +(including a shallow clone of the keyboards repo if it isn't already present), +and compiling the sources from that specific commit. + +For performance reasons, it is useful to setup KEYBOARDS_ROOT env var to point +to your local clone of the keyboards repository. The build will make a private +clone of this repo rather than the remote clone if it is available, which is +typically much faster. + +# Preparing fixtures + +prep.sh allows us to regenerate the fixtures with the legacy compiler for +testing. It depends on the regression test scripts in keyboards.sh, and specific +changes introduced into those scripts in +https://github.com/keymanapp/keyboards/pull/2169. + +It is essential that the keyboards repository is clean and checked out to the +commit that you want to be using (usually this will be HEAD of master). All of +Keyman Developer must be built locally and binaries should be in developer/bin/. + +prep.sh has only been tested on Windows, because it relies on kmcomp.exe. diff --git a/developer/src/kmcmplib/tests/api-test.cpp b/developer/src/kmcmplib/tests/api-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c46b1f1a408c166cda98dda0ef43d7449471a1e --- /dev/null +++ b/developer/src/kmcmplib/tests/api-test.cpp @@ -0,0 +1,89 @@ +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * API endpoint unit tests for kmcmplib + */ + +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "../src/compfile.h" +#include + +void setup(); +void test_kmcmp_CompileKeyboardFile(); +void test_kmcmp_CompileKeyboardFileToBuffer(); + +std::vector error_vec; + +int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context) { + error_vec.push_back(dwMsgCode); + const char*t = "unknown"; + switch(dwMsgCode & 0xF000) { + case CERR_HINT: t=" hint"; break; + case CERR_WARNING: t="warning"; break; + case CERR_ERROR: t=" error"; break; + case CERR_FATAL: t=" fatal"; break; + } + printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText); + return 1; +} + +int main(int argc, char *argv[]) { + setup(); + test_kmcmp_CompileKeyboardFile(); + + setup(); + test_kmcmp_CompileKeyboardFileToBuffer(); + + return 0; +} + +void setup() { + error_vec.clear(); +} + +void test_kmcmp_CompileKeyboardFile() { + char kmn_file[L_tmpnam], kmx_file[L_tmpnam]; + tmpnam(kmn_file); + tmpnam(kmx_file); + + // Create an empty file + FILE *fp = fopen(kmn_file, "w"); + fclose(fp); + + // It should fail when a zero-byte file is passed in + assert(!kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr)); + assert(error_vec.size() == 1); + assert(error_vec[0] == CERR_CannotReadInfile); + + unlink(kmn_file); +} + +void test_kmcmp_CompileKeyboardFileToBuffer() { + char kmn_file[L_tmpnam], kmx_file[L_tmpnam]; + tmpnam(kmn_file); + tmpnam(kmx_file); + + // Create an empty file + FILE *fp = fopen(kmn_file, "w"); + fclose(fp); + + FILE_KEYBOARD fk; + + // It should fail when a zero-byte file is passed in + assert(!kmcmp_CompileKeyboardFileToBuffer(kmn_file, &fk, true, false, msgproc, nullptr, CKF_KEYMAN)); + assert(error_vec.size() == 1); + assert(error_vec[0] == CERR_CannotReadInfile); + + unlink(kmn_file); +} \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx b/developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx new file mode 100644 index 0000000000000000000000000000000000000000..022254f30cb8d418d80a307b4450d3f7d3f7d58f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ahom_star.kmx b/developer/src/kmcmplib/tests/fixtures/ahom_star.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bb4a25dc3ed3d5223a66e941d659c7b24327526c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ahom_star.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx b/developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8ef0107686cda89ffea8ef62630f98b9c5c380d1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx b/developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2e2140a47de066096d67d83be4b29e16eae46dd4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/anii.kmx b/developer/src/kmcmplib/tests/fixtures/anii.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b7364fc6499a0370197eed458058f63aca6624bf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/anii.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx b/developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c573fecd20687968fce217b28d26ef2de91efee9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx new file mode 100644 index 0000000000000000000000000000000000000000..583078c41843337c1471ae45d1b8498643212912 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8010790e3da2f267e51bd25083d90030c6604ca6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d54f85abee2f4a5ce1d42cc39afb0a0a0dcad394 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx b/developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx new file mode 100644 index 0000000000000000000000000000000000000000..127914ffe52d26515f0b8332d4857d58fa8f8b96 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cab129cf0b11cdd1eb523e7324d2179128d863bd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ausephon.kmx b/developer/src/kmcmplib/tests/fixtures/ausephon.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5e1d99345156581ffa851c9a21bb26a8cd90eaee Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ausephon.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2d7e0606880adadb6e7e3e6611f7cf5145e4f5dc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5718c9ed54b10dfa73645fd3edcecbbdc185c05b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0aa472e7d719e58b40718c238411539d6b091fdc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..38714eb7888a0b8fabf64ca6fbb09c823e680d62 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx new file mode 100644 index 0000000000000000000000000000000000000000..20ca365b71c9086fbdfc5d6b58a6e22264688932 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..43112d0ad470c442a5da3b1e0094af9762a701a7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx b/developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6228742eca089c60966479c016e811ba2df6b6b7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx b/developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ba449452e698810b89b48545b9f637e650fcd26b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx b/developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx new file mode 100644 index 0000000000000000000000000000000000000000..582b517ef5e54e4751a5b9c2f106911241874aea Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a4e8a01c0136104c2ee07323f872e32411c98c5c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..87712cb69cf30d04a15f3f2e084a90fa655910cd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dacb36e258a277f875068760172e15002a835105 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx new file mode 100644 index 0000000000000000000000000000000000000000..81159040eea2a41a080e5b21a25ab4f8171e438c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx new file mode 100644 index 0000000000000000000000000000000000000000..797300726e58b26366f5a9dd044b49dca88899bd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9210a7fa3a951d9d06fbce7a81b65ae71861f6f4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e77ad47a93adb60880dd9238a150d7ca990065c0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a02cdd54ba4c8715c64ce37553ae3ba24f3d4425 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..073ce6d16f55b81a0f5f7dfae9228f6c38bec54e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx new file mode 100644 index 0000000000000000000000000000000000000000..490aaac05fe5aeab1cde4bb44d3d677b918895d7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d12f8d6f92749aa8d4fe1ce0c04dfc5db675411a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3c56347f51c3b624f892aec6a3b610d9ffcae01e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b5c2783d685ceacd2ac104faefb9cf615afae6f2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9000c86436324f8d6f1984de49823aef46366feb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b0d15fa23a599c23e7dae149e6c3e4c1f3ded254 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ddd9bded810372f9d5084f3164b033c238142bd1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..284a8878a1f92fbcf178f67c93993ad08675ee36 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx new file mode 100644 index 0000000000000000000000000000000000000000..937f755ea95c1572c13d38a80bfac9caba3f619b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2a36c250446e40dfaeb4b30b9da077fd3c27c3b8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f3d117172c4fb1353ee91451c21b96184ef1dd4b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8e27a0dbc67e28267ff91e9d03b8a485f3cfac23 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c1021e5c7fa13c2b375b21fb8a2ea83ea83b6561 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx new file mode 100644 index 0000000000000000000000000000000000000000..88c242a58fa3333c4f6a68cd8f139a4ccede3dd0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ebb3bae6ce546dae6b050c07992a2f8d2e74e8df Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..65e70a2c70643d1155e043aa1acd4d5d82f4a778 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx new file mode 100644 index 0000000000000000000000000000000000000000..792054a8903f8319da5d740621e278754e7e6236 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx new file mode 100644 index 0000000000000000000000000000000000000000..91c769d249377d4342dc979222b295ccaf954188 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fe0e9c70486314de772cff54606ebdc746f1762e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e293b056cdd0b30acc9a2b52a13442bd9b0ac5a8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5a7243bd93c34d954e2811cf559f036a44bee2a6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8cf0d820b9d749dfb3fde7ce074b4fc78b1a6fda Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx new file mode 100644 index 0000000000000000000000000000000000000000..aa282296e347cee97f1bb0dcb2f432ec3ea0a0de Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eea19450598ecb64afc91dc6f520fb1c17edba2f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a2bf45f00cdff79abc305ac7cb7c553ea1a6c0c0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f4731412f03e665988e89e21e763aa16c1118d45 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1b2b2584510ee4d22d429b96e9a07f7fa5a65c01 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx new file mode 100644 index 0000000000000000000000000000000000000000..97cd31535da991e80f11fd05e198184cb6c69438 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d66c6984b51ff54618b6b395296b7c212993c5e7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5c4f44b6ada77244cd511143e11a6fbed224a35c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..59bcae67d18e2f35a068ef78b0b97642ac88feab Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cc53ecc0c6873f956cacc66f947cb9f171ca2d3b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fa9d6c4c731b921917261800d55da17e624371f5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8aa400b50bed17f949766b8ea20f40dcad2ae5b1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a243166c2ddf8e5c8fb4d38486cb41af1d3e7b3a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bc26f816056204241042f01fb053eeeae0ac635d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx new file mode 100644 index 0000000000000000000000000000000000000000..31a621f17b6eb2199acd14bda278fa866c78c381 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..458e4ec060bff672178330c1792926f602288ab1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx new file mode 100644 index 0000000000000000000000000000000000000000..107967ba6f6a05b6ef02d3895f515db48680dbe7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1ac4c9083d5139c0c83dfae1e945a1982fa54fab Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c356eecd7b0f80b6d2dfbf242f9b4c1b12767bdb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7e251ebc113aafcaa45faaca87fa58e721d5932a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d264860a10731fbea01245b35413e6b0377b9b8e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cfd3cf5c319211f6b9501734cd053cefb7b9b934 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..116ee9be91a190128993eb728c32f06700063574 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8672a9c7c10c7dd0f1efee665e51fed7ef08c266 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dfbc61f0ee8938d292bf1fc7faebc06f56eae6e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c8ffe27cf7ac2364214d8fce23e6f50d00050136 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8486d0af507978fe632a7408628d7bfe53760c67 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9cf3c48b9a47602e637ba8b61ed125b5105d9539 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2268775a7d98bbdfe3ff2deace0509202d0daaac Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bdfc400f7537137cc3b4ee438d3e3201a9ac30d5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx new file mode 100644 index 0000000000000000000000000000000000000000..100fe70446bee6b910d3714743f69a53d898b0fd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx new file mode 100644 index 0000000000000000000000000000000000000000..72fb99e682fafa1c94c455b2a6417a35d3150b61 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b28ab2f42bc09ce6294d8d8f647a65dddb858b37 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e52d1e212251c9163e6d75969399161ee51d714e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx new file mode 100644 index 0000000000000000000000000000000000000000..044cc06f26c19c0796a89c79061e77cf1b82d76e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e629ffda18f00a74a37845a236f9b51e4edd857a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..92205a9bd96ecc92cfb6b24327b39829be2930a7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5d9ff3d278325f97734c741e0307fc9610e2a9f9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..081025252b168bc4242f95a02847885950d66052 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6d9df2e7a070a4efffde7464d9bc701bcc0d4611 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cac244378355ad3b72d8088ce762300d532823f8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..78a5acd3013f77194d0b29d7624dab1e899c7bf7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a660d951b8d4d394cc6eafaedef45ef0060fefe0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx new file mode 100644 index 0000000000000000000000000000000000000000..32d454daaa42013bb3f4e709222beaa2fc20a18c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0ce474be7cdf76d9b13884d6488480fb6b2213a6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3529d62f77935613be5bd00844a461fc228bd01a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..754d7fc8c8b8f735ab7e89ebe211be9fb0da37dd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..33a2802a734b856c394fe12126018a98c3579ff7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx new file mode 100644 index 0000000000000000000000000000000000000000..00f6e8c4e8d90a996450fe092dddc7763fbaa9ab Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6d7289da4a33dbd7feee2c477d7d028e23600047 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dfd8244e332eac1875a1116a3f7438229bbb408c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5ccb95ae1cf2ae39a1a5b9e5983f8f18131badbb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx new file mode 100644 index 0000000000000000000000000000000000000000..61fd2637dbfbba3f5315d408429eafe6839f1493 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx new file mode 100644 index 0000000000000000000000000000000000000000..217f05338f0aee310c33f778522dc108b8c911df Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..00b4e83b704c346638a165a753032ee012e4e3cc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2df897edd98028fd2febe69f24434e9612c9a81e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx new file mode 100644 index 0000000000000000000000000000000000000000..97488aaf6305aa9ed89337e66af3b58f53b7254a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d8b9d5f487c39b2caf00b5e2c7a66f4f0db0df2d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx new file mode 100644 index 0000000000000000000000000000000000000000..29b9e8cd621fc7eba5b4d8b385cde8a6f37ee970 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2d903c534f986212184b64a88f035094eb99bbfd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx new file mode 100644 index 0000000000000000000000000000000000000000..aec72e3a967ea872077e58a620d3081ecedce3c2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e1ad6cff7e97e02486fc02403c440d9a8a72512c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx new file mode 100644 index 0000000000000000000000000000000000000000..06a5b460b0719d2b77b01cdae5e5f20a86a61a96 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..86e3cf268033f3ed18a8250c430d6724efc398d2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a6a34652547aff4d9fc9736059b62e43975c4db8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8bdf865cf2e7d4d5d2032cb4ef089c999a1a3641 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3d9a1552827db8445b383ff98e1de14a8861fee7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx new file mode 100644 index 0000000000000000000000000000000000000000..584fe2cdabd727c9c137f9b45434011b2163e5f3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4fc843fb72f70905942e858fe29f6e77195533ff Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fc3d74e8e24fa4a5e975682bcf64ce4cb900df57 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0d4a108b46a5751a9b1269a23d9a11347748fde8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c250d4afbf9ac64f17fc450d75efdfb0ea60ba98 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx new file mode 100644 index 0000000000000000000000000000000000000000..417a7f5a7cb92840619f3f9ed6cddfefddbd11f9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9b599d3f0ff7b0ea13545d693e0f26cbdef605bf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a1f6d165d3f7b6e3df01c0111d1436d01edc9420 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx new file mode 100644 index 0000000000000000000000000000000000000000..031d3308d35d4e61a5419a619b15730a0fb18c8f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx new file mode 100644 index 0000000000000000000000000000000000000000..300b2b4bfc3f43b6d0a9d82bb6368fca8ff1f95a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b4a23b82c60f332eb3efe6795f168dc46a01b58a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx new file mode 100644 index 0000000000000000000000000000000000000000..16a9866e00416290c169d234eeec44895ad51624 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8168c69548710ac5b694e74e978dfa0775b64c9b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7e5e01839e9b29408703e3451b43c1de7a4fbc05 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0eb2da7dfca5662ecff6f0c169ff19f3ec4743ba Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx new file mode 100644 index 0000000000000000000000000000000000000000..92e3394b6b8974f7510a715df0738b4a1b14a00c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0b9e74cd8831e382bcf5b7d40dc66f3a3f4cd1a1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bf71710dbf0890345b13d11f817bd6d2e9117f28 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6d828eb350aa115af814062fcf6a9ca7338c19f5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx new file mode 100644 index 0000000000000000000000000000000000000000..099947ed552dbe68cbb1bd7cfbe7a2b8a6be0da6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ed63bae4f81ab4de9fab4adc89685872d9c8b0be Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6daa96d5429b64409433d26ae8c344dfd616e1c0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx new file mode 100644 index 0000000000000000000000000000000000000000..342556e10dc02ea8dd5782e81ae188ebe7413e4f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e090efd2e6a0c36911d3881f7ba4f8bb74111944 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b92652758f2e37d19a07e92e9ef0d0b977f92959 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3b2c0e1f950d80a0d7c8ce2fa4dd545ca97f3a08 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d4d21fc9bbe685ac90920097e268b1b640f34ab6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..49e2f06fce1239ee3e6c351c556272bd9f6fc6ee Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ef355a0eed9b1d94b80b15fcfb71257800fdf13d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx new file mode 100644 index 0000000000000000000000000000000000000000..56461e805022edc750dba2e14c9b0c925923228b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f76a7c010eb510cef972538b70dbfc1c33136db9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..28ebf385cf22e82be75922eb15d3c3dca7961c9d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5034a1f819e48a5aa5b78006f8d6d285db9afadf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dbb01e139190c6f7cee5eee943a34ec0ad68ed2f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b5697f6ce5c102e31a79ed35d0fc3619ef322263 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8da352fb796608ac33269c83dfae9ac4dadb9b68 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx new file mode 100644 index 0000000000000000000000000000000000000000..39963e29cddb310a21b6ab7916325d279fc835f5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3dd977a73b36515a68276eb35471af8f78d92d93 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d32a78212f04ae3aac89b4f80aaeb1d8cf158647 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e990000b160bb4f64f0dd078926479e6c076d326 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8e48bbca743a6bdd2a2608038825613c51ec4b38 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c744b176573babaf6c706539744bd21d3eb53d63 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8b7e53848ae945398ff9dd97f6ad9ae2dbb8cd6b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d7156aeeb198f168f744e8b1c00165ca700e1c17 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx new file mode 100644 index 0000000000000000000000000000000000000000..43b8227761ba7c3f20127044fa2b49501fdaa256 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ac06d938dd7d1da6b2037746db3cf61b6500fbbb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5780bffad658c15edda6ea7ae90f71ddf485b72a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8ee12d5340b087a2e6d62111842fb3dd9570d706 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4ffb2ac210062eafa30ae45cc8c27d0c1ef3ae9d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ddfb512ac08f0560a56cbe1508ffc9ad6505fc8c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..79ad8b9191bef369ccdf195eae0ef069a2198d09 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx new file mode 100644 index 0000000000000000000000000000000000000000..10a92aa9fdde8e1e0c0c5567a96153f2b36e6f09 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..03faa96bdef87d7bc8cbb0d9c561d8607dd92cd8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0bb6cd7737c48c838f7196a894e56539bf8ac964 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f6965e49f32217b1423ec0993b7f4ba02aa788f6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx new file mode 100644 index 0000000000000000000000000000000000000000..34fa144e1ddab8a1c862b66913ab94bd6a660281 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx new file mode 100644 index 0000000000000000000000000000000000000000..10581a036093ce46c7027b6d240f45e9670350f8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4111fffc0322fa88d432ee3a53cae74774bfa98a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..25ebec813b0cacaf855fd1920e180402a1bf6ddb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..50e9501d624f1572be0fe8826170ff7ad7110948 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx new file mode 100644 index 0000000000000000000000000000000000000000..388a30f355f7d6da912aa05256ceabe19644c969 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ea957fb340f6cb49b7296d2b52e5d120c28c33ab Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx new file mode 100644 index 0000000000000000000000000000000000000000..62538f2a1aa7980c2c8ad068e7f41c2bf7b7ee28 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3fdba0e5908435a8d8ade2cd7a0bc4106d3de931 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6140e900a41bea07ee2ed062198e825269bf142a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3b377481f5f410767f22302ab1e24863dd5ed641 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cbd7edec80661f676770c5dac7dd892e2f794f82 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e90d4804e68592fcacaf40ef6b3a377e7d830fc4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6fa483fdd123026f0f7237a092a855f7e4825538 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b2cd49ec3672c0b728130660b184f30bb6eab10e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c15af76e8fb1a8373cc268cdda5a760718509397 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4422e72bd3dfc2046830bd27368d1018f585aa45 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx new file mode 100644 index 0000000000000000000000000000000000000000..aa6a5df2dde595120b48ea7f7a20a371766770db Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx new file mode 100644 index 0000000000000000000000000000000000000000..79a262c7d68f7d80eee1e4c452cad29bed3e716f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0f1831265812eccc7f88f5b3497a906f9b468146 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..96270285970706cf8b5bff60e6a595849fb20dbc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ddf9eb6bec5629bf9d8c86432c0d843bd71232b1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..77b0662814678f80e75dbcb0ffe6eac0777a9798 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1866a0da59cc221891ac223007fd10011c11a321 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f03ab9148716777d1c96511b062193cf9a3cf5ea Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4f1639ebf3de2cd0f267e4c0a66ecd56f671e23e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx new file mode 100644 index 0000000000000000000000000000000000000000..26270dcfb3843ac380c4261bb9f32229703023f9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cf3c5f2bddecf79fc21e35afd1199e83bbcb81d4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx new file mode 100644 index 0000000000000000000000000000000000000000..616dd09565d5dc2df0f71ca42a6b17293f35c3cf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx new file mode 100644 index 0000000000000000000000000000000000000000..32f30e18ba265e7a5a87b65769d7e02c59b0bf41 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a30ea443abe55c1a6608ef97c34af0eb5c2081eb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx new file mode 100644 index 0000000000000000000000000000000000000000..38cdb5844bf0f70719b291bcbc79a8814e9685c8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx b/developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b6a0c27592e3cea916ceba55467637e3cd1e2724 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/batak.kmx b/developer/src/kmcmplib/tests/fixtures/batak.kmx new file mode 100644 index 0000000000000000000000000000000000000000..953244d282a6b529d84f055d9c0393afaac7a6c7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/batak.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/baybayin.kmx b/developer/src/kmcmplib/tests/fixtures/baybayin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a98eb66f7f741c35c3d684e0a415283506295070 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/baybayin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..59dbfdf7b677eb64c15479c5b3d09dee4d31ed8b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx b/developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx new file mode 100644 index 0000000000000000000000000000000000000000..db3ae952840d7387e35ba3dcfe32c16b7b05e149 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx b/developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c4ffe3262f314b79b78ad5b9d3bd0421b1c3e058 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx b/developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f8a7734542e5ea6e5c1b6287309f35644ac683e4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx b/developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx new file mode 100644 index 0000000000000000000000000000000000000000..da25ab5de88e916913f552e2119b2c3d61c95dac Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx b/developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a4da2173b7c1a740d9eb3d6333912f82cf087016 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu.kmx b/developer/src/kmcmplib/tests/fixtures/bj_innu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3071a9c18dc8628363d4b5883bd182908b1684d0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_innu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx b/developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fef5055f9921b55df24acb81cdf8494067814539 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx b/developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bc297ffb33ba8ad9f798dcc995525ee51e486b46 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx b/developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3308462a43cfc2829fe65b659e944c9cb4cf8319 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx b/developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c7e3cba86992dbf8bf693db89bf26ae614611033 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx b/developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d81548c7cbce5833c925d91cc552924d857cf993 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c2a6a0210e97ad9f02bad1ac184379b96b133e63 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx b/developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ce701d60552742aa52fbf0d91b86b4dcf1474085 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bf5f8b54a221a6431ae78976d1711c22ac6bc42c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/buhid.kmx b/developer/src/kmcmplib/tests/fixtures/buhid.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b379b84bb7cc0fd195e01e400fbb7a4535b64d53 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/buhid.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx b/developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e472e1e658a5cd6cde16d0bfd61f63f886423aca Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/cabecar.kmx b/developer/src/kmcmplib/tests/fixtures/cabecar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3122d53320713b7c74d33d4bb6938c8e6f520e3f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/cabecar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/carian.kmx b/developer/src/kmcmplib/tests/fixtures/carian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..28788cf0904f9fb4938e678fe4840d49ad5db040 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/carian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx b/developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d8a7799b3b7851e202e03bd4cbdadb1d1e802971 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx b/developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..619a3b42ca28f22a4cf48b4e9582522920cc8142 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx b/developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e902d3fda7795477097133c31be865e930415784 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx b/developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b995728e16734a9ec38ae36fc0457e14f88d2803 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/cim.kmx b/developer/src/kmcmplib/tests/fixtures/cim.kmx new file mode 100644 index 0000000000000000000000000000000000000000..25096b14a305e53fd44604a1ff9dc5e926fa4544 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/cim.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/clavbur9.kmx b/developer/src/kmcmplib/tests/fixtures/clavbur9.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3a7c6594a389ecf55e376752ca7367a3e8638bb9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/clavbur9.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx b/developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ab7f85d3d7b2aa1b7edb3b668772f0725ae5b8ee Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6eadc7175738b4cfd4ce2bfbe614233c5e5dab68 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx b/developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ec1329475e129d8d65d917b840408b2c357f53f5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx b/developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e380cea9530c6f1a56354f0cb33cf049071ace65 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b65388efb15306edab1f536d33f0031c34186214 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/dagbani.kmx b/developer/src/kmcmplib/tests/fixtures/dagbani.kmx new file mode 100644 index 0000000000000000000000000000000000000000..31cfe3998102576fbdb4840cac412db8235d0fd3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/dagbani.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/dega.kmx b/developer/src/kmcmplib/tests/fixtures/dega.kmx new file mode 100644 index 0000000000000000000000000000000000000000..31e3373f7620d3422bf9860149e72ba7cf8d16d1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/dega.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/dene.kmx b/developer/src/kmcmplib/tests/fixtures/dene.kmx new file mode 100644 index 0000000000000000000000000000000000000000..835bbf1a6386deded575d414686f0244b16cf06c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/dene.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/deseret.kmx b/developer/src/kmcmplib/tests/fixtures/deseret.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a9f264657267b916487d671128b587793dad4e2e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/deseret.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bae47782435070bd0d7827be7732eaf648818b81 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx b/developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx new file mode 100644 index 0000000000000000000000000000000000000000..df87cec7e7cadcc1278dd21a81994a93b0c8203a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx b/developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5b2bf899f9faca2030272add94c56cdab52c57dc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_dinka.kmx b/developer/src/kmcmplib/tests/fixtures/el_dinka.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5d0983836de38f86344012fe99c0eb835bed5adc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_dinka.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx b/developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1dd0c497b2bb3fb655ba3ea944188574be8ff49a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_naija.kmx b/developer/src/kmcmplib/tests/fixtures/el_naija.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9a5456e2140d1e3afec1d348cbfdce888af479c5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_naija.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_nuer.kmx b/developer/src/kmcmplib/tests/fixtures/el_nuer.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1b418368fb99612d4ddc01c304b9785cf2e4b3e5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_nuer.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx b/developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx new file mode 100644 index 0000000000000000000000000000000000000000..08a6cc250ea90368353cbccfb0a63e07dc3d6efa Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx b/developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0233d5c10f5117bef330618cb72f743a2db59dbe Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/embera_north.kmx b/developer/src/kmcmplib/tests/fixtures/embera_north.kmx new file mode 100644 index 0000000000000000000000000000000000000000..601a25116c877a9f3ab6c1ea0ff7086bc8511d5f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/embera_north.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/enga.kmx b/developer/src/kmcmplib/tests/fixtures/enga.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9543f557b9d4230dc4f53be848695885adacd21c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/enga.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/enggano.kmx b/developer/src/kmcmplib/tests/fixtures/enggano.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4e8ddd14db1a8bc550ce6474daa8fb5ea65c6760 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/enggano.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx b/developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx new file mode 100644 index 0000000000000000000000000000000000000000..628d7479471acba2abec59f96b2b03062bca1ddb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..92ac37c693a1eb690136d15b6c128925b9bfb65f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/engram.kmx b/developer/src/kmcmplib/tests/fixtures/engram.kmx new file mode 100644 index 0000000000000000000000000000000000000000..83f5d167f1d264bffe4d245317cd1b7c3b152471 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/engram.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/esperuni.kmx b/developer/src/kmcmplib/tests/fixtures/esperuni.kmx new file mode 100644 index 0000000000000000000000000000000000000000..093a94a107b73e0746e84e5b9b64d5befc7c2b0e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/esperuni.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/farsiman.kmx b/developer/src/kmcmplib/tests/fixtures/farsiman.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f5ac429891f5d2897a63b5a7b5ad53aa29d1ede3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/farsiman.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/finongan.kmx b/developer/src/kmcmplib/tests/fixtures/finongan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..122cd34d4562d9223a9afcde898107cfc45bb98c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/finongan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9bf05cacc81564e5415893275616b25e7db8cac1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9df1ae0728213a99f413b6db1ad7cb40ba9eaebe Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx b/developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1ab7508254240f730c718a4b75b0d0039e97d962 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx b/developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4f5f5c2343cb1b1c443cf504843cfc3b83a239b6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx b/developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a55a6f0e731ac910277eaba75290e8d587fbb59b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx b/developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dfae3c8f99da1aa4cbb92dbf3462021980c21aba Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx b/developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3cf3b42f11f4f0e53005bbb04642ee58263c08b7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx b/developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f99115394adcf306c53205ac9d88df600c052ea9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx b/developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d39dde6a48a5b0f717a563f5620ceb8f182b8eca Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx b/developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx new file mode 100644 index 0000000000000000000000000000000000000000..90b43453b2cef92d876a25a0fd1e1817daf6ee4b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx b/developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0572a5f9fcc56c53b1ae448d44154398fda637de Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_han.kmx b/developer/src/kmcmplib/tests/fixtures/fv_han.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0de6f7d9226fa9aed0834bc0184c133dd0f10b2c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_han.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx b/developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e2d4968e537c176bde029e7b45e258fec88664e3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx b/developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx new file mode 100644 index 0000000000000000000000000000000000000000..127e612a29259d059a634e5e661f50cb963640b5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx b/developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cfaf38533b9a0a836c41dbd8b82d83fb406d0b67 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx b/developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e893954de83c15c3dde7d48ceea393269965ea11 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx b/developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d4f0f395b42eb0db6c468c4832150233e9bf9398 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx b/developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx new file mode 100644 index 0000000000000000000000000000000000000000..13444e38f321b0e7e67e217ae33e8ba83b0fa4ba Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx b/developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx new file mode 100644 index 0000000000000000000000000000000000000000..05cab8441f0bd949a229b2f24a709b4aeab746c7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx b/developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx new file mode 100644 index 0000000000000000000000000000000000000000..55f1f49e2b3cc67cecb8e6cbbcaebf36d5576536 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx b/developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4e020950bca6db5389ffa79827d784f0bfadf453 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx b/developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c99549d94ffc9c23474c064807f6ec86057c23a2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx new file mode 100644 index 0000000000000000000000000000000000000000..27e05e8115182ef449af96a11721d4fdcb40c5f2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx b/developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1732ce5c3731594d1b5d2b4877d11343b35123e1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx b/developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0fa9e4ca53c46d2ee5b8e5c187d19c308ccf854a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx b/developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c87c548e0a777de94549ec64329cbf7c18df399e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx b/developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx new file mode 100644 index 0000000000000000000000000000000000000000..049ac7008585a3303179eac180659df286591ba0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx b/developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx new file mode 100644 index 0000000000000000000000000000000000000000..200d72f3a349322ce43d6a251c50257234c7c634 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx b/developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cdc7b6b3be8e99ac3636a901d6763a00199528a2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d500f6ea9d568d3192d62125af138a3dc0ddcd67 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx b/developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx new file mode 100644 index 0000000000000000000000000000000000000000..012755488ab0cd35c25a664ff5798fa799cffa1d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx b/developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx new file mode 100644 index 0000000000000000000000000000000000000000..01b8ba7bdb9e84e39d4576f13d59f3184e94d175 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx b/developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx new file mode 100644 index 0000000000000000000000000000000000000000..838054ba56178428342ee8504d724f9c0126d6bb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx b/developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5d7f52ce64e6329ead9cef9387cae765c6b380b4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx b/developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx new file mode 100644 index 0000000000000000000000000000000000000000..80f13d6898224217be7d37b385db2e095ccd7f19 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx b/developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx new file mode 100644 index 0000000000000000000000000000000000000000..277d17c7e3c2e30c25273021c3bc0656e80158f2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx b/developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..346221385782533a2c685470ffd34f39e317c435 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx b/developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx new file mode 100644 index 0000000000000000000000000000000000000000..129819a755e08fe4285c2af889f74849724a06fc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx b/developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5a98769f3bc6230d6643bd0d5dc90bceafd7394b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..111e206752c64c8c542ab949580a76974be3be53 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx b/developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dbdcfc4e41b4d7971a8abc24fc1542407f6f7aff Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1773d43a20ce7aafd29af816897f3adadccece85 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx b/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ca089a872773c2ef123f6521394010db71754063 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gandhari.kmx b/developer/src/kmcmplib/tests/fixtures/gandhari.kmx new file mode 100644 index 0000000000000000000000000000000000000000..880b46b6ed31e0ee448b2fe9315cca412feea015 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gandhari.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx b/developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..57d4f37e9359609f8fa01db63a223661a30ff404 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx b/developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e265cc988d7904e2d9a6777c36b1fcd98eeffd81 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx b/developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ba94ba13cc3d415af322360496db419baf861570 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_blin.kmx b/developer/src/kmcmplib/tests/fixtures/gff_blin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5c69d20dfc445dd73de9a63cbdd9877007f7cb77 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_blin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx b/developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6dbb905de67c2428b1d13c112c953462ada8b3e5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_geez.kmx b/developer/src/kmcmplib/tests/fixtures/gff_geez.kmx new file mode 100644 index 0000000000000000000000000000000000000000..08cd45f84ca618d648850971fbaf712a26618c34 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_geez.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx b/developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eb108c5d528031b73c848bcabe8ffbd73e35f6f7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx new file mode 100644 index 0000000000000000000000000000000000000000..35456108f22df91d110264281d9d1b9d58aae34d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx new file mode 100644 index 0000000000000000000000000000000000000000..be00b433cd84ecd44e1dcec2b0c7f285652f5e87 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx b/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx new file mode 100644 index 0000000000000000000000000000000000000000..32e7202e58bff33c41c8f105a57b50ae613994e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx b/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e2aab809091d3a481e0972ae70f15bc67f7c8b51 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ghana.kmx b/developer/src/kmcmplib/tests/fixtures/ghana.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5ef98ec4d8a2cb2303b506de4c98723e88ab8cd0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ghana.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki.kmx b/developer/src/kmcmplib/tests/fixtures/gilaki.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6e73cdb3007642b99f4d32edb0eddfcfd1dc9032 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gilaki.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b6d7c24ae246cb89e146f4126601f60b84305253 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx b/developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a11e9dbc652818d10d31a69e7390ef5f32ba3e4e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx b/developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx new file mode 100644 index 0000000000000000000000000000000000000000..40635fdec156f72101dc15b0f153154f8467c6ff Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx b/developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f53e419795040f7ccd55d1f92d65f9869ed0e0f9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/greekclassical.kmx b/developer/src/kmcmplib/tests/fixtures/greekclassical.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b52d6446635a8519646b522ea71806d3f40adf17 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/greekclassical.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hanunoo.kmx b/developer/src/kmcmplib/tests/fixtures/hanunoo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e27248f01e2fcfe720efcde8e778508f044eb0fe Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hanunoo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/haroi.kmx b/developer/src/kmcmplib/tests/fixtures/haroi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ab97cbb1b95937c779dac0be6ed0885849d7eff3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/haroi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a9c7206c16ba3ea953d6c9693fe259e9653ed94c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3893c37d1245a19623d58474a1776a1d044f95b2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx b/developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8b670fa110fa8c59703bf693a3354ee2d8e0b789 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hcesar.kmx b/developer/src/kmcmplib/tests/fixtures/hcesar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd2ac004d673e80d7c6f675b40316bd264c02b20 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hcesar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx b/developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..20e616e0e0c73f4d06299ff03f28bc97110d414d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8d6dd77e25fa8250c4010f55f3022e591cd17bf2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx b/developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ff755dabe0a6975f4385c0ab926dab23d90b7f29 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx b/developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d5c20049c17e30fe7b04a4a42a8f5b65584e87b3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/indonesia.kmx b/developer/src/kmcmplib/tests/fixtures/indonesia.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4141cd42785a7820c5726feafd8fc7a7338d627c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/indonesia.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx b/developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c7c77ad062f0b0bff7546e177585408449135a1a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx b/developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6cd4ba4462739e4b699f8baabdf360574653d27f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..512a576c4a39f0a295ad3d3bb1380cdcadf513fd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx new file mode 100644 index 0000000000000000000000000000000000000000..49103e231a450590c650734b9307208c1c4020b7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..69a3bed26648b58ce109c15bb496d470807ca383 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..06adc62f382f179e372a4196c3084770ccbe1933 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d46598d8d257e5ae384b0f8e8f182ef4f394fff1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c56a17b39c0c50d271acfe95ee76d39be392b2e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7cc9c171e1485322a6503a8d811cdae0d87daffa Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx b/developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a019db3923b33f3ed246b61d0ae0ff7da93aeab0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/jawa.kmx b/developer/src/kmcmplib/tests/fixtures/jawa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..709021f21c647106129009b114108bcbd928e303 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/jawa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/jorai.kmx b/developer/src/kmcmplib/tests/fixtures/jorai.kmx new file mode 100644 index 0000000000000000000000000000000000000000..519a4d2fab27ddd26222c317f3f5465a97291ad6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/jorai.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a6dc30f7d1bcccf133d83f48ffde542713763eec Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx b/developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..919e2ca18521f230ec17f12939094c9b6216052f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/kayan.kmx b/developer/src/kmcmplib/tests/fixtures/kayan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7aec141c66c6b5474696dad690cc7fd9a5325fa3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/kayan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b580f1905d6c8c1ee3585a538eca9d0796335c23 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a47fe866cdc0bbd1c674d771613b9ffa05c37d6e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx b/developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cc95b2cca78b5d12eedb33ad5647f3aad7005eed Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx b/developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d8495d187c4ea369dfa0b608d8a630e196360f16 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3ace43836be93fce942070daffce2bd9f6017b67 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx b/developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e0b8576fa6ec6d9f360c3c96cbd8da5229a670cd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/koalibrere.kmx b/developer/src/kmcmplib/tests/fixtures/koalibrere.kmx new file mode 100644 index 0000000000000000000000000000000000000000..abd7ba92100cdd3f02d3b6919b834a6b9bef3176 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/koalibrere.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/korean_rr.kmx b/developer/src/kmcmplib/tests/fixtures/korean_rr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..74057b8d43911cd0ae24229922ba290bcfd8ff9c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/korean_rr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/koreguaje.kmx b/developer/src/kmcmplib/tests/fixtures/koreguaje.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f99ac0c0a7b49efe549ff78694c15c1ed4d05b3d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/koreguaje.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/krung.kmx b/developer/src/kmcmplib/tests/fixtures/krung.kmx new file mode 100644 index 0000000000000000000000000000000000000000..596ed3f502c9365c712e83b8a30e5a7dae7aad0d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/krung.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lahu.kmx b/developer/src/kmcmplib/tests/fixtures/lahu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8d7ecec5ec27346d913aaf6067a1efbf644decfc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lahu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lamkaang.kmx b/developer/src/kmcmplib/tests/fixtures/lamkaang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eeb73b13ae5a47337c7559305f3756762b40c3a7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lamkaang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/landuma.kmx b/developer/src/kmcmplib/tests/fixtures/landuma.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2a106877d39f3d78b7609d4ed729e945f5129f8b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/landuma.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx b/developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f6a3cf52400b2a553875f4263771d9be9d724eba Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx b/developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7c19d841b50a447cea5207384f95f090fa3b2e03 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali.kmx b/developer/src/kmcmplib/tests/fixtures/lao_pali.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f2579f0f2193969f2b03cd24a9d940cf36f15a38 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lao_pali.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx b/developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx new file mode 100644 index 0000000000000000000000000000000000000000..90023f2e74297898083cef88fa2f4da6d5c32b81 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9e7abb7f8263e8263e050e242df560292d04390d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/lazuri.kmx b/developer/src/kmcmplib/tests/fixtures/lazuri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..19c9dec7358fbfb2b220083ff2f9332a380e15c1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/lazuri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/libtralo.kmx b/developer/src/kmcmplib/tests/fixtures/libtralo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..df52a88479248b8a1304baaefa32438da74e60be Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/libtralo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b630f2a8995bf6c73173eb975bea356831555cca Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/malar_braille.kmx b/developer/src/kmcmplib/tests/fixtures/malar_braille.kmx new file mode 100644 index 0000000000000000000000000000000000000000..069ed73292d4953c57955c6b1bd5d1a06ac61949 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/malar_braille.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b34931675b40ce95aff0e5edb7ad25f9e96384b2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..06984b9a682c11d4ef192705e431ef1f29e63ec0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx new file mode 100644 index 0000000000000000000000000000000000000000..de13a1a8c3ce27e10ff0f66e85973f15ad2b3173 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/maltese.kmx b/developer/src/kmcmplib/tests/fixtures/maltese.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8d083c4015315bf6a363b0a851119307a78096d4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/maltese.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0fd9217afb99d5601c03e222c650cb624786a12d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx b/developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ecf782860c0957caa0f60da23facd581c9bfc959 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/me_en.kmx b/developer/src/kmcmplib/tests/fixtures/me_en.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d9770fb8bb07953099881be8adb2ce582ef4e73f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/me_en.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fd43336cc69f8f891cc895b891ea76efd04ba223 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx b/developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..42057fdfb8ff0ef4d1e9c172c4078f29a8c2b555 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..caf54dfe1ca0bcb2cb92235cfe15a379fe3152f5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx b/developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx new file mode 100644 index 0000000000000000000000000000000000000000..95edf95455a0aec0915daf6343ccaa530e7d7a48 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1f8e5504d0bfb007698ae0a0df65a0e1f94e5f1a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e20fee204f1f5dcf11b9cb82ba8431abdedd477c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1c0137b88ca13c9bf05997b2075c1c77ba9fe8f7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0c24868d1e00e08d85c61aff1a55baf284616008 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..db5c54f6aa989d07a587ea53a78060b3ea0a4aff Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0f3ebfe1c996f42da41c7a0d3af9dca54c9e7234 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/munji.kmx b/developer/src/kmcmplib/tests/fixtures/munji.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bfc51c4c8681c0822b2b94250bbcb089fe41b664 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/munji.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/myancode_san.kmx b/developer/src/kmcmplib/tests/fixtures/myancode_san.kmx new file mode 100644 index 0000000000000000000000000000000000000000..95987ef8fabe2ce1ef3a4a0c4ca8c919ef7ac927 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/myancode_san.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f8e708fff9f7101b1698a20ee90f8f6cca16ecbe Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/naijatype.kmx b/developer/src/kmcmplib/tests/fixtures/naijatype.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d2345dfda7744bbecc4bad7e26d882cac87e24d4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/naijatype.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nailangs.kmx b/developer/src/kmcmplib/tests/fixtures/nailangs.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8dd07c6a088b2ba9d39ea782d652e13110def88f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nailangs.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx b/developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx new file mode 100644 index 0000000000000000000000000000000000000000..de011fdf954e454dfb6ccedc71b41031e06a20d7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx new file mode 100644 index 0000000000000000000000000000000000000000..206a4ac6d0ccf700a5ba430958c5a4fb81d69886 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1b2282ae0c74050428f565f69f09e3e6a61f48fc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9d1bdce86df35776f040b019c3178dd6876745ed Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nias.kmx b/developer/src/kmcmplib/tests/fixtures/nias.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d7079a039c34a89a9990cad605d6ae348bce943b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nias.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nisenan.kmx b/developer/src/kmcmplib/tests/fixtures/nisenan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a102ebd4b1c2c163d0c5358a5004c62ae42d08b9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nisenan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nko.kmx b/developer/src/kmcmplib/tests/fixtures/nko.kmx new file mode 100644 index 0000000000000000000000000000000000000000..69705f32b7467a77a723debace1709547f591d54 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nko.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nkonya.kmx b/developer/src/kmcmplib/tests/fixtures/nkonya.kmx new file mode 100644 index 0000000000000000000000000000000000000000..62812dab2755de7321afcf0520dfb83a80a66b8b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nkonya.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a99f0a6d1bf637acf6c702df81ed248de1d47226 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..894ae528cf41c25bc6c0c220103f5f222d4fbf18 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..171939f8f03724a59ff01a55acd117d5529c7245 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..cf966d1f791fa916c0b14c1f515eb9de2ad7c0cd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f7b1bb493e187ef9cc881ceb599c589271891546 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3a96d7f934a88587c04b8d3d2b2669f58f1ea011 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0035303ca43f40f4b0eb8962806cb8a85962cb52 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e3496b0640e231eba8e5083a7324db05f1cfc25a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a9d22bfdedd4ee03ba45f46c788442620f7ce4ef Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..176ce86e7859a924433be3eab0be33374796277f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx b/developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4c5dcebbd9c3c1a03f91a7d3096e764b8c9ca068 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx b/developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e39ae5d918a14b7ba987914169d836c6ce22e583 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/numanggang.kmx b/developer/src/kmcmplib/tests/fixtures/numanggang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6064a1e7d944437e01bad463d4cd43fd63a99603 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/numanggang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2d2b49f6839c14b0bfdd87bacd466127a47fc82e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/o_tissi.kmx b/developer/src/kmcmplib/tests/fixtures/o_tissi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..58515ef2390e23035659b871adad1882083a481f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/o_tissi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx b/developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..873f70b33be411657ac5c5f1632c89ffdf647f3b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7ef2dcdadbbc9162dc995a26d8db37afa18e593c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx b/developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ccb6ca06a48600872e28196197b76e07466f4aac Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3be7e17688a650e0ecfcbf5de9b4d91ce114dea2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/orma.kmx b/developer/src/kmcmplib/tests/fixtures/orma.kmx new file mode 100644 index 0000000000000000000000000000000000000000..63fb4e8cc043dc9951cf743c92d2b5816a723230 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/orma.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation.kmx b/developer/src/kmcmplib/tests/fixtures/osage_nation.kmx new file mode 100644 index 0000000000000000000000000000000000000000..69333f2e45922f0225750d6d4b12fcef9311d8b2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/osage_nation.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx b/developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ead429bb10378b05c455543aeaf82d3ee4cad888 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx b/developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3722c24dbf0f8b699ee2beffc5e5a9ef7af44e3e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..89620cddcddbdc1c6626574c298cba247e9f82c2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx b/developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4e8529646c4059e23a58ec2b655b6a01db1a277e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/pingelap.kmx b/developer/src/kmcmplib/tests/fixtures/pingelap.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a54eab2accc03082a23dd50f30d25b19b03c5676 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/pingelap.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx new file mode 100644 index 0000000000000000000000000000000000000000..48859e2af0c9830218fc5d0c2fcf64aee376fdbd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx b/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e8db651d3174033dc2ef8e57393227f998a9c736 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx new file mode 100644 index 0000000000000000000000000000000000000000..322b9fec3b6995d690acedb7fb5cc629c6caf29c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx b/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8b96e384e099b2ef15ece8a19e705e5e2764e29e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/pukapuka.kmx b/developer/src/kmcmplib/tests/fixtures/pukapuka.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8c892107a46f3ce8a9876c01c4e96836c25eb8e3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/pukapuka.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/qom.kmx b/developer/src/kmcmplib/tests/fixtures/qom.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ca6f9fb41dcb37627e78d4ea94abb0a6ecac5b23 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/qom.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/quinault.kmx b/developer/src/kmcmplib/tests/fixtures/quinault.kmx new file mode 100644 index 0000000000000000000000000000000000000000..82f88cf24107c5e8573af4f269d3d9443b009da4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/quinault.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx b/developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8f27dd42d24beb7ee1900132fc0b91ec63feab14 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_aer.kmx b/developer/src/kmcmplib/tests/fixtures/rac_aer.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f32daed2596b947a13f4befee425ae21df69b17a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_aer.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ba7ffbaa404f8e4ed72b8b254d74b1d3b74e6635 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_balti.kmx b/developer/src/kmcmplib/tests/fixtures/rac_balti.kmx new file mode 100644 index 0000000000000000000000000000000000000000..942b98e8933ddf32b4be02fa3333404964c25a2c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_balti.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx b/developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6a0fd1cafad6ab24a55f91479d85bd64dd0c3e48 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx b/developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6e63256900d63d84ec5dcbc5e03303ddda4c890d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx b/developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3864031e7459aacb5380c5da8ed32174faed9cd4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx b/developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8eb9c3359d32ef39dff1a202046c27d7e68af230 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx b/developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3e4abc594ae9f922a01ef0544465f7a3acd7cf96 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx b/developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..77345ca8a07b8f8424a733ce6e156f2fb9493855 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx b/developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dba08659fbe937d34dd7a145c43c9b4047ad73ea Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx b/developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3ae6573be7cb9668b3876478b014a7c5dcac257c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx b/developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d24481e11719943d04f30c14897cde8bb05507a6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx b/developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx new file mode 100644 index 0000000000000000000000000000000000000000..99453a79c50a2de81c6c48744e169efcdfccbd24 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx b/developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7966cfda014a4840b835cb101ecc2e912ba1496e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx b/developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx new file mode 100644 index 0000000000000000000000000000000000000000..de56a3d6a8937f3e8fa7dadd7451bb669837c424 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx b/developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx new file mode 100644 index 0000000000000000000000000000000000000000..075f85bf48fba5505452d63400c32747e4a9a6f1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx b/developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f2c5ee0545011cb7c2aedd06d603622312e8f0bb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..122edb343c7257dc723821f593b4b3d17770bb9f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx b/developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6144b12ccca43999f81e736e3dbb09e526791e99 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_munji.kmx b/developer/src/kmcmplib/tests/fixtures/rac_munji.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3714489c5b487189eed311895b58f5f1593a644d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_munji.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx b/developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7a94331ecbb48aea40899f04f1b310d6847d416d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx b/developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fe4dda40f07fc4f51e84c5dc238c2e7f3a2d6406 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx b/developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx new file mode 100644 index 0000000000000000000000000000000000000000..64d842679af8197ebd744f1c0d4a9daede057faa Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_palula.kmx b/developer/src/kmcmplib/tests/fixtures/rac_palula.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d6a28b1843121f26034e52c18bb00c2127fa4775 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_palula.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx b/developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3dd6be1d5cc3fe681029f232ef363ffbc3423945 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx b/developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx new file mode 100644 index 0000000000000000000000000000000000000000..965feaf6bd1c74be79fcda793e9a70daf8da68e4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx b/developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3d5bb2970f4932f1fc776781e2a554fd3b10663d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1cc25371ed06f48cbd4a4e64f089677ae71e68c3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_shina.kmx b/developer/src/kmcmplib/tests/fixtures/rac_shina.kmx new file mode 100644 index 0000000000000000000000000000000000000000..818c4bb9e0790dfc7c3c3784360e1573412050b8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_shina.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx b/developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7cf18e2146d7a7e052ea746a1858a934537d1a67 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b9917e064b494f068bea8fb9e634f1fff9012705 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c46a0af0e0a8262283704740e1cb9e58ead021cd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx b/developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e518f79d002eaa045b00de034ea588722d78b66a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx b/developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3bf55f93935f044d02c498e46bf1e93a309e152e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx b/developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8b1dcc69bfb24509e5a4ef9343483eecf87c9279 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx b/developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c3acae747f60de7830c118b2869455f5d96ec654 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eac2a21ec01380eacd24c68a99a23fdb718be4e7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5d777ce16c22245b1adfbac9398a7d5a5bf5e792 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rawang.kmx b/developer/src/kmcmplib/tests/fixtures/rawang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5ef43d22be467fcae7f86acfb85e2bfca0b4c192 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rawang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rejang.kmx b/developer/src/kmcmplib/tests/fixtures/rejang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c04ff065e2c4fecaaf16bbac394a805adcd326c6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rejang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/remington_gail.kmx b/developer/src/kmcmplib/tests/fixtures/remington_gail.kmx new file mode 100644 index 0000000000000000000000000000000000000000..16412933b6f09c949dc090c773037d03b64059a4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/remington_gail.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx b/developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx new file mode 100644 index 0000000000000000000000000000000000000000..07e61629b5e5a77cb9447deaaf46c32aa67a0387 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/rossel.kmx b/developer/src/kmcmplib/tests/fixtures/rossel.kmx new file mode 100644 index 0000000000000000000000000000000000000000..98bca0b9464cd808441242cf418896c3f9d1b0c6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/rossel.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/runeboard.kmx b/developer/src/kmcmplib/tests/fixtures/runeboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d41acc4124a158b91a799a7b8986cd04c85cc942 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/runeboard.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e9a99d02db29ac2a1e6b21511f1d406d14e64576 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx b/developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx new file mode 100644 index 0000000000000000000000000000000000000000..1b7f80b8b13135fa8b4b90783404de82bfa6114c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx b/developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bfac93d7e6e6bbc5fd45adaf44084fec2f87d5f4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx b/developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx new file mode 100644 index 0000000000000000000000000000000000000000..53d610e7e743131cb2b30f8d84ac62734334e045 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..739d01869397bfd75b556a5bb89e77eaef8c76fc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/santali_latin.kmx b/developer/src/kmcmplib/tests/fixtures/santali_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5a0756301cc95f11feb352d114b22d3191296c33 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/santali_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/saraiki.kmx new file mode 100644 index 0000000000000000000000000000000000000000..260e96f5ca778e5ffd784bf784c06fe3ea402e2c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/saraiki.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/satere.kmx b/developer/src/kmcmplib/tests/fixtures/satere.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e9af31938826265a6f8f3c9e70538ae4748d8ab0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/satere.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0d8cdb54f77be275dbe5e144b0b15ba9590aaa29 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/shan.kmx b/developer/src/kmcmplib/tests/fixtures/shan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d84a72e3ee815867d64e6e10d2f021112be65bfb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/shan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx b/developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx new file mode 100644 index 0000000000000000000000000000000000000000..87cd30ad34a3ef5fc400a62c8794ebde209e7690 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0f139602947659ba0cec79f6e37752ba845ecae0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx b/developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6a81a33d8b49cde3dbcd7601e54d93abe71b42bc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4caa43ac6410c7803a2dae8cb3b4284ed9e44a27 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_areare.kmx b/developer/src/kmcmplib/tests/fixtures/sil_areare.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8ffe02d000f21ba7ca0e7a7e5229169dddc73987 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_areare.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bari.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bari.kmx new file mode 100644 index 0000000000000000000000000000000000000000..639e50e028e82135e3a0992f2567ea7395e396b8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bari.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dae551bdbd1ccd955b3e366a6d92fe5912e51ea2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx new file mode 100644 index 0000000000000000000000000000000000000000..75cf86a81df962956a5c746241cce26c90deca02 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx b/developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dfd184f1ab4c9d170cecd1039fab0aa051a265f8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_brao.kmx b/developer/src/kmcmplib/tests/fixtures/sil_brao.kmx new file mode 100644 index 0000000000000000000000000000000000000000..80d140b65a28b8c2c2120e686a77b787444fbf42 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_brao.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bru.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bru.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0bb31ea045bec7b946d4bd4b7b8bc04819dccc2d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bru.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_buang.kmx b/developer/src/kmcmplib/tests/fixtures/sil_buang.kmx new file mode 100644 index 0000000000000000000000000000000000000000..288f2fb7fb7e862229b9d1520d8cc63b0ba99297 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_buang.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7eeaf731e1c7bc8cad1018d956c556ed2474e1bb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_busa.kmx b/developer/src/kmcmplib/tests/fixtures/sil_busa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4e907fa2f25088163584b9f2c31901a0c93f681e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_busa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx b/developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8fb25d32a672e15de8c31b2c1758f56e6dd90e99 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..da7f23d2d02fccb73ec655da2b38f85359483f4b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0e3bf67607d2cd58b666b905afd2cb432060b55a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx b/developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c568a85e0593e5a72914edd7fddbd97e990fb60b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx b/developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0fef098219c522cfac894926df2e8280b82d02d3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx b/developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f1eda8ec037084236280f625a52c9e565cfc6b38 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f4a4a85f62eabb51c48da4eb39a5c6d76455ce3c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dbb114e0c97a85c1c921f9b631b1445d519cf784 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..38dbb8008575750b2f51afc7bbaf7eca4a8afee2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx b/developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d0a824cec8b97802b572a3cdabb58fb36eaaeae0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx b/developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7d604831fbeef3c34e6aa02dc4d63c33a349109c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..67834cb25c3f1ff716064e804de91f78615a9f0a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3433f5c103b686d4fb258fc56b499f9cc8340fb6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx b/developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ce697e4e16984959b5c4c37eacfd502daf1c9a5a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx b/developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..60973192d778367acd633c7a9f2995a57bc64ffe Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx b/developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3dd06bd052011e7511d1b54f8a8e4e60f0f72c45 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..904fdeea2b7aeebcd084722787741445a1731ed0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx b/developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..74b83ca88a69defbe7f6d94047a6073f53247fac Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx b/developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx new file mode 100644 index 0000000000000000000000000000000000000000..77128fbd01e4677e05ebfdf11d62a53aa6231e99 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx new file mode 100644 index 0000000000000000000000000000000000000000..18163f9a14acaad09e9049233967f3b7a3cc1784 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0a8646d8ec070edea412ff3a1cd5ed5410d9d8ad Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b39614c21337458bb6df0bdd6d9be4eb20ee5804 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx b/developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f41b2cc8b3c88c94721993ad8a486f11b087e4eb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8d4d9d97e019248a37ad9f39674439a4ef3bbd2c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx b/developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx new file mode 100644 index 0000000000000000000000000000000000000000..90cc3a418d7af01bf19060d94fc8e3c3c5983970 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx b/developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a4214e1dfc88238c0607aad8adccc4da05b625f1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx b/developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5ab026b835f8a0de082f4b8b0059e993fd054560 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx b/developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e918e45b0cf87dbb66f838c70368ab6874fdc5bf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx b/developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx new file mode 100644 index 0000000000000000000000000000000000000000..52ed2dc0e3343ad197758490f6b85362626ade95 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx b/developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c53eb704f1b76c2c07ad391bb17287b212f494e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8cd336d8e4359e029999c0dca4bab35f17c2d81f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx b/developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..630c36cb3db0d08c0792a4fdf714bfd27b0d2093 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx b/developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c03f35d313c301379d34d05e2aed8c4ca8d03b61 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx b/developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e9f48e301300a9258e65296decc80fd1e5e9d90f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx b/developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e2adba2aca2f0d0cf33a488e0e3a683b9898abec Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx b/developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4dedd718537edbf1bca1e0174af80f4cf59e63dc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx b/developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx new file mode 100644 index 0000000000000000000000000000000000000000..663bf4580e7fef1b574ad1798343067e20536433 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..58ecb1a522654d1cad07881fd44b271bd7fd22ae Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..651bcbd28e83e827b04489db2b3788cc959c1a9d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d85fadca1cc584913b4f300f8a7beb8c136ec032 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx b/developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4eee9a9104c5e8d3855739d7a41685d84eeaf77d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..616799187aded941742991fea1afc1379275eff8 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_madi.kmx b/developer/src/kmcmplib/tests/fixtures/sil_madi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d92aff331b7fb8d5c10702e41289b5a01dba8d97 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_madi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx b/developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eec4b8b1b73f6ef0aeed01fd9c22b2b5ff317bf2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..84dd3f53d29dc2c5df805321780b5d44d9df9893 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..29e170f6e6b856e48104699304d34a967ff19abd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx b/developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b5bd3413865538a189d8abf1b29d6a7d03578db6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_moore.kmx b/developer/src/kmcmplib/tests/fixtures/sil_moore.kmx new file mode 100644 index 0000000000000000000000000000000000000000..72e3778d5e0fe59247192510bc40fd6d3b541099 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_moore.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx b/developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ad1cb863a99d36774764967e7e033b82b6870fed Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx b/developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d19b8ecdb53be4ca8f3f2d85385581a08eb9160f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d15171d8a1ee75b810d7423b9969e5797aa1320b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f6d6b9cb0970910d30d028e0fc5664915706ffcf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx new file mode 100644 index 0000000000000000000000000000000000000000..adb38082b23ea4709b27103651d7fd4d251c2b8a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nko.kmx b/developer/src/kmcmplib/tests/fixtures/sil_nko.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5616c8bb5888394246ca02efcdc7325549fdb4cc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_nko.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx b/developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bc178a0659b868841363a80ffe3ba73de23ff4c7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..dd3551d2bad7fbaa2e8854eaa3991e7adeac1faf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx b/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ab7eb053d9d0ad9c65ccf1d2e15e4df3531847b9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx b/developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fd5db7d4f4721bc2cd656f64895f40504c7d929a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx b/developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e5548549a7b9ad70c5cb5f53fa0fd0b5d948a323 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b7162c461ff023be25aa99f50d76413bb1d9ea4a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b74e2888f219f35d0571acc3f736ee382f6d49b1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..91de009800efe678f5250f1ce4d6604f13f38692 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8db0110549ab5c6b21f9ac830358e6a2cfd529ed Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..74ec159fb0d228cb9b75b9558c0bd6e769e5d736 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0afef1c629ba71650c6b0de5531ef920bdceb51f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bfc3da4321e228bc89cb7b1c5844284f3811dd62 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..69f9a2a06742bfb5ac21db819963368a49593af1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2d1eea7adee598daca7daafcc3e08b8da332f165 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c041de1fce019dfa3e1a34fcc298eeb4fbca4d4a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx b/developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx new file mode 100644 index 0000000000000000000000000000000000000000..96212bf1b295c9454719ae25228616ec3c3a1c78 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_shan.kmx b/developer/src/kmcmplib/tests/fixtures/sil_shan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..35a91760f2d4c44bbe95acc0ea48e939a51bd822 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_shan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fe7dd8513ce439d356aa6b1a3f5f73c5cfe3fa98 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx new file mode 100644 index 0000000000000000000000000000000000000000..9e400619ea30b2ef659929f2b1f1bf0b8f38bc78 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e05aa9b5c1335fb2e249aa8dbc308fe7f942ae78 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7db476799ff3ad24c81f3308edb693b4079ca9e7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f863586f92a5978b841387fbf58167a4c5412a4a Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx new file mode 100644 index 0000000000000000000000000000000000000000..19802d37264e2025a174e9c101c6c90925316c9d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx new file mode 100644 index 0000000000000000000000000000000000000000..2a591d64631b22b461893275f80379f07fe1f780 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c31bd27e433574ce77d26a400c586c3ee2766e1b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx b/developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ff3d6abdf02cb3b9ac1ab38bcc35ed21bd18e5fc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx b/developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx new file mode 100644 index 0000000000000000000000000000000000000000..852f028d5175d1d1fb12e7b19e99a70d45e402be Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_vai.kmx b/developer/src/kmcmplib/tests/fixtures/sil_vai.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eac0c3ff1e9343f9ce45660d5da547f8239e273d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_vai.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx b/developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a3a2499d0d369b245e1f9db525a431fc1e2c217d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ff801a19bb893cca42390a936bfe2f799a40c9ec Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yi.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c4bb0ad3ea065c0bc3d6607647b67f8a5c9873a0 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e37835c6ed0d02bb7dcf573d99c5562b070d354b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx new file mode 100644 index 0000000000000000000000000000000000000000..eee1e94330afd297708c62067c1603ef0ea9fe6c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e3c2b997995379ddc7a8a996104139dea24cab08 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b9aadb35b9443407e1c3fed290496129ed568cbd Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..e804035650d537fc0e3af7e625ba0608a0aee43f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx b/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d404a12d71c09a73e0aca6ee6a0cbae28a2738e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a9d75854a331bb82c471a28c4fc773bcc9c8b117 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx b/developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b74127fa0cbdc6aa5b4faf04d444928a05f2f112 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..25955c6bdb49528d398b369b39d6d9f74bebf750 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx new file mode 100644 index 0000000000000000000000000000000000000000..12b4230ab9650314389c92eb773b5dac06257482 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese.kmx b/developer/src/kmcmplib/tests/fixtures/sundanese.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d1ed7f30bb2c79edf881d63a8ba66932fe5a7a26 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sundanese.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx b/developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0b4a82916acfdeaf07271a68d90c53f1266dd725 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0becf6aa5cd34d1d24b8722093f30445c9d0379f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sxava.kmx b/developer/src/kmcmplib/tests/fixtures/sxava.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7a4d83475697630c6573ed49ab11507fca4ec088 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sxava.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx b/developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3023034630e2a8a40bfa0d85116a56385b519819 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx b/developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx new file mode 100644 index 0000000000000000000000000000000000000000..81b39c7d475ea1acea0b162fac839134631438e9 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a35c4a6663a2cab045a3bf6aa3469752ef78a563 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..057cadf299790ce7e8ea871683e8fdff8723a7a5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ad8f6e992f64cc4561042232a0478d55c01a4b12 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx b/developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f0266317fb413dddd8d485bffe9e5519befa8955 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tainua.kmx b/developer/src/kmcmplib/tests/fixtures/tainua.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a7c5302597f8ed0effa0313ad0f15fb3b01f53bf Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tainua.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx b/developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f92abaad901dec4ac4fee457acb06898a8e0dade Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx b/developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4ffcea47677876fd78de071d4505017419633ce4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx b/developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx new file mode 100644 index 0000000000000000000000000000000000000000..30988061fb57ed33cf2cbe4242ecf3d4b32618fb Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx b/developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx new file mode 100644 index 0000000000000000000000000000000000000000..78e7b891b8ef78cd46d1d55ac24d2efdd90f7418 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx b/developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx new file mode 100644 index 0000000000000000000000000000000000000000..5cef67f7a97ea534b8eab41ea69a356e9d0f7b17 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx b/developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx new file mode 100644 index 0000000000000000000000000000000000000000..13b6655801102ad5e2854f1a7e4c64ab9747b545 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0213650e6a946c64a78bd0593a8c5e8b82524db2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx b/developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fc6a07a9c2fc770f5c4798a0f277948d474bfb65 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx b/developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d13c37c692582ed4b0dd07c4183fc1bf460f80c3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx b/developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3db3c74b93494474af41b3d83692c676914fd476 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/tirhuta.kmx new file mode 100644 index 0000000000000000000000000000000000000000..722eacc10b64adff6f00cc309a90ddb5f366db74 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tirhuta.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tlahuica.kmx b/developer/src/kmcmplib/tests/fixtures/tlahuica.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c2a4aa4a8f301504e12633543d8bf86fbcecfc95 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tlahuica.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tsakonian.kmx b/developer/src/kmcmplib/tests/fixtures/tsakonian.kmx new file mode 100644 index 0000000000000000000000000000000000000000..d2530e34be4c0c6379bf18850502bb3c41ef3dc2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tsakonian.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx b/developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a007e67e5528ba90b1b410f5c0cc3ed4b7a02bb4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx b/developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx new file mode 100644 index 0000000000000000000000000000000000000000..805865a77f96af2bc7b0cfd8a38d5053ec205d47 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/txo_toto.kmx b/developer/src/kmcmplib/tests/fixtures/txo_toto.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f54ba9b1ef43379d7070c4a2682e37243d7f79ce Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/txo_toto.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx b/developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0312e80010d26c82f8f129644b791240b94b88b2 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/ukwuani.kmx b/developer/src/kmcmplib/tests/fixtures/ukwuani.kmx new file mode 100644 index 0000000000000000000000000000000000000000..94f0349856314f787bf869d174893bdfe5ed4b3b Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/ukwuani.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx b/developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..a19a27489e3a3e7ed4ab546468c6b77e9f9be5b7 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..74bcef0a5d0782eefcb75247519d14f7bfe02617 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..34cf644632d4c5ae7a3c586c6e77da961da790ca Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx b/developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ecdc3419be5f6a7f96e3ba60420346969226a88c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx b/developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx new file mode 100644 index 0000000000000000000000000000000000000000..277314fd88e903b77de07733d31cb38a7f51ff70 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx b/developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3afbbac8ee54484304e11e98a45b36893ae9bee4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c93a9b5f2bff06a4397274a95d3c8d7133fa2521 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx b/developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx new file mode 100644 index 0000000000000000000000000000000000000000..8ff5d267c9074e8fa1ded4c9a5cd76b89dfae6bc Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..fd86f63d25e71ad07f86c0c4315d55b912f80fba Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx b/developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4ceb2540adc9e0d4110d38c0fa1c092255a5f58d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/wancho.kmx b/developer/src/kmcmplib/tests/fixtures/wancho.kmx new file mode 100644 index 0000000000000000000000000000000000000000..68ab51c6e968cc753c8a76ee18bca6e0f9053b69 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/wancho.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/warang_citi.kmx b/developer/src/kmcmplib/tests/fixtures/warang_citi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f36f714a86f0348578896cad922c334c14a54ed5 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/warang_citi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/wolofal.kmx b/developer/src/kmcmplib/tests/fixtures/wolofal.kmx new file mode 100644 index 0000000000000000000000000000000000000000..b3cc61d41341c86758dc1535b06b7c77109a783e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/wolofal.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/xinaliq.kmx b/developer/src/kmcmplib/tests/fixtures/xinaliq.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4eb748763bf078c80db3d2a5d4474a091ea8ca1c Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/xinaliq.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx b/developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx new file mode 100644 index 0000000000000000000000000000000000000000..3836c816d5729a351009ea5ba0b52d945f536109 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/yidgha.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7544013c02f4c8afe67e7f1cac04ebb08f875707 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/yidgha.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx b/developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx new file mode 100644 index 0000000000000000000000000000000000000000..81761ab9045c7dae54c3e03200f7a661b0609d81 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx differ diff --git a/developer/src/kmcmplib/tests/get-test-source.sh b/developer/src/kmcmplib/tests/get-test-source.sh new file mode 100755 index 0000000000000000000000000000000000000000..13bea90e13219d0aaad84687a2794637df95c0d5 --- /dev/null +++ b/developer/src/kmcmplib/tests/get-test-source.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# +# Finds all keyboards in the keyboards repo (passed in $1) that are valid source +# keyboards (i.e. ///source/.kmn) +# +# Called from meson.build, so this script does not use build-utils.sh. Do not run this +# script directly. +# +set -eu +find "$1" -name '*.kmn' | grep -E '(release|experimental)/([a-z0-9_]+)/([a-z0-9_]+)/source/\3\.kmn$' diff --git a/developer/src/kmcmplib/tests/keyboards_commit_ref.txt b/developer/src/kmcmplib/tests/keyboards_commit_ref.txt new file mode 100644 index 0000000000000000000000000000000000000000..c8db9ade548b72bf9940548e68a41866ff18e50f --- /dev/null +++ b/developer/src/kmcmplib/tests/keyboards_commit_ref.txt @@ -0,0 +1 @@ +965ef1941f20a3bc0cb5d405792b4984cc46cd52 diff --git a/developer/src/kmcmplib/tests/kmcompxtest.cpp b/developer/src/kmcmplib/tests/kmcompxtest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ce542917d389df52ad136047d9b7ae09d15d9dd --- /dev/null +++ b/developer/src/kmcmplib/tests/kmcompxtest.cpp @@ -0,0 +1,233 @@ +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * Tiny frame console app to compile a .kmx from a .kmn + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#else +#include +#endif + +using namespace std; + +vector < int > error_vec; + +#define CERR_FATAL 0x00008000 +#define CERR_ERROR 0x00004000 +#define CERR_WARNING 0x00002000 +#define CERR_HINT 0x00001000 + +int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context) +{ + error_vec.push_back(dwMsgCode); + const char*t = "unknown"; + switch(dwMsgCode & 0xF000) { + case CERR_HINT: t=" hint"; break; + case CERR_WARNING: t="warning"; break; + case CERR_ERROR: t=" error"; break; + case CERR_FATAL: t=" fatal"; break; + } + printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText); + return 1; +} + +#include "../src/filesystem.h" + +int main(int argc, char *argv[]) +{ + if(argc < 4) { + puts("Usage: kmcompxtest source.kmn output.kmx fixture.kmx"); + return __LINE__; + } + + char* kmn_file = argv[1]; + char* kmx_file = argv[2]; + char* reference_kmx = argv[3]; + + // Replace \ with / + for(char *p = kmn_file; *p; p++) { + if(*p == '\\') *p = '/'; + } + + for(char *p = kmx_file; *p; p++) { + if(*p == '\\') *p = '/'; + } + + for(char *p = reference_kmx; *p; p++) { + if(*p == '\\') *p = '/'; + } + + char first5[6] = "CERR_"; + char* pfirst5 = first5; + + KMCMP_COMPILER_OPTIONS kcopts; + kcopts.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); + kcopts.ShouldAddCompilerVersion = false; // So we can compare against existing compiled keyboards that also don't have compiler version + if(!kmcmp_SetCompilerOptions(&kcopts)) { + return __LINE__; + } + + if(kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr)) { + char* testname = strrchr( (char*) kmn_file, '/') + 1; + if(strncmp(testname, pfirst5, 5) == 0){ + return __LINE__; // exit code: CERR_ in Name + no Error found + } + + // On non-win32 platforms, we cannot get kmcmpdll.dll to build keyboards + // legacy-mode, so we'll compare to a hopefully existing file that we've + // been passed + FILE* fp1 = Open_File(kmx_file, "rb"); + if(!fp1) return __LINE__; + + + FILE* fp2 = Open_File(reference_kmx, "rb"); + if(!fp2) return __LINE__; // exit code: fail if no reference kmx file in build-folder + + fseek(fp1, 0, SEEK_END); + auto sz1 = ftell(fp1); + fseek(fp1, 0, SEEK_SET); + fseek(fp2, 0, SEEK_END); + auto sz2 = ftell(fp2); + fseek(fp2, 0, SEEK_SET); + if (sz1 != sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder + + char* buf1 = new char[sz1]; + char* buf2 = new char[sz1]; + fread(buf1, 1, sz1, fp1); + fread(buf2, 1, sz1, fp2); + return memcmp(buf1, buf2, sz1) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder + // success: when contents of kmx-file in build and source folder are the same + } + else { /*if Errors found: check number (e.g. CERR_4061_balochi_phonetic.kmn should produce Error 4061)*/ + int error_val = 0; + char* testname = strrchr( (char*) kmn_file, '/') + 1; + + // Does testname contain CERR_ && contains '_' on pos 9 ? -> Get ErrorValue + if ((strncmp(testname, pfirst5, 5) == 0) && (testname[9] == '_')) { + char* ErrNr = strchr(testname, '_') +1 ; + std::istringstream(ErrNr) >> std::hex >> error_val; + + // check if error_val is in Array of Errors; if it is found return 0 (it's not an error) + for (int i = 0; i < error_vec.size() ; i++) { + if (error_vec[i] == error_val) { + return 0; // success: CERR_ in Name + Error (specified in CERR_Name) IS found + } + } + return __LINE__; // exit code: CERR_ in Name + Error (specified in CERR_Name) is NOT found + } + return __LINE__; // exit code: no correct CERR_ in Name + CompileKeyboardFile failed (i.e. test config is invalid) + } +} + + +bool equalIgnoreCase(const string& a, const string& b) +{ + return std::equal( + a.begin(), a.end(), + b.begin(), b.end(), + [](char a, char b) { + return tolower(a) == tolower(b); + } + ); +} + +// Returns TRUE if the keyboard has a &TARGETS store with +// 'any', 'windows', 'macosx', 'linux', or 'desktop' targets, +// or if no &TARGETS store exists (which is equivalent to +// 'windows'). +bool isDesktopKeyboard(FILE* fp) { + COMP_KEYBOARD fk; + + if(fseek(fp, 0, SEEK_SET) != 0) { + printf("isDesktopKeyboard: fseek(0) failed\n"); + exit(1); + } + + if(!fread(&fk, sizeof(fk), 1, fp)) { + printf("isDesktopKeyboard failed: could not read COMP_KEYBOARD\n"); + exit(1); + } + + // Note: this code assumes a valid .kmx. This is okay for a unit test + if(fseek(fp, fk.dpStoreArray, SEEK_SET) != 0) { + printf("isDesktopKeyboard failed: could not locate fk.dpStoreArray=%x\n", fk.dpStoreArray); + exit(1); + } + + PCOMP_STORE pfs = new COMP_STORE[fk.cxStoreArray]; + if(fread(pfs, sizeof(COMP_STORE), fk.cxStoreArray, fp) != fk.cxStoreArray) { + printf("isDesktopKeyboard failed: could not read fk.dpStoreArray=%x [%x]\n", fk.dpStoreArray, fk.cxStoreArray); + exit(1); + } + + PCOMP_STORE s = pfs; + + for(int i = 0; i < fk.cxStoreArray; i++, s++) { + if(s->dwSystemID != TSS_TARGETS) { + continue; + } + + // Read targets store + if(fseek(fp, s->dpString, SEEK_SET) != 0) { + printf("isDesktopKeyboard: could not seek to TSS_TARGETS.dpString %x\n", s->dpString); + exit(1); + } + + KMX_WCHAR buf[256]; + if(fread(buf, sizeof(KMX_WCHAR), 256, fp) == 0) { + printf("isDesktopKeyboard: could not read TSS_TARGETS.dpString %x\n", s->dpString); + exit(1); + } + buf[255] = 0; // sanity + + if(fseek(fp, 0, SEEK_SET) != 0) { + printf("isDesktopKeyboard: fseek(0) failed\n"); + exit(1); + } + + std::string targets1 = string_from_u16string(buf); + std::stringstream targets(targets1); + std::string target; + + printf("isDesktopKeyboard: TSS_TARGETS: %s\n", targets1.c_str()); + + delete[] pfs; + + while(targets >> target) { + //Matches KMXKeymanTargets from UKeymanTargets.pas + if(equalIgnoreCase(target, "any") || + equalIgnoreCase(target, "windows") || + equalIgnoreCase(target, "macosx") || + equalIgnoreCase(target, "linux") || + equalIgnoreCase(target, "desktop") + ) { + printf("isDesktopKeyboard: TSS_TARGETS does contain desktop target %s\n", target.c_str()); + return true; + } + } + + printf("isDesktopKeyboard: TSS_TARGETS does not contain desktop target\n"); + return false; + } + + delete[] pfs; + + if(fseek(fp, 0, SEEK_SET) != 0) { + printf("isDesktopKeyboard: fseek(0) failed\n"); + exit(1); + } + + printf("isDesktopKeyboard: TSS_TARGETS does not exist\n"); + return true; +} diff --git a/developer/src/kmcmplib/tests/meson.build b/developer/src/kmcmplib/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b81429806e16a3d414c2626c34fba9caa4d2bc12 --- /dev/null +++ b/developer/src/kmcmplib/tests/meson.build @@ -0,0 +1,130 @@ +# +# Keyman is copyright (C) SIL International. MIT License. +# +# Cross platform build script to compile and run tests for kmcompx +# + +fs = import('fs') + +tests_flags = [] + +input_path = meson.current_source_dir() / '../../../../common/test/keyboards/baseline' +output_path = meson.current_build_dir() + +kmcompxtest = executable('kmcompxtest', 'kmcompxtest.cpp', + cpp_args: defns, + include_directories: inc, + name_suffix: name_suffix, + link_args: links + tests_flags, + objects: lib.extract_all_objects(), + dependencies: icuuc_dep, + ) + +# Test keyboards that we have in Core + +tests = [ + 'k_000___null_keyboard', + 'k_001___basic_input_unicodei', + 'k_002___basic_input_unicode', + 'k_003___nul', + 'k_004___basic_input__shift_2_', + 'k_005___nul_with_initial_context', + 'k_006___vkey_input__shift_ctrl_', + 'k_007___vkey_input__ctrl_alt_', + 'k_008___vkey_input__ctrl_alt_2_', + 'k_012___ralt', + 'k_013___deadkeys', + 'k_014___groups_and_virtual_keys', + 'k_015___ralt_2', + 'k_017___space_mnemonic_kbd', + 'k_018___nul_testing', + 'k_019___multiple_deadkeys', + 'k_020___deadkeys_and_backspace', + 'k_021___options', + 'k_022___options_with_preset', + 'k_023___options_with_save', + 'k_024___options_with_save_and_preset', + 'k_025___options_with_reset', + 'k_026___system_stores', + 'k_027___system_stores_2', + 'k_028___smp', + 'k_029___beep', + 'k_030___multiple_groups', + 'k_031___caps_lock', + 'k_032___caps_control', + 'k_033___caps_always_off', + 'k_034___options_double_set_reset', + 'k_035___options_double_set_staged', + 'k_036___options___double_reset_staged', + 'k_037___options___double_reset', + 'k_038___punctkeys', + 'k_039___generic_ctrlalt', + 'k_040___long_context', + 'k_041___long_context_and_deadkeys', + 'k_042___long_context_and_split_deadkeys', + 'k_043___output_and_keystroke', + 'k_044___if_and_context', + 'k_045___deadkey_and_context', + 'k_046___deadkey_and_contextex', + 'k_047___caps_always_off_initially_on', + 'k_048___modifier_keys_keep_context' +] + +foreach kbd : tests + kbd_src = join_paths(input_path, kbd) + '.kmn' + kbd_obj = join_paths(output_path, kbd) + '.kmx' + test(kbd, kmcompxtest, args: [kbd_src, kbd_obj, join_paths(input_path, kbd) + '.kmx']) +endforeach + +# Test fixtures that come from keyboards repo -- but only for a "full" test, +# which typically we run on CI no more than once a day, because it's expensive. + +if get_option('full_test') + + keyboards_root = meson.current_source_dir() / 'keyboards' + + if build_machine.system() == 'windows' + test_source = run_command('run-shell.bat', 'get-test-source.sh', keyboards_root, capture: true, check: true) + else + test_source = run_command('get-test-source.sh', keyboards_root, capture: true, check: true) + endif + + tests = test_source.stdout().strip().split('\n') + + foreach kbd : tests + test_basename = fs.replace_suffix(fs.name(kbd), '') + + # kbd is going to be an absolute path + kbd_obj = output_path / test_basename + '.kmx' + reference_kmx = meson.current_source_dir() / 'fixtures' / test_basename + '.kmx' + + if fs.is_file(reference_kmx) + test(test_basename, kmcompxtest, args: [kbd, kbd_obj, reference_kmx]) + endif + endforeach + +endif + +# Test the API endpoints + +apitest = executable('api-test', 'api-test.cpp', + cpp_args: defns, + include_directories: inc, + name_suffix: name_suffix, + link_args: links + tests_flags, + objects: lib.extract_all_objects(), + dependencies: icuuc_dep, + ) + +test('api-test', apitest) + +usetapitest = executable('uset-api-test', 'uset-api-test.cpp', + cpp_args: defns, + include_directories: inc, + name_suffix: name_suffix, + link_args: links + tests_flags, + objects: lib.extract_all_objects(), + dependencies: icuuc_dep, + ) + +test('uset-api-test', usetapitest) diff --git a/developer/src/kmcmplib/tests/prep.sh b/developer/src/kmcmplib/tests/prep.sh new file mode 100755 index 0000000000000000000000000000000000000000..058d4a1b9b0bfdd50fe13d29bcbdff7af508bd3c --- /dev/null +++ b/developer/src/kmcmplib/tests/prep.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Generates fixtures for testing against the legacy compiler +# +# Assumes that the +# + +# Exit on command failure and when using unset variables: +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +cd "$THIS_SCRIPT_PATH" + +builder_describe \ +"Generates fixtures for testing against the legacy compiler. + + Developer must be built locally and binaries should be in developer/bin/. + + Assumes that the keyboards repository exists and is clean. Will record the + commit hash to allow corresponding CI builds to use the same commit hash + for validation." \ + clean \ + build \ + "--keyboards-root,-k=KEYBOARDS_ROOT Path to keyboards repository" + +builder_parse "$@" + +if ! builder_has_option --keyboards-root; then + builder_die "--keyboards-root option is required" +fi + +if [[ ! -d "$KEYBOARDS_ROOT/.git" ]] || [[ ! -f "$KEYBOARDS_ROOT/tools/regression.sh" ]]; then + builder_die "Path $KEYBOARDS_ROOT does not appear to be a valid keyboard repository" +fi + +if builder_start_action clean; then + rm -rf "$THIS_SCRIPT_PATH/fixtures" + rm -f "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt" + builder_finish_action success clean +fi + +if builder_start_action build; then + # yes, remove existing fixtures before build so we don't end up with stale fixtures + rm -rf "$THIS_SCRIPT_PATH/fixtures" + + # record the revision of the keyboards repo + pushd "$KEYBOARDS_ROOT" > /dev/null + git rev-parse head > "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt" + popd > /dev/null + + "$KEYBOARDS_ROOT/tools/regression.sh" --use-legacy-compiler --local "$KEYMAN_ROOT/developer/bin/" --output "$THIS_SCRIPT_PATH/fixtures/" + + cd "$THIS_SCRIPT_PATH" + + # We don't need visual keyboards + rm ./fixtures/*.kvk + # Nor do we need kmw outputs + rm ./fixtures/*.js + # Finally, we remove any keyboards over 500kb, as we won't automatically test them for now + # TODO: consider if we want to keep those large keyboards in the future + find ./fixtures/ -size +500k -exec rm {} \+ + builder_finish_action success build +fi \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/run-shell.bat b/developer/src/kmcmplib/tests/run-shell.bat new file mode 100644 index 0000000000000000000000000000000000000000..f0a51d786900e77246a032380d8813cab0fd25cc --- /dev/null +++ b/developer/src/kmcmplib/tests/run-shell.bat @@ -0,0 +1,7 @@ +@echo off +if defined GIT_BASH_FOR_KEYMAN ( + set GIT_BASH=%GIT_BASH_FOR_KEYMAN% +) else ( + set GIT_BASH="%ProgramFiles%\git\bin\bash.exe" --init-file "%ProgramFiles%\Git\etc\profile" -l +) +%GIT_BASH% %1 %2 %3 %4 %5 %6 %7 %8 %9 \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/uset-api-test.cpp b/developer/src/kmcmplib/tests/uset-api-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fe599ca2e83a1af9d50d61368bafa4b337f3c82 --- /dev/null +++ b/developer/src/kmcmplib/tests/uset-api-test.cpp @@ -0,0 +1,98 @@ +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * UnicodeSet API endpoint unit tests for kmcmplib + */ + +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "../src/compfile.h" +#include + +void test_kmcmp_ParseUnicodeSetProc(); + +// std::vector error_vec; + +int main(int argc, char *argv[]) { + test_kmcmp_ParseUnicodeSetProc(); + + return 0; +} + + +void test_kmcmp_ParseUnicodeSetProc() { + { + // null test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[]", buf, bufsiz); + assert(rc == KMCMP_USET_OK); + } + { + // basic test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz); + assert(rc == 2); + assert(buf[0] == 0x41); + assert(buf[1] == 0x43); + assert(buf[2] == 0x78); + assert(buf[3] == 0x78); + } + { + // bigger test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[[🙀A-C]-[CB]]", buf, bufsiz); + assert(rc == 2); + assert(buf[0] == 0x41); + assert(buf[1] == 0x41); + assert(buf[2] == 0x1F640); + assert(buf[3] == 0x1F640); + } + { + // overflow test + const auto bufsiz = 1; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz); + assert(rc == KMCMP_FATAL_OUT_OF_RANGE); + } + { + // err test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[:Adlm:]", buf, bufsiz); + assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY); + } + { + // err test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[[\\p{Mn}]&[A-Z]]", buf, bufsiz); + assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY); + } + { + // err test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[abc{def}]", buf, bufsiz); + assert(rc == KMCMP_ERROR_HAS_STRINGS); + } + { + // err test + const auto bufsiz = 128; + uint32_t buf[bufsiz]; + int rc = kmcmp_ParseUnicodeSet(u8"[[]", buf, bufsiz); + assert(rc == KMCMP_ERROR_SYNTAX_ERR); + } +} diff --git a/developer/src/kmcmplib/version.rc b/developer/src/kmcmplib/version.rc new file mode 100644 index 0000000000000000000000000000000000000000..b963c10ddb1be06b12eec81667301c4f551c3cc5 --- /dev/null +++ b/developer/src/kmcmplib/version.rc @@ -0,0 +1,32 @@ +#include "../../../common/windows/cpp/include/keymanversion.h" + +1 VERSIONINFO + FILEVERSION KV_FILEVERSION + PRODUCTVERSION KV_PRODUCTVERSION + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0C0904E4" + BEGIN + VALUE "CompanyName", KV_COMPANY_NAME + VALUE "FileDescription", "Keyman compiler library\0" + VALUE "FileVersion", KV_VERSION_STRING + VALUE "InternalName", "KMCOMP\0" + VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS + VALUE "OriginalFilename", "KMCOMP.DLL\0" + VALUE "ProductName", "Keyman Developer\0" + VALUE "ProductVersion", KV_VERSION_STRING + VALUE "Comments", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1252 + END + END diff --git a/developer/src/kmcmplib/wasm.build.linux.in b/developer/src/kmcmplib/wasm.build.linux.in new file mode 100644 index 0000000000000000000000000000000000000000..c5e61a9bda94e66bcc6c147361c56c4488ef60c6 --- /dev/null +++ b/developer/src/kmcmplib/wasm.build.linux.in @@ -0,0 +1,4 @@ +[binaries] +c = ['$EMSCRIPTEN_BASE/emcc.py'] +cpp = ['$EMSCRIPTEN_BASE/em++.py'] +ar = ['$EMSCRIPTEN_BASE/emar.py'] diff --git a/developer/src/kmcmplib/wasm.build.mac.in b/developer/src/kmcmplib/wasm.build.mac.in new file mode 100644 index 0000000000000000000000000000000000000000..c5e61a9bda94e66bcc6c147361c56c4488ef60c6 --- /dev/null +++ b/developer/src/kmcmplib/wasm.build.mac.in @@ -0,0 +1,4 @@ +[binaries] +c = ['$EMSCRIPTEN_BASE/emcc.py'] +cpp = ['$EMSCRIPTEN_BASE/em++.py'] +ar = ['$EMSCRIPTEN_BASE/emar.py'] diff --git a/developer/src/kmcmplib/wasm.build.win.in b/developer/src/kmcmplib/wasm.build.win.in new file mode 100644 index 0000000000000000000000000000000000000000..68c21fb00f5f6ba0a3e0316aabbf894500bd0f3d --- /dev/null +++ b/developer/src/kmcmplib/wasm.build.win.in @@ -0,0 +1,4 @@ +[binaries] +c = ['python.exe', '$EMSCRIPTEN_BASE/emcc.py'] +cpp = ['python.exe', '$EMSCRIPTEN_BASE/em++.py'] +ar = ['python.exe', '$EMSCRIPTEN_BASE/emar.py'] diff --git a/developer/src/kmcmplib/wasm.defs.build b/developer/src/kmcmplib/wasm.defs.build new file mode 100644 index 0000000000000000000000000000000000000000..4a598a1e1c121d759e5637df28d17dabcb675734 --- /dev/null +++ b/developer/src/kmcmplib/wasm.defs.build @@ -0,0 +1,18 @@ +[binaries] +c = ['emcc.py'] +cpp = ['em++.py'] +ar = ['emar.py'] +exe_wrapper = 'node' + +[properties] +root = '$EMSCRIPTEN_BASE/system' +shared_lib_suffix = 'js' +static_lib_suffix = 'js' +shared_module_suffix = 'js' +exe_suffix = 'js' + +[host_machine] +system = 'emscripten' +cpu_family = 'x86' +cpu = 'i686' +endian = 'little' diff --git a/developer/src/kmcomp/kccompilekvk.pas b/developer/src/kmcomp/kccompilekvk.pas index 4e9e52c8376f743400d884501888d64ab65a5a14..facb82122aec4e7c2f30bae26f6f1951870fad45 100644 --- a/developer/src/kmcomp/kccompilekvk.pas +++ b/developer/src/kmcomp/kccompilekvk.pas @@ -20,7 +20,7 @@ uses VisualKeyboard; const - CWARN_KVKFileIsInSourceFormat = $000020A2; // from comperr.h + CWARN_KVKFileIsInSourceFormat = $000020A2; // from kmn_compiler_errors.h (** Compiles the visual keyboard from xml to binary, and/or copies to destination folder diff --git a/developer/src/kmcomp/main.pas b/developer/src/kmcomp/main.pas index 4aa29cf6c1ace54494285d0f9bd9c1f701d45040..5a29927a807bccd47dea671dbb6ce8d629a1f727 100644 --- a/developer/src/kmcomp/main.pas +++ b/developer/src/kmcomp/main.pas @@ -65,7 +65,7 @@ uses UKeymanTargets; function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; forward; // I4706 -function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; forward; +function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean; forward; //function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; forward; procedure FixupPathSlashes(var path: string); forward; @@ -90,6 +90,7 @@ var FParamDistribution: Boolean; FMergingValidateIds: Boolean; FShouldAddCompilerVersion: Boolean; + FUseKmcmpLib: Boolean; FJsonSchemaPath: string; FParamSourcePath: string; FParamHelpLink: string; @@ -116,6 +117,7 @@ begin FColorMode := cmDefault; FShouldAddCompilerVersion := True; + FUseKmcmpLib := True; FParamInfile := ''; FParamOutfile := ''; @@ -191,6 +193,8 @@ begin FColorMode := cmForceNoColor else if s = '-no-compiler-version' then FShouldAddCompilerVersion := False + else if s = '-use-legacy-compiler' then + FUseKmcmpLib := False else if (s = '-help') or (s = '-h') then begin // Force help @@ -219,6 +223,8 @@ begin writeln(SKeymanDeveloperName + ' Compiler (32-bit)'); {$ENDIF} writeln('Version ' + CKeymanVersionInfo.VersionWithTag + ', ' + GetVersionCopyright); + if not FUseKmcmpLib then + writeln('Note: using legacy compiler'); end; if FError or (FParamInfile = '') then @@ -228,7 +234,7 @@ begin writeln(''); writeln('Usage: '+cmd+' [-s[s]] [-nologo] [-c] [-d] [-w] [-cfc] [-v[s|d]] [-source-path path] [-schema-path path] '); writeln(' '+spc+' [-m] infile [-m infile] [-t target] [outfile.kmx|outfile.js [error.log]]'); // I4699 - writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version]'); + writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version] [-use-legacy-compiler]'); writeln(' '+spc+' [-extract-keyboard-info field[,field...]]'); writeln(' infile can be a .kmn file (Keyboard Source, .kps file (Package Source), or .kpj (project)'); // I4699 // I4825 writeln(' if -v specified, can also be a .keyboard_info file'); @@ -252,6 +258,7 @@ begin writeln(' uses console mode to determine whether color should be used.'); writeln; writeln(' -no-compiler-version Don''t embed the compiler version stores, useful for regression tests.'); + writeln(' -use-legacy-compiler Use legacy compiler (will be removed in 18.0)'); writeln; writeln(' JSON .keyboard_info compile targets:'); writeln(' -v[s] validate infile against source schema'); @@ -291,7 +298,7 @@ begin TProjectLogConsole.Create(FSilent, FFullySilent, hOutfile, FColorMode); - KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion); + KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion, FUseKmcmpLib); if FValidateRepoChanges then FError := not TValidateRepoChanges.Execute(FParamInfile, FParamOutfile) @@ -317,7 +324,7 @@ begin ExitCode := 1; end; -function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; +function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean; var opt: TCompilerOptions; begin @@ -325,6 +332,7 @@ begin opt.dwSize := sizeof(TCompilerOptions); opt.ShouldAddCompilerVersion := FShouldAddCompilerVersion; + opt.UseKmcmpLib := FUseKmcmpLib; Result := SetCompilerOptions(@opt, @CompilerMessageW); diff --git a/developer/src/test/auto/Makefile b/developer/src/test/auto/Makefile index 635d933bd104e408c2ffe9ab6ee2b786009cecc8..ebbdf290160a8222ffcdda4a7c821c28bacc7d4b 100644 --- a/developer/src/test/auto/Makefile +++ b/developer/src/test/auto/Makefile @@ -26,7 +26,7 @@ developer-tests: \ model-ts-parser \ package-info \ kmc \ - kmc-keyboard \ + kmc-ldml \ kmc-model \ kmc-model-info \ kmc-package @@ -87,8 +87,8 @@ kmc: .virtual cd $(DEVELOPER_ROOT)\src\kmc $(MAKE) $(TARGET) -kmc-keyboard: .virtual - cd $(DEVELOPER_ROOT)\src\kmc-keyboard +kmc-ldml: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-ldml $(MAKE) $(TARGET) kmc-model: .virtual diff --git a/developer/src/test/auto/kmcomp/test.bat b/developer/src/test/auto/kmcomp/test.bat index 0fbaefa7f26fd8827be74e1491069e8f8e3da745..808c2f9068b3260fa6f2fcd30386d6072c5fccfb 100644 --- a/developer/src/test/auto/kmcomp/test.bat +++ b/developer/src/test/auto/kmcomp/test.bat @@ -143,26 +143,38 @@ echo %BLUE%TEST: %1 %WHITE% if exist error.log del error.log "%compiler%" -no-color -nologo -ss -w tests.kpj -t "%2" > error.log if !ERRORLEVEL! GTR 0 ( + echo %RED%FAILED: expected %2 to be a valid keyboard.%WHITE% 1>&2 + echo --------------------------------------------------------------------------- + echo %BLUE%## error.log%WHITE% type error.log + echo --------------------------------------------------------------------------- del error.log - echo %RED%FAILED: expected %2 to be a valid keyboard.%WHITE% 1>&2 exit /b 1 ) if "!outfile!" == "" ( findstr /L /C:%3 error.log > nul if !ERRORLEVEL! GTR 0 ( + echo %RED%FAILED: expected compile results to contain message %3.%WHITE% 1>&2 + echo --------------------------------------------------------------------------- + echo %BLUE%## error.log%WHITE% type error.log + echo --------------------------------------------------------------------------- del error.log - echo %RED%FAILED: expected compile results to contain message %3.%WHITE% 1>&2 exit /b 1 ) ) else ( fc error.log !outfile! > nul if !ERRORLEVEL! GTR 0 ( + echo %RED%FAILED: expected output in error.log did not match !outfile!.%WHITE% 1>&2 + echo --------------------------------------------------------------------------- + echo %BLUE%## error.log%WHITE% type error.log + echo --------------------------------------------------------------------------- + echo %BLUE%## !outfile!%WHITE% + type !outfile! + echo --------------------------------------------------------------------------- del error.log - echo %RED%FAILED: expected output in error.log did not match !outfile!.%WHITE% 1>&2 exit /b 1 ) ) diff --git a/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk b/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk index 2d494cfc2e61ff74440731f29a77ea42c37b2d6d..a46a1fffae959d0c39646bdc37b3c9a105f5911e 100644 Binary files a/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk and b/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk differ diff --git a/developer/src/tike/Makefile b/developer/src/tike/Makefile index f9a4c61d367340c8682a108f239030f1937b836c..ce20a3673c6b4306646819111c14cd02b401f282 100644 --- a/developer/src/tike/Makefile +++ b/developer/src/tike/Makefile @@ -68,6 +68,10 @@ clean: def-clean signcode: $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\tike.exe $(SIGNCODE) /d "Keyman Core" $(DEVELOPER_PROGRAM)\kmnkbp0-0.dll +# Sign the Sentry executables and libraries here + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\sentry.dll + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\sentry.x64.dll + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\crashpad_handler.exe wrap-symbols: $(SYMSTORE) $(DEVELOPER_PROGRAM)\tike.exe /t keyman-developer diff --git a/developer/src/tike/compile/keyman32_int.pas b/developer/src/tike/compile/keyman32_int.pas deleted file mode 100644 index 37ddbe93475c8994379563db135fcb7371618ba7..0000000000000000000000000000000000000000 --- a/developer/src/tike/compile/keyman32_int.pas +++ /dev/null @@ -1,108 +0,0 @@ -unit keyman32_int; - -interface - -uses Windows, SysUtils, Forms; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -function Keyman_Exit: Boolean; -function Keyman_ForceKeyboard(const s: string): Boolean; -function Keyman_StopForcingKeyboard: Boolean; - -implementation - -uses TikeUtils; - -type TKeyman_ForceKeyboard = function (s: PChar): Boolean; stdcall; -type TKeyman_StopForcingKeyboard = function: Boolean; stdcall; -type TKeyman_Initialise = function(h: THandle; FSingleApp: LongBool): Boolean; stdcall; -type TKeyman_Exit = function: Boolean; stdcall; - -var - FInitKeyman: Boolean = False; - FKeyman32Path: string = ''; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -var - hkeyman: THandle; - FLoad: Boolean; - ki: TKeyman_Initialise; -begin - Result := False; - FLoad := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - hkeyman := LoadLibrary(PChar(GetKeymanInstallPath+'keyman32.dll')); - if hkeyman = 0 then Exit; - FLoad := True; - end; - ki := TKeyman_Initialise(GetProcAddress(hkeyman, 'Keyman_Initialise')); - if not Assigned(@ki) then Exit; - if not ki(Handle, FSingleApp) then - begin - if FLoad then FreeLibrary(hkeyman); - Exit; - end; - - FInitKeyman := True; - Result := True; -end; - -function Keyman_Exit: Boolean; -var - hkeyman: THandle; - ke: TKeyman_Exit; -begin - if not FInitKeyman then - begin - Result := True; - Exit; - end; - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - ke := TKeyman_Exit(GetProcAddress(hkeyman, 'Keyman_Exit')); - if not Assigned(@ke) then Exit; - if not ke then Exit; - if FInitKeyman then FreeLibrary(hkeyman); - FInitKeyman := False; - Result := True; -end; - -function Keyman_ForceKeyboard(const s: string): Boolean; -var - hkeyman: THandle; - fk: TKeyman_ForceKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - if not Keyman_Initialise(Application.MainForm.Handle, True) then Exit; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - end; - fk := TKeyman_ForceKeyboard(GetProcAddress(hkeyman, 'Keyman_ForceKeyboard')); - if(Assigned(@fk)) then - Result := fk(PChar(s)); -end; - -function Keyman_StopForcingKeyboard: Boolean; -var - hkeyman: THandle; - sfk: TKeyman_StopForcingKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - sfk := TKeyman_StopForcingKeyboard(GetProcAddress(hkeyman, 'Keyman_StopForcingKeyboard')); - if(Assigned(@sfk)) then - Result := sfk; - Keyman_Exit; -end; - -initialization -finalization - Keyman_Exit; -end. diff --git a/developer/src/tike/dialogs/UfrmOptions.dfm b/developer/src/tike/dialogs/UfrmOptions.dfm index 5968496f38e62dbc14b66ee29bca4aa751dadde7..d20db381fd77cb69b121c18ec52477107ede8a16 100644 --- a/developer/src/tike/dialogs/UfrmOptions.dfm +++ b/developer/src/tike/dialogs/UfrmOptions.dfm @@ -30,7 +30,7 @@ inherited frmOptions: TfrmOptions Width = 125 Height = 25 Caption = '&Proxy Settings...' - TabOrder = 4 + TabOrder = 5 OnClick = cmdProxySettingsClick end object gbExternalEditor: TGroupBox @@ -39,7 +39,7 @@ inherited frmOptions: TfrmOptions Width = 401 Height = 45 Caption = '&External Editor Path' - TabOrder = 1 + TabOrder = 2 object editExternalEditorPath: TEdit Left = 8 Top = 16 @@ -62,7 +62,7 @@ inherited frmOptions: TfrmOptions Width = 125 Height = 25 Caption = 'S&MTP Settings...' - TabOrder = 3 + TabOrder = 6 OnClick = cmdSMTPSettingsClick end object chkOpenKeyboardFilesInSourceView: TCheckBox @@ -71,7 +71,7 @@ inherited frmOptions: TfrmOptions Width = 221 Height = 17 Caption = 'Open &keyboard files in source view' - TabOrder = 0 + TabOrder = 1 end object cmdResetToolWindows: TButton Left = 8 @@ -79,7 +79,7 @@ inherited frmOptions: TfrmOptions Width = 125 Height = 25 Caption = '&Reset tool windows' - TabOrder = 2 + TabOrder = 4 OnClick = cmdResetToolWindowsClick end object chkAllowMultipleInstances: TCheckBox @@ -88,7 +88,7 @@ inherited frmOptions: TfrmOptions Width = 289 Height = 17 Caption = '&Allow multiple instances of Keyman Developer' - TabOrder = 5 + TabOrder = 0 end object gbDefaultProjectPath: TGroupBox Left = 8 @@ -96,7 +96,7 @@ inherited frmOptions: TfrmOptions Width = 401 Height = 45 Caption = '&Default Project Folder' - TabOrder = 6 + TabOrder = 3 object editDefaultProjectPath: TEdit Left = 8 Top = 16 @@ -136,14 +136,14 @@ inherited frmOptions: TfrmOptions Width = 401 Height = 73 Caption = 'Privacy' - TabOrder = 9 + TabOrder = 10 object chkReportUsage: TCheckBox Left = 8 Top = 44 Width = 311 Height = 17 Caption = '&Share anonymous usage statistics with keyman.com' - TabOrder = 0 + TabOrder = 1 end object chkReportErrors: TCheckBox Left = 8 @@ -151,9 +151,17 @@ inherited frmOptions: TfrmOptions Width = 311 Height = 17 Caption = 'Automatically report &errors to keyman.com' - TabOrder = 1 + TabOrder = 0 end end + object chkUseLegacyCompiler: TCheckBox + Left = 152 + Top = 244 + Width = 250 + Height = 17 + Caption = 'Use legacy .kmn compiler (removing in 18.0)' + TabOrder = 9 + end end object tabEditor: TTabSheet Caption = 'Editor' diff --git a/developer/src/tike/dialogs/UfrmOptions.pas b/developer/src/tike/dialogs/UfrmOptions.pas index 77aa090ea2f6f27e18cea8bd201322965132158d..3976e6ceb572971856d5b400c09fcc648ac73226 100644 --- a/developer/src/tike/dialogs/UfrmOptions.pas +++ b/developer/src/tike/dialogs/UfrmOptions.pas @@ -105,6 +105,7 @@ type gbServer: TGroupBox; chkListLocalURLs: TCheckBox; cmdConfigureServer: TButton; + chkUseLegacyCompiler: TCheckBox; procedure FormCreate(Sender: TObject); procedure cmdOKClick(Sender: TObject); procedure cmdDefaultFontClick(Sender: TObject); @@ -194,6 +195,8 @@ begin chkAutoSaveBeforeCompiling.Checked := AutoSaveBeforeCompiling; chkOSKAutoSaveBeforeImporting.Checked := OSKAutoSaveBeforeImporting; + chkUseLegacyCompiler.Checked := UseLegacyCompiler; + chkCharMapAutoLookup.Checked := CharMapAutoLookup; chkCharMapDisableDatabaseLookups.Checked := CharMapDisableDatabaseLookups; @@ -295,6 +298,8 @@ begin AutoSaveBeforeCompiling := chkAutoSaveBeforeCompiling.Checked; OSKAutoSaveBeforeImporting := chkOSKAutoSaveBeforeImporting.Checked; + UseLegacyCompiler := chkUseLegacyCompiler.Checked; + ServerUseLocalAddresses := chkListLocalURLs.Checked; CharMapAutoLookup := chkCharMapAutoLookup.Checked; diff --git a/developer/src/tike/main/KeymanDeveloperOptions.pas b/developer/src/tike/main/KeymanDeveloperOptions.pas index 677f55d5909c800a59b1c7db06b16c3e203fb6ca..d7068788cd9611fbe475718ba6d52c846a0d58f4 100644 --- a/developer/src/tike/main/KeymanDeveloperOptions.pas +++ b/developer/src/tike/main/KeymanDeveloperOptions.pas @@ -70,6 +70,7 @@ type FServerNgrokToken: string; FServerNgrokRegion: string; FServerKeepAlive: Boolean; + FUseLegacyCompiler: Boolean; procedure CloseRegistry; procedure OpenRegistry; function regReadString(const nm, def: string): string; @@ -104,6 +105,8 @@ type property AutoSaveBeforeCompiling: Boolean read FAutoSaveBeforeCompiling write FAutoSaveBeforeCompiling; property OSKAutoSaveBeforeImporting: Boolean read FOSKAutoSaveBeforeImporting write FOSKAutoSaveBeforeImporting; + property UseLegacyCompiler: Boolean read FUseLegacyCompiler write FUseLegacyCompiler; + property ReportErrors: Boolean read FReportErrors write FReportErrors; property ReportUsage: Boolean read FReportUsage write FReportUsage; @@ -215,6 +218,8 @@ begin FAutoSaveBeforeCompiling := regReadBool(SRegValue_IDEOptAutoSaveBeforeCompiling, False); FOSKAutoSaveBeforeImporting := regReadBool(SRegValue_IDEOptOSKAutoSaveBeforeImporting, False); + FUseLegacyCompiler := regReadBool(SRegValue_IDEOptUseLegacyCompiler, False); + FServerDefaultPort := regReadInt(SRegValue_IDEOptServerPort, 8008); FServerKeepAlive := regReadBool(SRegValue_IDEOptServerKeepAlive, False); FServerUseLocalAddresses := regReadBool(SRegValue_IDEOptServerUseLocalAddresses, True); @@ -276,6 +281,7 @@ begin regWriteBool(SRegValue_IDEOptAutoSaveBeforeCompiling, FAutoSaveBeforeCompiling); regWriteBool(SRegValue_IDEOptOSKAutoSaveBeforeImporting, FOSKAutoSaveBeforeImporting); + regWriteBool(SRegValue_IDEOptUseLegacyCompiler, FUseLegacyCompiler); regWriteInt(SRegValue_IDEOptServerPort, FServerDefaultPort); regWriteBool(SRegValue_IDEOptServerKeepAlive, FServerKeepAlive); diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas index 4f0503509c0c83fbf0df1a56495288a003a2fc14..703a1e4a0b6edd891950b647e5240c2e1250f02b 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas @@ -19,6 +19,8 @@ type public function CompileKeyboard: Boolean; function Clean: Boolean; + function DoSetCompilerOptions(addVersion, + useLegacyCompiler: Boolean): Boolean; end; implementation @@ -106,6 +108,22 @@ begin end; end; +function TkmnProjectFileAction.DoSetCompilerOptions(addVersion: Boolean; useLegacyCompiler: Boolean): Boolean; +var + options: COMPILER_OPTIONS; +begin + TProject.CompilerMessageFile := Self; + options.dwSize := sizeof(COMPILER_OPTIONS); + options.ShouldAddCompilerVersion := addVersion; + options.UseKmcmpLib := not useLegacyCompiler; + if not SetCompilerOptions(@options, ProjectCompilerMessage) then + begin + Log(plsFatal, 'Unable to set compiler options', CERR_FATAL, 0); + Exit(False); + end; + Result := True; +end; + function TkmnProjectFileAction.CompileKeyboard: Boolean; var KMXFileName: String; diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas index 9b2dd467c5be2011eaa205c91612b12c9fb9ec99..72c9b43fd47f817361314b093eaef8dbee180750 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas @@ -130,7 +130,11 @@ begin if not FSilent then frmMessages.DoShowForm; - Result := ProjectFile.CompileKeyboard; + Result := + // Note: we do not surface the ability to exclude version information from + // the TIKE compiler; this must be done from kmcomp.exe. + ProjectFile.DoSetCompilerOptions(True, FKeymanDeveloperOptions.UseLegacyCompiler) and + ProjectFile.CompileKeyboard; if Result and TServerDebugAPI.Running and diff --git a/docs/build/linux-ubuntu.md b/docs/build/linux-ubuntu.md index d5cb971f8c50db032cc138c266d66d684574f0d4..5b576a6ce89f397ca342dda79e58a42ddf81cbea 100644 --- a/docs/build/linux-ubuntu.md +++ b/docs/build/linux-ubuntu.md @@ -6,8 +6,8 @@ On Linux, you can build the following projects: * [Keyman for Linux](#keyman-for-linux) * [Keyman Core](#keyman-core) (Linux only) (aka core) - -* Keyman for Android +* [Keyman for Android](#keyman-for-android) + * Keyman Core (wasm targets) * Common/Web * KeymanWeb @@ -21,7 +21,7 @@ The following projects **cannot** be built on Linux: ## System Requirements -* Minimum Ubuntu version: Ubuntu 18.04 +* Minimum Ubuntu version: Ubuntu 20.04 Other Linux distributions will also work if appropriate dependencies are installed. @@ -98,4 +98,69 @@ cd keymanapp/keyman docker run -it --rm -v $(pwd):/home/build/src/keyman -u root -w /home/build/src/keyman keymanapp/keyman-linux-builder:latest bash -c "cd linux && make reconf && make fullbuild && make install" ``` +## Keyman for Android +**Dependencies:** + +* [Base](#base-dependencies) +* [Web](./windows#web-dependencies) + +**Additional requirements:** + +* Android SDK +* [Android Studio](https://developer.android.com/studio/install#linux) +* Gradle +* Maven +* OpenJDK 11 (for Keyman 17.0+) +* pandoc + +Run Android Studio once after installation to install additional components +such as emulator images and SDK updates. + +**Required environment variable:** + +* `ANDROID_HOME` pointing to Android SDK (`$HOME/Android/Sdk`) + +**Recommended environment variable:** + +* [`JAVA_HOME`](#java_home) + +Building: + +* [Building Keyman for Android](../../android/README.md) + +## Prerequisites + +Many dependencies are only required for specific projects. + +### Base Dependencies + +**Environment variables:** + +* -- + +## Notes on Environment Variables + +### JAVA_HOME + +This environment variable tells Gradle what version of Java to use for building +Keyman for Android. OpenJDK 11 is used for master. + +It's recommended to set the environment variables to: + +```bash +export JAVA_HOME="[path to OpenJDK 11]" +``` + +Also edit `/etc/profile.d/jvm.sh` as sudo: + +```bash +export JAVA_HOME="[path to OpenJDK 11]" +``` + +**Multiple versions of Java:** If you need to build Keyman for Android 16.0 or +older versions, you can set `JAVA_HOME_11` to the OpenJDK 11 path and +`JAVA_HOME` to the OpenJDK 8 path. This will build both versions correctly +from command line. But note that you do need to update your `JAVA_HOME` env +var to the associated version before opening Android Studio and loading any +Android projects. `JAVA_HOME_11` is mostly used by CI. diff --git a/docs/build/windows.md b/docs/build/windows.md index 25889345cf3503be633028d92d24e4d66efc9d02..ef4a77e18fc890a80417f968f2d5cfc6211ed68c 100644 --- a/docs/build/windows.md +++ b/docs/build/windows.md @@ -101,12 +101,13 @@ Building: * Ant * Gradle * Maven +* OpenJDK 11 ```ps1 # Elevated PowerShell choco install android-sdk android-studio ant gradle maven # optionally install sdk images -sdkmanager "system-images;android-30;google_apis;armeabi-v7a" +sdkmanager "system-images;android-33;google_apis;armeabi-v7a" sdkmanager --update sdkmanager --licenses ``` @@ -114,6 +115,12 @@ sdkmanager --licenses * Run Android Studio once after installation to install additional components such as emulator images and SDK updates. +**Required environment variables**: +* [`JAVA_HOME`](#java_home) + +**Optional environment variables**: +* [`JAVA_HOME_11`](#java_home) + Building: * [Building Keyman for Android](../../android/README.md) @@ -175,9 +182,9 @@ You can use Windows Settings to add these environment variables permanently: * KeymanWeb **Requirements**: -* emscripten 2.0.23+ -* node.js 14+ -* openjdk 8+ +* emscripten 3.1.32 +* node.js 18 +* [openjdk 11](https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-11)+ ```ps1 # Elevated PowerShell @@ -245,6 +252,10 @@ choco install openjdk ```ps1 choco install visualstudio2019community visualstudio2019-workload-nativedesktop visualstudio2019buildtools ``` + * Verify required build tools are installed + * Run `Visual Studio Installer` + * Check the `Individual components` tab + * Verify `MSVC v142 - VS 2019 c++ x64/x86 build tools (Latest)` is installed. If not, install it. Recommended: configure Visual Studio to use two-space tab stops: 1. Open the options dialog: Tools > Options. @@ -341,6 +352,17 @@ To include UserDefines.mak in the build, use the command line parameter `-DUSERDEFINES`. You can also set an environment variable `USERDEFINES=1` to get the same result. +### JAVA_HOME + +This environment variable tells Gradle what version of Java to use for building Keyman for Android. + +**Multiple versions of Java:** If you need to build Keyman for Android 16.0 or older versions, you can set `JAVA_HOME_11` to the OpenJDK 11 path and `JAVA_HOME` to the OpenJDK 8 path. This will build both versions correctly from command line. But note that you do need to update your `JAVA_HOME` env var to the associated version before opening Android Studio and loading any Android projects. `JAVA_HOME_11` is mostly used by CI. + +```bat +SET JAVA_HOME="path to OpenJDK 8" +SET JAVA_HOME_11="path to OpenJDK 11" +``` + ## Optional Tools * sentry-cli (optional) diff --git a/docs/linux/README.md b/docs/linux/README.md index 3aa15291e32ef25472905f326dd775a1572af196..8690b46a55e5db337187b12fd0de336e1f7f5c7e 100644 --- a/docs/linux/README.md +++ b/docs/linux/README.md @@ -22,7 +22,8 @@ See [license information](../../linux/LICENSE.md) about licensing. python3-magic python3-numpy python3-pil python3-pip python3-qrcode \ python3-requests python3-requests-cache python3 python3-gi dconf-cli \ dconf-editor cargo python3-dbus gir1.2-webkit2-4.0 ibus libglib2.0-bin \ - liblocale-gettext-perl xvfb xserver-xephyr metacity mutter + liblocale-gettext-perl xvfb xserver-xephyr metacity mutter \ + libicu-dev pkg-config ``` ## Compiling from Command Line diff --git a/docs/linux/packaging.md b/docs/linux/packaging.md index 91154934b44be5059f04bd4dca47717ea832a7bb..59f16880eb2ef028efe0620a71b1d47c5bd9a51d 100644 --- a/docs/linux/packaging.md +++ b/docs/linux/packaging.md @@ -239,7 +239,7 @@ You'll have to create the docker image. ```bash cd $KEYMAN_ROOT docker run -v $(pwd):/source -i -t -w /source --platform=linux/amd64 \ - sillsdev/jammy keyman_*.dsc /source + sillsdev/jammy jammy keyman_*.dsc ``` This will create the binary packages in `$KEYMAN_ROOT/artifacts`. diff --git a/docs/npm-packages.md b/docs/npm-packages.md index 221180cfe89b73d365879e69b3a5a4242e21d6d2..040a6e11ccf48b052ec0cd51b925666664b53de8 100644 --- a/docs/npm-packages.md +++ b/docs/npm-packages.md @@ -10,16 +10,23 @@ Packages are **never** published from a developer's machine. ## List of current published npm packages -* `@keymanapp/models-types` -- located at `common/models/types` +* `@keymanapp/common-types` -- located at `common/web/types` +* `@keymanapp/keyman-version` -- located at `common/web/keyman-version` * `@keymanapp/kmc` -- located at `developer/src/kmc` -* `@keymanapp/kmc-keyboard` -- located at `developer/src/kmc-keyboard` +* `@keymanapp/kmc-kmn` -- located at `developer/src/kmc-kmn` +* `@keymanapp/kmc-ldml` -- located at `developer/src/kmc-ldml` * `@keymanapp/kmc-model` -- located at `developer/src/kmc-model` * `@keymanapp/kmc-model-info` -- located at `developer/src/kmc-model-info` * `@keymanapp/kmc-package` -- located at `developer/src/kmc-package` +* `@keymanapp/models-types` -- located at `common/models/types` ### Deprecated npm packages +* `@keymanapp/developer-lexical-model-compiler` -- replaced by `@keymanapp/kmc` * `@keymanapp/lexical-model-compiler` -- replaced by `@keymanapp/kmc` +* `@keymanapp/lexical-model-types` -- replaced by `@keymanapp/models-types` +* `@keymanapp/lmlayer-cli` -- replaced by `@keymanapp/kmc` +* `@keymanapp/models-wordbreakers` -- published accidentally ## For CI developers @@ -89,13 +96,12 @@ For every release of **Keyman Developer**, the CI publishes a release of `@keymanapp/kmc`. The version number is locked with the particular version of Keyman Developer. -### `set_npm_version` +### `builder_publish_to_npm` -This helper function sets the version in the `package.json` for the given -package, according to the VERSION_WITH_TAG variable. +This helper function updates version numbers and does the publish step for the +package.json in the working directory, using the VERSION_WITH_TAG variable. ```bash -. $KEYMAN_ROOT/resources/shellHelperFunctions.sh - -set_npm_version + . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + builder_publish_to_npm ``` diff --git a/docs/settings/linux/launch.json b/docs/settings/linux/launch.json index a66c76f0a9b749dff3b6c97f1d32b8071f176b65..bfae335300d1980249d74352ff9ce193ede01ca9 100644 --- a/docs/settings/linux/launch.json +++ b/docs/settings/linux/launch.json @@ -20,7 +20,7 @@ "name": "Launch kmx unit tests", "target": "build/arch/debug/tests/unit/kmx/kmx", "cwd": "${workspaceRoot}/core/", - "arguments": "tests/unit/kmx/033\\ -\\ caps\\ always\\ off.km{n,x}", + "arguments": "tests/unit/kmx/k_033___caps_always_off.km{n,x}", "valuesFormatting": "parseText" }, { diff --git a/docs/settings/linux/settings.json b/docs/settings/linux/settings.json index be15b13027d8b8a133b7c310d4ab9ad1160cf6d6..891507dd2ecfc3b11a2ad1eae765934819ac0506 100644 --- a/docs/settings/linux/settings.json +++ b/docs/settings/linux/settings.json @@ -107,4 +107,5 @@ "--external-sources --source-path=resources/build/" ], "C_Cpp.errorSquiggles": "EnabledIfIncludesResolve", + "shellcheck.useWorkspaceRootAsCwd": false } diff --git a/docs/settings/linux/tasks.json b/docs/settings/linux/tasks.json index 77df789fe318c41f7099a86255d6476ce05c3658..9728cd709e4f582510b38aca95e5b78a299d9ac9 100644 --- a/docs/settings/linux/tasks.json +++ b/docs/settings/linux/tasks.json @@ -158,5 +158,100 @@ "group": "test", "detail": "unit test keyman-config" }, + { + "type": "shell", + "label": "keyman-system-service: clean", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "clean" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service" + }, + "group": "build", + "detail": "clean keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: build", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "build" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service" + }, + "problemMatcher": [ + { + "owner": "cpp", + "source": "compiler", + "fileLocation": [ + "relative", + "${workspaceFolder}/linux/keyman-system-service/" + ], + "pattern": [ + { + "regexp": "^../../../(.*):(\\d+):(\\d+):\\s+(warning|error|note):\\s+(.*)(\\s+\\[.*\\])?$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + }, + { + "owner": "cpp", + "source": "linker", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(/usr/bin/ld:\\s+)?(.*):(\\d+):\\s+(.*)$", + "file": 2, + "location": 3, + "message": 4 + } + ] + } + ], + "presentation": { + "clear": true + }, + "group": "build", + "detail": "build keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: configure", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "configure" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service", + }, + "presentation": { + "clear": true + }, + "group": "build", + "detail": "configure keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: test", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "test:arch" + ], + "problemMatcher": [ + "$gcc" + ], + "group": "test", + "detail": "run tests for keyman-system-service" + }, ] } diff --git a/ios/Cartfile b/ios/Cartfile index 5ad8178ed48a4e7ad28589d41fe9ed382617a5c9..b265ef77af28f9f7d45032ba8f753477057e456f 100644 --- a/ios/Cartfile +++ b/ios/Cartfile @@ -1,5 +1,5 @@ github "marmelroy/Zip" -github "DaveWoodCom/XCGLogger" ~> 6.1.0 +github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" -github "getsentry/sentry-cocoa" ~> 6.2.1 +github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/ios/Cartfile.resolved b/ios/Cartfile.resolved index fe74a57322bc07d42adfbf6539596e6ad6035902..ff30a8871ddc681497e00c68287eb34808d94541 100644 --- a/ios/Cartfile.resolved +++ b/ios/Cartfile.resolved @@ -1,5 +1,5 @@ -github "DaveWoodCom/XCGLogger" "6.1.0" github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.0.0" -github "getsentry/sentry-cocoa" "6.2.1" +github "getsentry/sentry-cocoa" "8.7.0" +github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" github "marmelroy/Zip" "2.1.2" diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index 127fcd12b5bd2b4fb8d900586073e0ab94971bcc..94f988507606db6fe75025f89cc91045ecc8ffd0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -167,7 +167,7 @@ public class SentryManager { public static func breadcrumbAndLog(crumb: Sentry.Breadcrumb, logLevel: XCGLogger.Level? = nil) { // Guarded in case a library consumer decides against initializing Sentry. if _started { - SentrySDK.addBreadcrumb(crumb: crumb) + SentrySDK.addBreadcrumb(crumb) } let level = logLevel ?? mapLoggingLevel(crumb.level) @@ -193,7 +193,7 @@ public class SentryManager { } public static func forceError() { - SentrySDK.addBreadcrumb(crumb: Sentry.Breadcrumb(level: .info, category: "Deliberate testing error")) + SentrySDK.addBreadcrumb(Sentry.Breadcrumb(level: .info, category: "Deliberate testing error")) SentrySDK.crash() } } diff --git a/ios/help/basic/config/index.md b/ios/help/basic/config/index.md new file mode 100644 index 0000000000000000000000000000000000000000..22468a4021c9b39a7f6ca23021f88d150914be18 --- /dev/null +++ b/ios/help/basic/config/index.md @@ -0,0 +1,60 @@ +--- +title: Using the Settings Screen - Keyman for iPhone and iPad Help +--- + +## Using the Settings Screen + +This Settings screen is used to configure keyboards and dictionaries. +It also has many of the options that are available on the 'Get Started' menu. To access the Settings screen: + +Step 1) +Open the Keyman app. + +Step 2) +Click the ellipsis (...) for additional options. Select 'Settings' + +![](../../ios_images/settings-screen.png) + +Each selection on the Keyman Settings screen is described in further detail below: + + +## Installed Languages +This displays the number of installed languages. Click this line for the 'Installed Languages' +screen where you may view the languages you have installed and, by clicking any one language, find its keyboards and dictionaries along with [more settings](../language-settings). + +![](../../ios_images/find-new.png) + +Click on this to [search for a keyboard or language](../../start/searching-for-keyboards). + +## Show Banner +This adds a banner to the top of the keyboard to allow space for pop-up keys to render above the top row of keys. + +![](../../ios_images/banner.png) + +When this is off, + +![](../../ios_images/no-banner.png) + +pop-up keys in the top row render horizontally. + +## Show "Get Started" on startup +When enabled, the Keyman app will display the 'Get Started' screen on app startup. + +## Allow error reporting +When enabled, the Keyman app will send crash reporting information to https://sentry.keyman.com. No personally identifiable information or keyboard strokes are recorded. + +## Spacebar Caption +Click on this to [change the displayed keyboard name](spacebar-caption) on the spacebar. + +## System Keyboard Settings +By default, installed Keyman keyboards are only available to use within the Keyman app. Click on this setting to +bring up the iOS system configuration so Keyman can be added as a system-wide keyboard. + +## Set Keyman as default keyboard +Select 'Keyboards' to bring up the iOS system configuration for setting Keyman as the default system-wide keyboard. Toggle both 'Keyman' and 'Allow Full Access'. + +![](../../ios_images/system-wide-setting.png) + +## Install From File +Select this to browse the files on your device and install a keyboard from a .kmp file. + diff --git a/ios/help/basic/config/spacebar-caption.md b/ios/help/basic/config/spacebar-caption.md new file mode 100644 index 0000000000000000000000000000000000000000..4344c83063f676c1090a201bd567c0373f763ee1 --- /dev/null +++ b/ios/help/basic/config/spacebar-caption.md @@ -0,0 +1,13 @@ +--- +title: Spacebar Caption - Keyman for iPhone and iPad Help +--- + +## Spacebar Caption + +This menu can be used to change the displayed keyboard name on the spacebar. The options are: +* Language name +* Keyboard name +* Language name + Keyboard name +* Blank + +![](../../ios_images/spacebar-caption.png) diff --git a/ios/help/basic/index.md b/ios/help/basic/index.md index 10df8f8300ccbd7807ee1527fbb352f80565a89e..e2c9e82d6d272aea9a058d8e80e54c5a7ee5ecd3 100644 --- a/ios/help/basic/index.md +++ b/ios/help/basic/index.md @@ -4,6 +4,9 @@ title: Basic Help * [Switching Between Keyboards](switching-between-keyboards) +* [Using the Settings Screen](config/) + +* [Language Settings](language-settings) * [Installing Custom Keyboards/Dictionaries](installing-custom-keyboards-dictionaries) * [Sharing Keyboards and Dictionaries](sharing-keyboards) diff --git a/ios/help/basic/language-settings.md b/ios/help/basic/language-settings.md new file mode 100644 index 0000000000000000000000000000000000000000..af39c37f36b9b238b15541ae511a732568520b64 --- /dev/null +++ b/ios/help/basic/language-settings.md @@ -0,0 +1,58 @@ +--- +title: Language Settings - Keyman for iPhone and iPad Help +--- + +### Access "Installed Languages" +First, open the "Settings" menu. + +![](../ios_images/add-keyboard-i.png) + +The "Installed Languages" menu found here manages your installed keyboards and dictionaries. + +![](../ios_images/add-keyboard-i2.png) + +You should then see the following screen: + +![](../ios_images/add-keyboard-i3.png) + + +### Select a Language to View its Settings +After selecting a language, you will find settings in these categories: Keyboards, Language Settings, and Dictionaries. + +![](../ios_images/language-settings.png) + +### Keyboard Settings +In Keyboard settings, you will find a list of keyboards installed in the language. After clicking a keyboard, you will see + +* the version of the installed keyboard +* the link to the 'Keyboard help' screen with information about its symbols and fonts +* the option to Uninstall the keyboard +* the QR code for loading this keyboard on another device + +### Language Settings +In Language settings, you may see + +* the option to enable predictions +* if predictions are enabled, the option to enable corrections + +These predictions and corrections are given while a user types, based upon the dictionary selected under 'Dictionaries'. + +### Dictionaries +In Dictionaries, you will see a list of dictionaries installed for the language. + +![](../ios_images/find-new.png) + +Click this to select a new dictionary from a list of available dictionaries in the language. + +![](../ios_images/dictionary-select.png) + +Back in the dictionaries list, after clicking a dictionary, you will see its name again, along with a blue information icon: + +![](../ios_images/dictionary-list.png) + +After clicking the 'i' icon, you will see + +* the version of the installed dictionary +* the option to Uninstall the dictionary + + diff --git a/ios/help/index.md b/ios/help/index.md index 883d4e5112ed659b94e6d94983bf305424470eb0..333dafb9f2d104948a2413a94f32590e31f25956 100644 --- a/ios/help/index.md +++ b/ios/help/index.md @@ -25,6 +25,8 @@ title: Keyman for iPhone and iPad 17.0 Help ### [Using Keyman for iPhone and iPad](basic/) * [Switching Between Keyboards](basic/switching-between-keyboards) +* [Using the Settings Screen](basic/config/) +* [Language Settings](basic/language-settings) * [Installing Custom Keyboards/Dictionaries](basic/installing-custom-keyboards-dictionaries) * [Sharing Keyboards and Dictionaries](basic/sharing-keyboards) * [Using the Keyman Browser](basic/using-keyman-browser) diff --git a/ios/help/ios_images/banner.png b/ios/help/ios_images/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..e7d38e8a2ee9de8ce55335e35e982fd05a3faffb Binary files /dev/null and b/ios/help/ios_images/banner.png differ diff --git a/ios/help/ios_images/dictionary-list.png b/ios/help/ios_images/dictionary-list.png new file mode 100644 index 0000000000000000000000000000000000000000..b2af2f53f5ac0f2325dab8a05c7935863c2a8193 Binary files /dev/null and b/ios/help/ios_images/dictionary-list.png differ diff --git a/ios/help/ios_images/dictionary-select.png b/ios/help/ios_images/dictionary-select.png new file mode 100644 index 0000000000000000000000000000000000000000..ae8300530af8579072b8ef5c0bf687ee897b4d2b Binary files /dev/null and b/ios/help/ios_images/dictionary-select.png differ diff --git a/ios/help/ios_images/find-new.png b/ios/help/ios_images/find-new.png new file mode 100644 index 0000000000000000000000000000000000000000..b76df881b690c6542de9dfcf755c9f7f1bda54f9 Binary files /dev/null and b/ios/help/ios_images/find-new.png differ diff --git a/ios/help/ios_images/language-settings.png b/ios/help/ios_images/language-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..1cea1ffcc1174573acb59dd5d09ad7d244d76075 Binary files /dev/null and b/ios/help/ios_images/language-settings.png differ diff --git a/ios/help/ios_images/no-banner.png b/ios/help/ios_images/no-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..68a19f67f6c953452722a515e9ce7e631d4286d7 Binary files /dev/null and b/ios/help/ios_images/no-banner.png differ diff --git a/ios/help/ios_images/settings-screen.png b/ios/help/ios_images/settings-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5c72b7f853965362b61a2636e4ab30ffcc193a Binary files /dev/null and b/ios/help/ios_images/settings-screen.png differ diff --git a/ios/help/ios_images/spacebar-caption.png b/ios/help/ios_images/spacebar-caption.png new file mode 100644 index 0000000000000000000000000000000000000000..2577c59e81d90846a8eceb181e2205665ced0a1c Binary files /dev/null and b/ios/help/ios_images/spacebar-caption.png differ diff --git a/ios/help/ios_images/system-wide-setting.png b/ios/help/ios_images/system-wide-setting.png new file mode 100644 index 0000000000000000000000000000000000000000..126fe13ec12cc05450ac5dba43a60b5aa1d6d691 Binary files /dev/null and b/ios/help/ios_images/system-wide-setting.png differ diff --git a/ios/samples/build_common.sh b/ios/samples/build_common.sh index c3397e29b4006efea8280bdecd500962c8df1176..e1ed7ee95fb07307caf721d4c718084df9e333bf 100755 --- a/ios/samples/build_common.sh +++ b/ios/samples/build_common.sh @@ -129,7 +129,7 @@ if [ $DO_UPDATE = true ]; then fi # Copy resources. - cp -Rf "$KEYMAN_ENGINE_FRAMEWORK_SRC" "$KEYMAN_ENGINE_FRAMEWORK_DST" + /bin/cp -Rf "$KEYMAN_ENGINE_FRAMEWORK_SRC" "$KEYMAN_ENGINE_FRAMEWORK_DST" fi if [ $CODE_SIGN = true ]; then diff --git a/ios/tools/prepRelease.sh b/ios/tools/prepRelease.sh index 1f22de45f096ec4a11cd41dc7fa14de36b423f7d..39b46722f79c973bee51d128192bfac1cc647c6f 100755 --- a/ios/tools/prepRelease.sh +++ b/ios/tools/prepRelease.sh @@ -70,17 +70,17 @@ KEYMAN_SAMPLES="samples" echo "engine dest: $KMEI_DST" -echo "Zipping ${UNI_FRAMEWORK} => ${UPLOAD_DIR}/${KMEI_DST}..." +echo "Zipping ${FRAMEWORK} => ${UPLOAD_DIR}/${KMEI_DST}..." cd "${KMEI_FRAMEWORK_BASE}" -zip -qrX ${KMEI_DST} ${FRAMEWORK} -cd $WORK_DIR +zip -qrX "${KMEI_DST}" ${FRAMEWORK} +cd "$WORK_DIR" echo "Copying Keyman Engine samples into ${UPLOAD_DIR}/${KMEI_DST_NAME}..." cp -rf "${KEYMAN_SAMPLES}" "${UPLOAD_DIR}/samples" cd "${UPLOAD_DIR}" zip -qr "${KMEI_DST_NAME}" "samples" rm -rf "samples" -cd $WORK_DIR +cd "$WORK_DIR" # # Keyman app diff --git a/linux/.gitignore b/linux/.gitignore index 478f418b608e52d3898a613528475097a4d10e4d..0abbd99c585433153b15c9eca0fd5e604ebb33d2 100644 --- a/linux/.gitignore +++ b/linux/.gitignore @@ -17,7 +17,7 @@ dist/ upload/ launchpad launchpad-* -keyman-config/build +build/ keyman-config/debian/man keyman-config/experiments/*.txt keyman-config/keyman_config/version.py diff --git a/linux/.pbuilderrc b/linux/.pbuilderrc index 21b738f1d9981cd2139090d805598e485c4dc79e..1384eec453548017b10ee36eebab6d17b842d352 100644 --- a/linux/.pbuilderrc +++ b/linux/.pbuilderrc @@ -14,7 +14,7 @@ DEBIAN_SUITES=($UNSTABLE_CODENAME $TESTING_CODENAME $STABLE_CODENAME $STABLE_BAC "experimental" "unstable" "testing" "stable") # List of Ubuntu suites. Update these when needed. -UBUNTU_SUITES=("kinetic" "jammy" "focal" "bionic") +UBUNTU_SUITES=("lunar" "kinetic" "jammy" "focal") # Mirrors to use. Update these to your preferred mirror. DEBIAN_MIRROR="deb.debian.org" diff --git a/linux/Jenkinsfile b/linux/Jenkinsfile index 15f5b27777d79d4442b63efac7d1d1650aa606f4..b70cb95d706cfc3c24ccf6e90a242f407d74ccae 100644 --- a/linux/Jenkinsfile +++ b/linux/Jenkinsfile @@ -5,7 +5,7 @@ @Library('lsdev-pipeline-library') _ keymanPackaging { - distributionsToPackage = 'focal jammy kinetic' + distributionsToPackage = 'focal jammy kinetic lunar' arches = 'amd64 i386' packagesToBuild = ['keyman'] } diff --git a/linux/build.sh b/linux/build.sh index f0aaff70d57fd0652c942c611a864975d1f87662..6c7a92069b3b11bd1f1032e88e6db28fd03b5317 100755 --- a/linux/build.sh +++ b/linux/build.sh @@ -14,7 +14,9 @@ cd "$THIS_SCRIPT_PATH" builder_describe \ "Build Keyman for Linux." \ - ":engine=ibus-keyman ibus-keyman" \ + ":config=keyman-config keyman-config" \ + ":engine=ibus-keyman ibus-keyman" \ + ":service=keyman-system-service keyman-system-service" \ "clean" \ "configure" \ "build" \ diff --git a/linux/debian/changelog b/linux/debian/changelog index 167b2452c658aa80ab2c27df3ddc1f831475265b..9f69cc9a12fb8cff409c44c39751958f1cf4d7c9 100644 --- a/linux/debian/changelog +++ b/linux/debian/changelog @@ -1,3 +1,22 @@ +keyman (16.0.139-4) unstable; urgency=medium + + * debian/tests: Revert previous change and ignore s390x from autopkgtests + + -- Eberhard Beilharz Fri, 24 Mar 2023 16:05:07 +0100 + +keyman (16.0.139-3) unstable; urgency=medium + + * debian/tests: Run autopkgtests on s390x but immediately return + + -- Eberhard Beilharz Wed, 22 Mar 2023 19:25:02 +0100 + +keyman (16.0.139-2) unstable; urgency=medium + + * Don't build on s390x because Keyman doesn't work on big-endian architectures + (upstream bug https://github.com/keymanapp/keyman/issues/5111) + + -- Eberhard Beilharz Mon, 20 Mar 2023 19:54:44 +0100 + keyman (16.0.139-1) unstable; urgency=medium * New upstream release. diff --git a/linux/debian/control b/linux/debian/control index 33f50a90dcd7f7f45dab0a1978f74ed907fb4378..02dd064749398f358939404520cdc4746a606261 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -22,15 +22,18 @@ Build-Depends: python3-pil, python3-pip, python3-qrcode, - python3-sentry-sdk (>= 1.1) | python3-raven, + python3-sentry-sdk (>= 1.1), python3-requests, python3-requests-cache, python3-setuptools, meson (>= 0.53), ninja-build, + libevdev-dev, libgtk-3-dev, + libicu-dev, libibus-1.0-dev (>= 1.2), libjson-glib-dev (>= 1.4.0), + libsystemd-dev, pkg-config, xserver-xephyr, xvfb, @@ -55,9 +58,9 @@ Recommends: Description: Type in your language with Keyman for Linux Keyman makes it possible for you to type in over 2,000 languages on Windows, macOS, Linux, iPhone, iPad, Android tablets and phones, and even instantly - in your web browser. With the world’s most powerful keyboarding engine, + in your web browser. With the world's most powerful keyboarding engine, intuitive and rapid text input is now possible in your language, and for - over 99% of the global population’s mother tongues! + over 99% of the global population's mother tongues! . Originally created in 1993 to type Lao on Windows, Keyman is now a free and open source keyboarding platform which allows anyone to write a keyboard @@ -85,7 +88,7 @@ Depends: keyman-engine, python3-bs4, python3-gi, - python3-sentry-sdk (>= 1.1) | python3-raven, + python3-sentry-sdk (>= 1.1), dbus-x11, ${misc:Depends}, ${python3:Depends}, @@ -105,7 +108,7 @@ Description: Keyman for Linux configuration information about Keyman keyboard packages. Package: libkmnkbp-dev -Architecture: any +Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64 Section: libdevel Depends: libkmnkbp0-0 (= ${binary:Version}), @@ -129,7 +132,7 @@ Description: Development files for Keyman keyboard processing library This package contains development headers and libraries. Package: libkmnkbp0-0 -Architecture: any +Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64 Section: libs Pre-Depends: ${misc:Pre-Depends}, @@ -155,11 +158,11 @@ Description: Keyman keyboard processing library and applies rules from compiled Keyman keyboard files. Package: ibus-keyman -Architecture: any +Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64 Depends: ibus (>= 1.3.7), - sudo, libkmnkbp0-0 (= ${binary:Version}), + keyman-system-service (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends}, Provides: @@ -181,3 +184,24 @@ Description: Keyman engine for IBus . This package provides the Keyman IM engine for IBus. With this module, you can use keyboard layouts designed for Keyman under the IBus platform. + +Package: keyman-system-service +Architecture: any +Depends: + libevdev2, + ${misc:Depends}, + ${shlibs:Depends}, +Description: Keyman system service + Originally created in 1993 to type Lao on Windows, Keyman is now a free and + open source keyboarding platform which allows anyone to write a keyboard + layout for their language. Keyman is available for many platforms, including + Windows, macOS, iOS, Android, Linux and the web. + . + Keyboard layouts are defined with a clear and easy to understand keyboard + grammar. Keyman's contextual input model means keyboard layouts can be + intelligent and make it simple to type even the most complex languages. + Keyboard layouts are distributed through an open catalog to all major desktop + and mobile platforms. + . + This package provides a DBus/systemd system service as a helper for + running Keyman with Wayland. diff --git a/linux/debian/ibus-keyman.postinst b/linux/debian/ibus-keyman.postinst index 4143effb61526534dedaf9110b5376235bc60d26..6cff5c525c0b8ae1c5c0d4c064cf9aa8210c9cd2 100644 --- a/linux/debian/ibus-keyman.postinst +++ b/linux/debian/ibus-keyman.postinst @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/bash -# Don't call `set -e`. Even if some commands should fail, it's still -# worth running the rest of the commands. +# Exit on errors - Debian policy 10.4 +set -e case "$1" in @@ -12,7 +12,7 @@ case "$1" in if which sudo > /dev/null && which ps > /dev/null; then # check for gnome-shell as it works differently - gspid=$(ps -C gnome-shell -o pid=|head -n 1) + ! gspid=$(ps -C gnome-shell -o pid=|head -n 1) if [ "$gspid" != "" ]; then # gnome-shell has multiple ibus-daemon processes and needs exit instead of restart is_gnome_shell=1 @@ -21,7 +21,7 @@ case "$1" in fi # Restart IBus if it is running - ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1) + ! ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1) if [ "$ibuspid" != "" ]; then if [ "$is_gnome_shell" = "1" ]; then @@ -41,7 +41,7 @@ case "$1" in # Verify that it's running now if [ -n "$SUDO_USER" ] && id "$SUDO_USER" > /dev/null 2>/dev/null; then - ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*") + ! ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*") if [ "$ibusdaemon" = "" ]; then # otherwise try to start it for the user installing the package if [ "$is_gnome_shell" = "1" ]; then diff --git a/linux/debian/keyman-system-service.install b/linux/debian/keyman-system-service.install new file mode 100644 index 0000000000000000000000000000000000000000..f7ce93bd82b3a8f77baee2d76a13124ba317500f --- /dev/null +++ b/linux/debian/keyman-system-service.install @@ -0,0 +1,5 @@ +usr/lib/systemd/system/systemd-keyman.service +usr/libexec/keyman-system-service +usr/share/dbus-1/interfaces/com.keyman.SystemService1.System.xml +usr/share/dbus-1/system-services/com.keyman.SystemService1.service +usr/share/dbus-1/system.d/com.keyman.SystemService1.conf diff --git a/linux/debian/rules b/linux/debian/rules index e67ea1b5af5d2067f7b93a324564b00ee7971476..ab4ad409e4e38c790121455b5e84b54834aa4af1 100755 --- a/linux/debian/rules +++ b/linux/debian/rules @@ -9,6 +9,8 @@ export PYBUILD_INSTALL_ARGS=--install-scripts=/usr/share/keyman-config/ export KEYMAN_PKG_BUILD=1 +export XDG_DATA_HOME=$(shell mktemp -d) + # xenial needs this to be explicit export LC_ALL=C.UTF-8 @@ -22,23 +24,20 @@ include /usr/share/dpkg/pkg-info.mk override_dh_auto_configure: # keymankeyboardprocessor - we need to configure and build core before # we can configure ibus-keyman! - cd core && \ - ./build.sh --no-tests configure:arch build:arch -- --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ - --localstatedir=/var --libdir=lib/$(DEB_TARGET_MULTIARCH) \ - --libexecdir=lib/$(DEB_TARGET_MULTIARCH) - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh configure -- --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ - --localstatedir=/var + core/build.sh --no-tests configure:arch build:arch -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ + --localstatedir=/var --libdir=lib/$(DEB_TARGET_MULTIARCH) \ + --libexecdir=lib/$(DEB_TARGET_MULTIARCH) + linux/ibus-keyman/build.sh configure -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var + linux/keyman-system-service/build.sh configure -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var # keyman-config override_dh_auto_build: - # keymankeyboardprocessor - cd core && \ - ./build.sh --no-tests build:arch - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh build + core/build.sh --no-tests build:arch + linux/ibus-keyman/build.sh build + linux/keyman-system-service/build.sh build # keyman-config cd linux/keyman-config && \ make man && \ @@ -48,23 +47,18 @@ override_dh_auto_build: override_dh_auto_test: ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - # keymankeyboardprocessor - cd core && \ - ./build.sh --no-tests test:arch - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh test + core/build.sh --no-tests test:arch + linux/ibus-keyman/build.sh test + linux/keyman-system-service/build.sh test # keyman-config dh_auto_test --sourcedir=linux/keyman-config --buildsystem=pybuild $@ endif override_dh_auto_install: install -d $(CURDIR)/debian/tmp - cd core && \ - DESTDIR=$(CURDIR)/debian/tmp ./build.sh --no-tests install:arch - # ibus-keyman - cd linux/ibus-keyman && \ - DESTDIR=$(CURDIR)/debian/tmp ./build.sh install + DESTDIR=$(CURDIR)/debian/tmp core/build.sh --no-tests install:arch + DESTDIR=$(CURDIR)/debian/tmp linux/ibus-keyman/build.sh install + DESTDIR=$(CURDIR)/debian/tmp linux/keyman-system-service/build.sh install # keyman-config install -d $(CURDIR)/debian/keyman/usr/share/ cp -r linux/keyman-config/locale/ $(CURDIR)/debian/keyman/usr/share/ @@ -72,7 +66,7 @@ override_dh_auto_install: dh_auto_install --sourcedir=linux/keyman-config --buildsystem=pybuild $@ # Unfortunately bash-completion 2.10 (focal) doesn't yet provide dh-sequence-bash-completion, # which we could add as build-dependency, so we'll have to explicitly call - # dh_bash_completion here... + # dh_bash_completion here instead of using `dh $@ --with-python3 --with bash-completion` dh_bash-completion -O--buildsystem=pybuild dh_python3 -O--buildsystem=pybuild diff --git a/linux/help/common/index.md b/linux/help/common/index.md index 7bbc820dd0de6641616c99fde5f6bf16c9ae40b2..a25eb07da08f2a0d154b8e4d6dad4182ec0fa04b 100644 --- a/linux/help/common/index.md +++ b/linux/help/common/index.md @@ -57,22 +57,28 @@ sudo dpkg --purge ibus-kmfl libkmfl ## Q. What Linux distros will Keyman work with? -**A.** Keyman runs on Debian, Ubuntu, Wasta Linux and can be compiled to run from source in most distributions. +**A.** Keyman runs on Debian, Ubuntu, Wasta Linux and can be compiled to run +from source in most distributions. **Note:** Keyman for Linux no longer supports Ubuntu 16.04 LTS (Xenial Xerus). +Keyman 16 was the last version that supports Ubuntu 18.04 LTS (Bionic Beaver). ## Q. Will my existing Windows Keyman keyboard work with Keyman for Linux? -**A.** Most keyboards will work without change. A small subset of keyboards require features which -are not yet available in Keyman for Linux. These features will be progressively implemented. +**A.** Most keyboards will work without change. A small subset of keyboards +require features which are not yet available in Keyman for Linux. These +features will be progressively implemented. ## Q. How can I disable automatically reporting errors? -**A.** If Keyman crashes, it will automatically send a report to the development team. This report -is anonymous and contains only technical details relating to the crash. It does not include keystroke -data or personally identifying data. If you don't want these automatic error reports to be sent you -can set the environment variable `KEYMAN_NOSENTRY`: +**A.** If Keyman crashes, it will automatically send a report to the development +team. This report is anonymous and contains only technical details relating to +the crash. It does not include keystroke data or personally identifying data. If +you don't want these automatic error reports to be sent you can set the +environment variable `KEYMAN_NOSENTRY` and start `km-config` from +the command line: ```bash export KEYMAN_NOSENTRY=1 +km-config ``` diff --git a/linux/ibus-keyman/build.sh b/linux/ibus-keyman/build.sh index 4eed1e2805871e390e39861c02e5d11451c8bb79..8a16bbb1deafe1f08024fd91c3174f5429d35068 100755 --- a/linux/ibus-keyman/build.sh +++ b/linux/ibus-keyman/build.sh @@ -33,10 +33,11 @@ else fi MESON_PATH="../build/$(uname -m)/$MESON_TARGET" +cd "$THIS_SCRIPT_PATH" + builder_describe_outputs \ configure "${MESON_PATH}/build.ninja" \ - build "${MESON_PATH}/src/ibus-engine-keyman" \ - build "${MESON_PATH}/tests/ibus-keyman-tests" + build "${MESON_PATH}/src/ibus-engine-keyman" if builder_start_action clean; then rm -rf "$THIS_SCRIPT_PATH/../build/" @@ -51,25 +52,25 @@ if builder_start_action configure; then fi if builder_start_action build; then - cd "$MESON_PATH" + cd "$THIS_SCRIPT_PATH/$MESON_PATH" ninja builder_finish_action success build fi if builder_start_action test; then - cd "$MESON_PATH" + cd "$THIS_SCRIPT_PATH/$MESON_PATH" meson test --print-errorlogs $builder_verbose builder_finish_action success test fi if builder_start_action install; then - cd "$MESON_PATH" + cd "$THIS_SCRIPT_PATH/$MESON_PATH" ninja install builder_finish_action success install fi if builder_start_action uninstall; then - cd "$MESON_PATH" + cd "$THIS_SCRIPT_PATH/$MESON_PATH" ninja uninstall builder_finish_action success uninstall fi diff --git a/linux/ibus-keyman/meson.build b/linux/ibus-keyman/meson.build index e8548432d4234201853fe8bcfcce2079ddfe35fd..e4b31df7dbd59d02c6c790d8b9285d8cb42daf7b 100644 --- a/linux/ibus-keyman/meson.build +++ b/linux/ibus-keyman/meson.build @@ -9,16 +9,16 @@ project('ibus-keyman', 'c', 'cpp', cc = meson.get_compiler('c') conf = configuration_data() -ibus = dependency('ibus-1.0', version: '>= 1.2.0') -gtk = dependency('gtk+-3.0', version: '>= 2.4') -x11 = dependency('x11', version: '>= 1.6') -json_glib = dependency('json-glib-1.0', version: '>= 1.0') +ibus = dependency('ibus-1.0', version: '>= 1.2.0') +gtk = dependency('gtk+-3.0', version: '>= 2.4') +json_glib = dependency('json-glib-1.0', version: '>= 1.0') +systemd = dependency('libsystemd') core_dir = meson.current_source_dir() / '../../core' common_dir = meson.current_source_dir() / '../../common' kmnkbp_lib = cc.find_library( - 'libkmnkbp0', + 'kmnkbp0', # meson will prefix 'lib' dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'src' ] ) diff --git a/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp b/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90f459d1a21e853e0f5509756822c4292e2fd299 --- /dev/null +++ b/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp @@ -0,0 +1,106 @@ +#include +#include +#include "KeymanSystemServiceClient.h" + +#define KEYMAN_BUS_NAME "com.keyman.SystemService1" +#define KEYMAN_INTERFACE_NAME "com.keyman.SystemService1.System" +#define KEYMAN_OBJECT_PATH "/com/keyman/SystemService1/System" + +extern gboolean testing; + +class KeymanSystemServiceClient { +private: + sd_bus_error *error = NULL; + sd_bus_message *msg = NULL; + sd_bus *bus = NULL; + +public: + KeymanSystemServiceClient(); + ~KeymanSystemServiceClient(); + + void SetCapsLockIndicator(guint32 capsLock); + gint32 GetCapsLockIndicator(); +}; + +KeymanSystemServiceClient::KeymanSystemServiceClient() { + int result; + if (testing) { + result = sd_bus_open_user(&bus); + } else { + result = sd_bus_default_system(&bus); + } + if (result < 0) { + g_error("%s: Can't get connection: %s", __FUNCTION__, strerror(-result)); + } +} + +KeymanSystemServiceClient::~KeymanSystemServiceClient() { + if (error) { sd_bus_error_free(error); } + if (msg) { sd_bus_message_unref(msg); } + if (bus) { sd_bus_unref(bus); } +} + +void KeymanSystemServiceClient::SetCapsLockIndicator(guint32 capsLock) { + // If Set/GetCapsLockIndicator is called more than once and previously + // failed, we will leak `error`. + assert(error == NULL); + + if (!bus) { + // we already reported the error, so just return + return; + } + + if (capsLock > 1) { + g_error("%s: Invalid value '%d' for parameter capsLock", __FUNCTION__, capsLock); + return; + } + + int result = sd_bus_call_method(bus, KEYMAN_BUS_NAME, KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, "SetCapsLockIndicator", error, &msg, "b", capsLock); + if (result < 0) { + g_error("%s: Failed to call method SetCapsLockIndicator: %s. %s. %s.", + __FUNCTION__, strerror(-result), error->name, error->message); + return; + } +} + +gint32 KeymanSystemServiceClient::GetCapsLockIndicator() { + // If Set/GetCapsLockIndicator is called more than once and previously + // failed, we will leak `error`. + assert(error == NULL); + + if (!bus) { + // we already reported the error, so just return + return -1; + } + + int result = sd_bus_call_method(bus, KEYMAN_BUS_NAME, KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, "GetCapsLockIndicator", error, &msg, ""); + if (result < 0) { + g_error("%s: Failed to call method GetCapsLockIndicator: %s. %s. %s.", + __FUNCTION__, strerror(-result), error->name, error->message); + return -1; + } + + guint32 capsLock = 0; + result = sd_bus_message_read(msg, "b", &capsLock); + if (result < 0) { + g_error("%s: Failed to parse response message: %s", + __FUNCTION__, strerror(-result)); + return -1; + } + + return capsLock; +} + +void set_capslock_indicator( + guint32 capsLock +) { + KeymanSystemServiceClient client; + client.SetCapsLockIndicator(capsLock); +} + +gint32 get_capslock_indicator() { + KeymanSystemServiceClient client; + return client.GetCapsLockIndicator(); +} diff --git a/linux/ibus-keyman/src/KeymanSystemServiceClient.h b/linux/ibus-keyman/src/KeymanSystemServiceClient.h new file mode 100644 index 0000000000000000000000000000000000000000..746974df5194e43fca411ef8081fdae8f6a0b9ae --- /dev/null +++ b/linux/ibus-keyman/src/KeymanSystemServiceClient.h @@ -0,0 +1,17 @@ +#ifndef __KEYMANSYSTEMSERVICECLIENT_H__ +#define __KEYMANSYSTEMSERVICECLIENT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void set_capslock_indicator(guint32 capsLockState); +gint32 get_capslock_indicator(); + +#ifdef __cplusplus +} +#endif + +#endif // __KEYMANSYSTEMSERVICECLIENT_H__ diff --git a/linux/ibus-keyman/src/engine.c b/linux/ibus-keyman/src/engine.c index b7699f55623e46ac7f84f95697768d0ca141e401..49b9161f9a3f766786a8f60014470f4e2ba0a057 100644 --- a/linux/ibus-keyman/src/engine.c +++ b/linux/ibus-keyman/src/engine.c @@ -3,7 +3,7 @@ /* * Keyman Input Method for IBUS (The Input Bus) * - * Copyright (C) 2009-2022 SIL International + * Copyright (C) 2009-2023 SIL International * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -28,21 +28,13 @@ #include #include -#ifdef GDK_WINDOWING_X11 -#include -#include -#endif - -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - #include #include #include "config.h" #include "keymanutil.h" #include "keyman-service.h" +#include "KeymanSystemServiceClient.h" #include "engine.h" #include "keycodes.h" @@ -90,12 +82,6 @@ struct _IBusKeymanEngine { IBusLookupTable *table; IBusProperty *status_prop; IBusPropList *prop_list; -#ifdef GDK_WINDOWING_X11 - Display *xdisplay; -#endif -#ifdef GDK_WINDOWING_WAYLAND - GdkWaylandDisplay *wldisplay; -#endif commit_queue_item commit_queue[MAX_QUEUE_SIZE]; commit_queue_item *commit_item; @@ -311,29 +297,6 @@ ibus_keyman_engine_init(IBusKeymanEngine *keyman) { ibus_prop_list_append(keyman->prop_list, keyman->status_prop); keyman->state = NULL; -#ifdef GDK_WINDOWING_X11 - keyman->xdisplay = NULL; -#endif -#ifdef GDK_WINDOWING_WAYLAND - keyman->wldisplay = NULL; -#endif - - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY(gdkDisplay)) { - g_debug("Using X11"); - keyman->xdisplay = GDK_DISPLAY_XDISPLAY(gdkDisplay); - } else -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - g_debug("Using Wayland"); - keyman->wldisplay = GDK_WAYLAND_DISPLAY(gdkDisplay); - } else -#endif - { - g_error("unsupported GDK backend"); - } } static km_kbp_cp* get_base_layout() @@ -752,19 +715,7 @@ process_capslock_action( ) { g_message("%s: %s caps-lock", __FUNCTION__, action_item->capsLock ? "Enable" : "Disable"); -#ifdef GDK_WINDOWING_X11 - if (keyman->xdisplay) { - XkbLockModifiers(keyman->xdisplay, XkbUseCoreKbd, LockMask, action_item->capsLock ? LockMask : 0); - - XSync(keyman->xdisplay, False); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - // TODO - if (keyman->wldisplay) { - - } -#endif + set_capslock_indicator(action_item->capsLock); return TRUE; } diff --git a/linux/ibus-keyman/src/kmpdetails.c b/linux/ibus-keyman/src/kmpdetails.c index a6b7fbb18906326405f92f843fd3a84ddffeca77..eba95f4ef9752d4ce3b2c3fcd00480b068631e17 100644 --- a/linux/ibus-keyman/src/kmpdetails.c +++ b/linux/ibus-keyman/src/kmpdetails.c @@ -272,9 +272,6 @@ kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboa } g_object_unref(parser); return JSON_OK; - - - } kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) @@ -295,7 +292,6 @@ kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) return JSON_FILE_NOT_EXISTS; } - json_parser_load_from_file (parser, kmp_json, &error); diff --git a/linux/ibus-keyman/src/main.c b/linux/ibus-keyman/src/main.c index f791cb45640db610194765626ed3fb36c4a307c0..4252fa93e8953ee01ca55fc732cc21db2f4b5e8c 100644 --- a/linux/ibus-keyman/src/main.c +++ b/linux/ibus-keyman/src/main.c @@ -36,12 +36,14 @@ static IBusFactory *factory = NULL; static gboolean xml = FALSE; static gboolean ibus = FALSE; static gboolean verbose = FALSE; +gboolean testing = FALSE; static const GOptionEntry entries[] = { { "xml", 'x', 0, G_OPTION_ARG_NONE, &xml, "generate xml for engines", NULL }, { "ibus", 'i', 0, G_OPTION_ARG_NONE, &ibus, "component is executed by ibus", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "verbose", NULL }, + { "testing", 0, 0, G_OPTION_ARG_NONE, &testing, "component is executed by integration testing", NULL}, { NULL }, }; diff --git a/linux/ibus-keyman/src/meson.build b/linux/ibus-keyman/src/meson.build index 29f265290d51ab76130db7f37d03c4c435cfb1c0..2e83a6510650099ea69dd3686687f137ba52ddfb 100644 --- a/linux/ibus-keyman/src/meson.build +++ b/linux/ibus-keyman/src/meson.build @@ -1,17 +1,13 @@ util_files = files( 'keymanutil.c', - 'keymanutil.h', 'kmpdetails.c', - 'kmpdetails.h', ) engine_files = files( 'main.c', 'engine.c', - 'engine.h', - 'keycodes.h', 'keyman-service.c', - 'keyman-service.h', + 'KeymanSystemServiceClient.cpp', ) include_dirs = [ @@ -20,7 +16,7 @@ include_dirs = [ include_directories(meson.current_build_dir() / '..'), ] -deps = [ibus, gtk, x11, json_glib, kmnkbp_lib] +deps = [ibus, gtk, json_glib, kmnkbp_lib, systemd] prefix = get_option('prefix') cfg = configuration_data() diff --git a/linux/ibus-keyman/src/test/meson.build b/linux/ibus-keyman/src/test/meson.build index c652d8f783f7bbe0be8aa3201bc388d725305f77..7e5cd21dbeab533bd4597326efbaf805e5ce1dc7 100644 --- a/linux/ibus-keyman/src/test/meson.build +++ b/linux/ibus-keyman/src/test/meson.build @@ -3,7 +3,7 @@ keymanutil_sources = [ util_files, ] -keymanutil_deps = [ibus, gtk, x11, json_glib, kmnkbp_lib] +keymanutil_deps = [ibus, gtk, json_glib, kmnkbp_lib] test_env = [ 'G_TEST_SRCDIR=' + meson.current_source_dir(), diff --git a/linux/ibus-keyman/tests/KmDbusTestServer.cpp b/linux/ibus-keyman/tests/KmDbusTestServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d4746163d5171fa3fcbf10d58db8d099e97b961 --- /dev/null +++ b/linux/ibus-keyman/tests/KmDbusTestServer.cpp @@ -0,0 +1,146 @@ +// DBus test server. The server will start and listen on a non-standard DBus. +// It runs until the DBus Exit method gets called. +#include +#include +#include +#include + +#ifndef KEYMAN_TEST_SERVICE_PATH +#warning KEYMAN_TEST_SERVICE_PATH is undefined +#define KEYMAN_TEST_SERVICE_PATH "." +#endif + +#define KEYMAN_TESTSVC_BUS_NAME "com.keyman.ExitTestService" +#define KEYMAN_TESTSVC_INTERFACE_NAME "com.keyman.ExitTestService.Exit" +#define KEYMAN_TESTSVC_OBJECT_PATH "/com/keyman/ExitTestService/Exit" + +using namespace std; + +class KmDbusTestServer +{ +private: + GTestDBus *dbus; + sd_bus_slot *slot = NULL; + sd_bus *bus = NULL; + +public: + KmDbusTestServer(); + ~KmDbusTestServer(); + + void Loop(); +}; + +static int32_t +on_exit_method(sd_bus_message *msg, void *user_data, sd_bus_error *ret_error) { + g_debug("%s called", __FUNCTION__); + guint *exitFlag = (guint *)user_data; + *exitFlag = TRUE; + + return sd_bus_reply_method_return(msg, ""); +} + +static const sd_bus_vtable exit_test_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Exit", "", "", on_exit_method, 0), + SD_BUS_VTABLE_END +}; + +KmDbusTestServer::KmDbusTestServer() +{ + dbus = g_test_dbus_new(G_TEST_DBUS_NONE); + + // Add the private directory with our in-tree service files. + g_test_dbus_add_service_dir(dbus, KEYMAN_TEST_SERVICE_PATH); + + std::cout << "Add service dir to " << KEYMAN_TEST_SERVICE_PATH << std::endl; + + // Start the private D-Bus daemon + g_test_dbus_up(dbus); + + const gchar *address = g_test_dbus_get_bus_address(dbus); + + std::cout << "Test server running on: " << address << std::endl; + + ofstream file("/tmp/km-test-server.env", ios_base::out); + if (file) { + // write the address to a file which can be sourced + file << "export DBUS_SESSION_BUS_ADDRESS=" << address << std::endl; + file.close(); + } +} + +KmDbusTestServer::~KmDbusTestServer() +{ + if (bus) sd_bus_release_name(bus, KEYMAN_TESTSVC_BUS_NAME); + if (slot) sd_bus_slot_unref(slot); + if (bus) sd_bus_close_unref(bus); + + g_test_dbus_down(dbus); + g_object_unref(dbus); +} + +void KmDbusTestServer::Loop() +{ + int ret; + + ret = sd_bus_open_user(&bus); + + if (ret < 0) { + g_error("Failed to connect to system bus: %s", strerror(-ret)); + return; + } + + guint exitFlag = FALSE; + + // Install the object + ret = sd_bus_add_object_vtable(bus, &slot, KEYMAN_TESTSVC_OBJECT_PATH, + KEYMAN_TESTSVC_INTERFACE_NAME, exit_test_service_vtable, &exitFlag); + if (ret < 0) { + g_error("Failed to issue method call: %s", strerror(-ret)); + return; + } + + // Take a well-known service name so that clients can find us + ret = sd_bus_request_name(bus, KEYMAN_TESTSVC_BUS_NAME, 0); + if (ret < 0) { + g_error("Failed to acquire service name: %s", strerror(-ret)); + return; + } + + for (;;) { + // Process requests + ret = sd_bus_process(bus, NULL); + g_debug("sd_bus_process returned %d, exitFlag=%d", ret, exitFlag); + if (ret < 0) { + g_error("Failed to process bus: %s", strerror(-ret)); + return; + } + if (exitFlag) { + // `exitFlag` can be modified by the callback function + // `on_exit_method` which can be called by `sd_bus_process` + g_debug("Exiting loop"); + return; + } + + if (ret > 0) { + // we processed a request, try to process another one, right-away + continue; + } + + // Wait for the next request to process + ret = sd_bus_wait(bus, (uint64_t)-1); + g_debug("sd_bus_wait returned %d", ret); + if (ret < 0) { + g_error("Failed to wait on bus: %s", strerror(-ret)); + return; + } + } +} + +int +main(int argc, char *argv[]) { + KmDbusTestServer testServer; + testServer.Loop(); + + return 0; +} diff --git a/linux/ibus-keyman/tests/README.md b/linux/ibus-keyman/tests/README.md index f7537a79fda820adb7b340a52c063e5362d2efe1..046333be9d4a8218b62632f380579eb51acfd43e 100644 --- a/linux/ibus-keyman/tests/README.md +++ b/linux/ibus-keyman/tests/README.md @@ -1,6 +1,6 @@ # ibus-keyman integration tests -The ibus-keyman integration tests run the tests defined in `core/tests/unit/kmx`. +The ibus-keyman integration tests run the tests defined in `common/test/keyboards/baseline`. The tests run in a separate Xephyr session. Any dconf settings that have to be set during the tests are stored in `~/.config/glib-2.0/settings/keyfile`. @@ -19,7 +19,7 @@ scripts/run-tests.sh ### Run specific tests To run a single test you pass the testname (as found in -`core/build/arch/*/tests/unit/kmx`). Multiple tests should be separated by space. +`common/test/keyboards/baseline`). Multiple tests should be separated by space. ```bash scripts/run-tests.sh -- k_000___null_keyboard k_005___nul_with_initial_context diff --git a/linux/ibus-keyman/tests/StopTestServer.cpp b/linux/ibus-keyman/tests/StopTestServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97ce5bfbe7f8200624e02e8defa05bbb4d4ece42 --- /dev/null +++ b/linux/ibus-keyman/tests/StopTestServer.cpp @@ -0,0 +1,58 @@ +// Call the Exit method on the km-dbus-test-server. Remember to source +// `/tmp/km-test-server.env` prior to running stop-test-server in order +// to run on our non-standard DBus. +#include +#include + +using namespace std; + +class StopTestServer +{ +private: + sd_bus_error* error = NULL; + sd_bus_message* msg = NULL; + sd_bus* bus = NULL; + +public: + StopTestServer(); + ~StopTestServer(); + + int Run(); +}; + +StopTestServer::StopTestServer() +{ +} + +StopTestServer::~StopTestServer() +{ + if (error) { sd_bus_error_free(error); } + if (msg) { sd_bus_message_unref(msg); } + if (bus) { sd_bus_unref(bus); } +} + +int StopTestServer::Run() +{ + int result; + + result = sd_bus_open_user(&bus); + if (result < 0) { + std::cerr << "Can't get connection: " << strerror(-result) << std::endl; + return 1; + } + + result = sd_bus_call_method(bus, "com.keyman.ExitTestService", + "/com/keyman/ExitTestService/Exit", "com.keyman.ExitTestService.Exit", + "Exit", error, &msg, ""); + if (result < 0) { + std::cerr << "Failed to issue method call: " << strerror(-result) << std::endl; + return 1; + } + return 0; +} + +int +main(int argc, char* argv[]) { + StopTestServer stopServer; + return stopServer.Run(); +} diff --git a/linux/ibus-keyman/tests/meson.build b/linux/ibus-keyman/tests/meson.build index 8b2dea17a0e40953f4da58bcd6bf12b4e6611c08..130e3b8bce491473e0b172dd5c289f19494aebfe 100644 --- a/linux/ibus-keyman/tests/meson.build +++ b/linux/ibus-keyman/tests/meson.build @@ -1,21 +1,17 @@ test_files = [ 'ibusimcontext.c', - 'ibusimcontext.h', 'testfixture.cpp', 'testmodule.c', - 'testmodule.h', -] - -kmx_dir = [ - core_dir / 'build/arch' / get_option('buildtype') / 'tests/kmx_test_source' + '../src/KeymanSystemServiceClient.cpp', ] kmnkbp_tests_lib = cc.find_library( - 'libkmnkbp-tests', - dirs: kmx_dir + 'kmnkbp-tests', # meson will prefix 'lib' + dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'tests/kmx_test_source' ] ) -deps = [ibus, gtk, x11, json_glib, kmnkbp_lib, kmnkbp_tests_lib] +test_deps = [ibus, gtk, json_glib, kmnkbp_lib, kmnkbp_tests_lib, systemd] +dbus_deps = [gtk, systemd] test_env = [ 'G_TEST_SRCDIR=' + meson.current_source_dir(), @@ -32,10 +28,27 @@ test_include_dirs = [ test_exe = executable( 'ibus-keyman-tests', test_files, util_files, - dependencies: deps, + dependencies: test_deps, include_directories: test_include_dirs, ) +system_service_dir = meson.current_source_dir() / '../../keyman-system-service/build' / build_machine.cpu_family() / get_option('buildtype') / 'tests/services' + +dbus_test_server = executable( + 'km-dbus-test-server', + 'KmDbusTestServer.cpp', + dependencies: dbus_deps, + cpp_args: [ + '-DKEYMAN_TEST_SERVICE_PATH="' + system_service_dir + '"' + ] +) + +stop_test_server = executable( + 'stop-test-server', + 'StopTestServer.cpp', + dependencies: dbus_deps, +) + env_file = '/tmp/env.txt' pid_file = '/tmp/ibus-keyman-test-pids' @@ -84,7 +97,7 @@ test( kmxtest_files = run_command( find_tests, - kmx_dir, + [ common_dir / 'test/keyboards/baseline' ], check: true, ).stdout().split('\n') diff --git a/linux/ibus-keyman/tests/scripts/run-single-test.sh b/linux/ibus-keyman/tests/scripts/run-single-test.sh index c265c27ccdf3c9c78533dc58b37585b16fca611c..19baca7dd6f8a16d9e5c91f4ddc2e5392d9de29a 100755 --- a/linux/ibus-keyman/tests/scripts/run-single-test.sh +++ b/linux/ibus-keyman/tests/scripts/run-single-test.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash +set -eu TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx -. "$(dirname "$0")"/test-helper.sh +. "$(dirname "$0")"/test-helper.inc.sh if [ -v KEYMAN_PKG_BUILD ]; then # During package builds we skip these tests that require to start ibus because diff --git a/linux/ibus-keyman/tests/scripts/run-tests.sh b/linux/ibus-keyman/tests/scripts/run-tests.sh index e3af25cac17d63c0b1168b06752b14aebd52a917..92cae307ed3e232b10b35b21431bbeb5c3dfa36c 100755 --- a/linux/ibus-keyman/tests/scripts/run-tests.sh +++ b/linux/ibus-keyman/tests/scripts/run-tests.sh @@ -1,9 +1,17 @@ #!/usr/bin/env bash +set -eu -TOP_SRCDIR=${top_srcdir:-$(realpath "$(dirname "$0")/..")} -TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx +TOP_SRCDIR=${top_srcdir:-$(realpath "$(dirname "$0")/../..")} +TESTBASEDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman +TESTDIR=${TESTBASEDIR}/test_kmx +PID_FILE=/tmp/ibus-keyman-test-pids +ENV_FILE=/tmp/keyman-env.txt -. "$(dirname "$0")"/test-helper.sh +. "$(dirname "$0")"/test-helper.inc.sh + +local_cleanup() { + cleanup "$PID_FILE" +} if [ -v KEYMAN_PKG_BUILD ]; then # During package builds we skip these tests that require to start ibus because @@ -20,16 +28,6 @@ if ! which Xvfb > /dev/null || ! which Xephyr > /dev/null || ! which metacity > exit 1 fi -function cleanup() { - if [ -f "$PID_FILE" ]; then - echo - echo "# Shutting down processes..." - bash "$PID_FILE" > /dev/null 2>&1 - rm "$PID_FILE" - echo "# Finished shutdown of processes." - fi -} - function help() { echo "Usage:" echo " $0 [-k] [--tap] [--surrounding-text] [--no-surrounding-text] [--no-wayland] [--no-x11] [[--] TEST...]" @@ -54,79 +52,24 @@ function run_tests() { DISPLAY_SERVER=$1 shift - echo > "$PID_FILE" - TEMP_DATA_DIR=$(mktemp --directory) - echo "rm -rf ${TEMP_DATA_DIR}" >> "$PID_FILE" - - COMMON_ARCH_DIR= - [ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch - - if [ -d "${COMMON_ARCH_DIR}"/release ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release - elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug + if [ -d "$(dirname "$0")/../../../build/$(arch)/debug" ]; then + CONFIG=debug + elif [ -d "$(dirname "$0")/../../../build/$(arch)/release" ]; then + CONFIG=release else - echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" - exit 2 + echo "Cannot find ../../../build/$(arch)/debug or ../../../build/$(arch)/release" + exit 9 fi - # TODO: we are borrowing the KMX tests out of core; these should be shared in common - # to avoid deep links like this in future - KMX_TEST_DIR="${TOP_SRCDIR}"/../../core/tests/unit/kmx - - if [ ! -d "$TESTDIR" ] || ! [[ $(find "${TESTDIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - if [[ $(find "${KMX_TEST_DIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - mkdir -p "$(realpath --canonicalize-missing "$TESTDIR"/..)" - ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" - else - echo "Can't find kmx files in ${KMX_TEST_DIR}" - exit 3 - fi - fi + G_TEST_BUILDDIR="$(dirname "$0")/../../../build/$(arch)/${CONFIG}/tests" + + setup "$DISPLAY_SERVER" "$ENV_FILE" "$PID_FILE" echo "# NOTE: When the tests fail check /tmp/ibus-engine-keyman.log and /tmp/ibus-daemon.log!" echo "" - if [ "$DISPLAY_SERVER" == "wayland" ]; then - if ! can_run_wayland; then - # support for --headless got added in mutter 40.x - echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." - exit 7 - fi - echo "# Running on Wayland..." - TMPFILE=$(mktemp) - # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' - mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s - export WAYLAND_DISPLAY - WAYLAND_DISPLAY=$(cat "$TMPFILE" | grep "Using Wayland display" | cut -d"'" -f2) - rm "$TMPFILE" - else - echo "# Starting Xvfb..." - Xvfb -screen 0 1024x768x24 :33 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1 - echo "# Starting Xephyr..." - DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1 - echo "# Starting metacity" - metacity --display=:32 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - - export DISPLAY=:32 - fi - - # Install schema to temporary directory. This removes the build dependency on the keyman package. - SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas - export XDG_DATA_DIRS=$TEMP_DATA_DIR:$XDG_DATA_DIRS - - mkdir -p "$SCHEMA_DIR" - cp "${TOP_SRCDIR}"/../keyman-config/com.keyman.gschema.xml "$SCHEMA_DIR"/ - glib-compile-schemas "$SCHEMA_DIR" - if [ $# -gt 0 ]; then + #shellcheck disable=SC2206 TESTFILES=($@) else pushd "$TESTDIR" > /dev/null || exit @@ -134,32 +77,20 @@ function run_tests() { popd > /dev/null || exit fi - export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:$LD_LIBRARY_PATH - - # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile - # backend enabled, so we just use the default backend. Otherwise we use the keyfile - # store which interferes less when running on a dev machine. - if [ -f /usr/libexec/ibus-memconf ]; then - export GSETTINGS_BACKEND=keyfile - IBUS_CONFIG=--config=/usr/libexec/ibus-memconf - fi - - ibus-daemon "${ARG_VERBOSE-}" --panel=disable ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s - - ../src/ibus-engine-keyman "${ARG_VERBOSE-}" &> /tmp/ibus-engine-keyman.log & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s - echo "# Starting tests..." + # shellcheck disable=SC1090 + source "$ENV_FILE" + echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" + # Note: -k and --tap are consumed by the GLib testing framework - "${G_TEST_BUILDDIR:-.}"/ibus-keyman-tests "${ARG_K-}" "${ARG_TAP-}" \ - "${ARG_VERBOSE-}" "${ARG_DEBUG-}" "${ARG_SURROUNDING_TEXT-}" "${ARG_NO_SURROUNDING_TEXT-}" \ - --directory "$TESTDIR" --"${DISPLAY_SERVER}" "${TESTFILES[@]}" + #shellcheck disable=SC2068 # we want to split array elements! + #shellcheck disable=SC2086 + "${G_TEST_BUILDDIR:-../../build/$(arch)/${CONFIG}/tests}/ibus-keyman-tests" ${ARG_K-} ${ARG_TAP-} \ + ${ARG_VERBOSE-} ${ARG_DEBUG-} ${ARG_SURROUNDING_TEXT-} ${ARG_NO_SURROUNDING_TEXT-} \ + --directory "$TESTDIR" --"${DISPLAY_SERVER}" ${TESTFILES[@]} echo "# Finished tests." - cleanup + cleanup "$PID_FILE" } USE_WAYLAND=1 @@ -182,7 +113,7 @@ while (( $# )); do shift || (echo "Error: The last argument is missing a value. Exiting."; false) || exit 5 done -if ! can_run_wayland; then +if ! can_run_wayland && [ "$USE_WAYLAND" == "0" ]; then # support for --headless got added in mutter 40.x echo "# WARNING: mutter doesn't support running headless. Skipping Wayland tests." USE_WAYLAND=0 @@ -197,13 +128,18 @@ if [ "$USE_WAYLAND" == "0" ] && [ "$USE_X11" == "0" ]; then exit 6 fi +G_TEST_BUILDDIR="${G_TEST_BUILDDIR:-../../build/$(arch)/debug/tests}" +if [ ! -f "${G_TEST_BUILDDIR}/ibus-keyman-tests" ]; then + G_TEST_BUILDDIR="${G_TEST_BUILDDIR:-../../build/$(arch)/release/tests}" +fi + echo > "$PID_FILE" -trap cleanup EXIT SIGINT +trap local_cleanup EXIT SIGINT if [ "$USE_WAYLAND" == "1" ]; then - run_tests wayland "$@" + ( run_tests wayland "$@" ) fi if [ "$USE_X11" == "1" ]; then - run_tests x11 "$@" + ( run_tests x11 "$@" ) fi diff --git a/linux/ibus-keyman/tests/scripts/setup-tests.sh b/linux/ibus-keyman/tests/scripts/setup-tests.sh index 0b47145aae75deb17ac73e0fca72178d7c866c79..9d591869fbbe46f77252d509beb2257c0efeba71 100755 --- a/linux/ibus-keyman/tests/scripts/setup-tests.sh +++ b/linux/ibus-keyman/tests/scripts/setup-tests.sh @@ -1,114 +1,6 @@ #!/usr/bin/env bash -DISPLAY_SERVER=$1 -ENV_FILE=$2 -PID_FILE=$3 +set -eu -TOP_SRCDIR=${G_TEST_SRCDIR:-$(realpath "$(dirname "$0")/..")}/.. -TOP_BINDIR=${G_TEST_BUILDDIR:-$(realpath "$(dirname "$0/..")")}/.. -TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx +. "$(dirname "$0")/test-helper.inc.sh" -. "$(dirname "$0")/"/test-helper.sh - -echo > "$ENV_FILE" - -if [ -f "$PID_FILE" ]; then - # kill previous instances - "$(dirname "$0")"/teardown-tests.sh "$PID_FILE" -fi - -echo > "$PID_FILE" -TEMP_DATA_DIR=$(mktemp --directory) -echo "rm -rf ${TEMP_DATA_DIR}" >> "$PID_FILE" - -COMMON_ARCH_DIR= -[ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch - -if [ -d "${COMMON_ARCH_DIR}"/release ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release -elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug -else - echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" - exit 2 -fi - -# TODO: we are borrowing the KMX tests out of core; these should be shared in common -# to avoid deep links like this in future -KMX_TEST_DIR="${TOP_SRCDIR}"/../../core/tests/unit/kmx - -if [ ! -d "$TESTDIR" ] || ! [[ $(find "${TESTDIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - if [[ $(find "${KMX_TEST_DIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - mkdir -p "$(realpath --canonicalize-missing "$TESTDIR"/..)" - ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" - else - echo "Can't find kmx files in ${KMX_TEST_DIR}" - exit 3 - fi -fi - -if [ "$DISPLAY_SERVER" == "wayland" ]; then - if ! can_run_wayland; then - # support for --headless got added in mutter 40.x - echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." - exit 7 - fi - echo "Running on Wayland..." - TMPFILE=$(mktemp) - # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' - mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s - export WAYLAND_DISPLAY - WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) - rm "$TMPFILE" - echo "export WAYLAND_DISPLAY=\"$WAYLAND_DISPLAY\"" >> "$ENV_FILE" -else - echo "Starting Xvfb..." - Xvfb -screen 0 1024x768x24 :33 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1 - echo "Starting Xephyr..." - DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1 - echo "Starting metacity" - metacity --display=:32 &> /dev/null & - echo "kill -9 $!" >> "$PID_FILE" - - export DISPLAY=:32 - echo "export DISPLAY=\"$DISPLAY\"" >> "$ENV_FILE" -fi - - # Install schema to temporary directory. This removes the build dependency on the keyman package. - SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas - export XDG_DATA_DIRS=$TEMP_DATA_DIR:$XDG_DATA_DIRS - echo "export XDG_DATA_DIRS=\"$XDG_DATA_DIRS\"" >> "$ENV_FILE" - - mkdir -p "$SCHEMA_DIR" - cp "${TOP_SRCDIR}"/../keyman-config/com.keyman.gschema.xml "$SCHEMA_DIR"/ - glib-compile-schemas "$SCHEMA_DIR" - - export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:$LD_LIBRARY_PATH - echo "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\"" >> "$ENV_FILE" - - # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile - # backend enabled, so we just use the default backend. Otherwise we use the keyfile - # store which interferes less when running on a dev machine. - if [ -f /usr/libexec/ibus-memconf ]; then - export GSETTINGS_BACKEND=keyfile - echo "export GSETTINGS_BACKEND=\"$GSETTINGS_BACKEND\"" >> "$ENV_FILE" - IBUS_CONFIG=--config=/usr/libexec/ibus-memconf - fi - - ibus-daemon "${ARG_VERBOSE-}" --daemonize --panel=disable ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s - - IBUS_ADDRESS=$(ibus address) - export IBUS_ADDRESS - - echo "export IBUS_ADDRESS=\"$IBUS_ADDRESS\"" >> "$ENV_FILE" - - "${TOP_BINDIR}"/src/ibus-engine-keyman "${ARG_VERBOSE-}" &> /tmp/ibus-engine-keyman.log & - echo "kill -9 $!" >> "$PID_FILE" - sleep 1s +setup "$1" "$2" "$3" diff --git a/linux/ibus-keyman/tests/scripts/teardown-tests.sh b/linux/ibus-keyman/tests/scripts/teardown-tests.sh index 489769d7218efed5bce5bd5874ce412cf5f47b05..c3b955a13a3d7aefe846d5160171c5d444ee232d 100755 --- a/linux/ibus-keyman/tests/scripts/teardown-tests.sh +++ b/linux/ibus-keyman/tests/scripts/teardown-tests.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -PID_FILE=$1 +set -eu -echo "Shutting down processes..." -bash "$PID_FILE" > /dev/null 2>&1 -rm "$PID_FILE" -echo "Finished shutdown of processes." +. "$(dirname "$0")/test-helper.inc.sh" + +cleanup "$1" diff --git a/linux/ibus-keyman/tests/scripts/test-helper.inc.sh b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh new file mode 100755 index 0000000000000000000000000000000000000000..930eb4fe2ca684c120d0f57a061bcc6ce86991de --- /dev/null +++ b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash + +function can_run_wayland() { + local MUTTER_VERSION + MUTTER_VERSION=$(mutter --version | head -1 | cut -f2 -d' ' | cut -f1 -d'.') + if (( MUTTER_VERSION < 40 )); then + return 1 + else + return 0 + fi +} + +function generate_kmpjson() { + local TESTDIR + TESTDIR="$1" + pushd "$TESTDIR" > /dev/null || exit + KMPFILE="${TESTDIR}/kmp.json" + cat <<-EOF > "$KMPFILE" + { + "system": { + "keymanDeveloperVersion": "10.0.1099.0", + "fileVersion": "7.0" + }, + "info": { + "name": { + "description": "Test Keyboards" + }, + "version": { + "description": "0.0" + } + }, + "files": [ + { + "name": "kmp.json", + "description": "Package information (JSON)" + } +EOF + for f in k_*.kmx; do + keyboard=$(basename "$f") + keyboard="${keyboard%.*}" + echo " ," >> "$KMPFILE" + cat <<-EOF >> "$KMPFILE" + { + "name": "$keyboard", + "description": "$keyboard" + } +EOF + done + cat <<-EOF >> "$KMPFILE" + ], + "keyboards": [ +EOF + FIRST=true + for f in k_*.kmx; do + keyboard=$(basename "$f") + keyboard="${keyboard%.*}" + if $FIRST; then + FIRST=false + else + echo " ," >> "$KMPFILE" + fi + cat <<-EOF >> "$KMPFILE" + { + "name": "$keyboard", + "id": "$keyboard", + "version": "0.0", + "languages": [ + { + "name": "Undetermined", + "id": "und" + } + ] + } +EOF + done + echo " ]" >> "$KMPFILE" + echo "}" >> "$KMPFILE" + popd > /dev/null || exit +} + +function link_test_keyboards() { + KMX_TEST_DIR=$1 + TESTDIR=$2 + TESTBASEDIR=$3 + + if [[ $(find "${KMX_TEST_DIR}/" -name k_\*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then + mkdir -p "$(realpath --canonicalize-missing "$TESTBASEDIR")" + rm -f "$TESTDIR" + ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" + else + echo "Can't find test kmx files in ${KMX_TEST_DIR}" + exit 3 + fi +} + +function setup() { + local DISPLAY_SERVER ENV_FILE PID_FILE TOP_SRCDIR TOP_BINDIR TESTBASEDIR TESTDIR + DISPLAY_SERVER=$1 + ENV_FILE=$2 + PID_FILE=$3 + + TOP_SRCDIR=${G_TEST_SRCDIR:-$(realpath "$(dirname "$0")/..")}/.. + TOP_BINDIR=${G_TEST_BUILDDIR:-$(realpath "$(dirname "$0/..")")}/.. + TESTBASEDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman + TESTDIR=${TESTBASEDIR}/test_kmx + + echo > "$ENV_FILE" + + if [ -f "$PID_FILE" ]; then + # kill previous instances + "$(dirname "$0")"/teardown-tests.sh "$PID_FILE" + fi + + echo > "$PID_FILE" + TEMP_DATA_DIR=$(mktemp --directory) + echo "rm -rf ${TEMP_DATA_DIR} || true" >> "$PID_FILE" + + COMMON_ARCH_DIR= + [ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch + + if [ -d "${COMMON_ARCH_DIR}"/release ]; then + COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release + elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then + COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug + else + echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" + exit 2 + fi + + link_test_keyboards "${TOP_SRCDIR}/../../common/test/keyboards/baseline" "$TESTDIR" "$TESTBASEDIR" + + generate_kmpjson "$TESTDIR" + + # Start test dbus server + "${TOP_BINDIR}/tests/km-dbus-test-server" &> /dev/null & + sleep 1 + source /tmp/km-test-server.env + cat /tmp/km-test-server.env >> "$ENV_FILE" + cat /tmp/km-test-server.env >> "$PID_FILE" + echo "${TOP_BINDIR}/tests/stop-test-server" >> "$PID_FILE" + + if [ "$DISPLAY_SERVER" == "wayland" ]; then + if ! can_run_wayland; then + # support for --headless got added in mutter 40.x + echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." + exit 7 + fi + echo "Running on Wayland:" + echo "Starting mutter..." + TMPFILE=$(mktemp) + # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' + mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s + export WAYLAND_DISPLAY + WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) + rm "$TMPFILE" + echo "export WAYLAND_DISPLAY=\"$WAYLAND_DISPLAY\"" >> "$ENV_FILE" + else + echo "Running on X11:" + echo "Starting Xvfb..." + Xvfb -screen 0 1024x768x24 :33 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1 + echo "Starting Xephyr..." + DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1 + echo "Starting metacity" + metacity --display=:32 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + + export DISPLAY=:32 + echo "export DISPLAY=\"$DISPLAY\"" >> "$ENV_FILE" + fi + + # Install schema to temporary directory. This removes the build dependency on the keyman package. + SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas + export XDG_DATA_DIRS=$TEMP_DATA_DIR:${XDG_DATA_DIRS-} + echo "export XDG_DATA_DIRS=$XDG_DATA_DIRS" >> "$ENV_FILE" + + export GSETTINGS_SCHEMA_DIR=${SCHEMA_DIR} + echo "export GSETTINGS_SCHEMA_DIR=\"$SCHEMA_DIR\"" >> "$ENV_FILE" + + mkdir -p "$SCHEMA_DIR" + cp "${TOP_SRCDIR}"/../keyman-config/com.keyman.gschema.xml "$SCHEMA_DIR"/ + glib-compile-schemas "$SCHEMA_DIR" + + export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:${LD_LIBRARY_PATH-} + echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> "$ENV_FILE" + + # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile + # backend enabled, so we just use the default backend. Otherwise we use the keyfile + # store which interferes less when running on a dev machine. + if [ -f /usr/libexec/ibus-memconf ]; then + export GSETTINGS_BACKEND=keyfile + echo "export GSETTINGS_BACKEND=\"$GSETTINGS_BACKEND\"" >> "$ENV_FILE" + IBUS_CONFIG=--config=/usr/libexec/ibus-memconf + fi + + #shellcheck disable=SC2086 + ibus-daemon ${ARG_VERBOSE-} --daemonize --panel=disable --address=unix:abstract="${TEMP_DATA_DIR}/test-ibus" ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s + + IBUS_ADDRESS=$(ibus address) + export IBUS_ADDRESS + + echo "export IBUS_ADDRESS=\"$IBUS_ADDRESS\"" >> "$ENV_FILE" + + echo "# DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" + #shellcheck disable=SC2086 + "${TOP_BINDIR}/src/ibus-engine-keyman" --testing ${ARG_VERBOSE-} &> /tmp/ibus-engine-keyman.log & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s +} + +function cleanup() { + local PID_FILE + PID_FILE=$1 + + if [ -f "$PID_FILE" ]; then + echo + echo "# Shutting down processes..." + bash "$PID_FILE" > /dev/null 2>&1 + rm "$PID_FILE" + echo "# Finished shutdown of processes." + fi +} diff --git a/linux/ibus-keyman/tests/scripts/test-helper.sh b/linux/ibus-keyman/tests/scripts/test-helper.sh deleted file mode 100755 index 92c8a7e5287928c2bcc853bdb047e2ef6fbfc2f8..0000000000000000000000000000000000000000 --- a/linux/ibus-keyman/tests/scripts/test-helper.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -function can_run_wayland() { - local MUTTER_VERSION - MUTTER_VERSION=$(mutter --version | head -1 | cut -f2 -d' ' | cut -f1 -d'.') - if (( MUTTER_VERSION < 40 )); then - return 1 - else - return 0 - fi -} diff --git a/linux/ibus-keyman/tests/testfixture.cpp b/linux/ibus-keyman/tests/testfixture.cpp index 3e189258bdb11e19c6ba9b69aea1982473572163..84aab933c836a7fde6518be66a77ddbef1bddada 100644 --- a/linux/ibus-keyman/tests/testfixture.cpp +++ b/linux/ibus-keyman/tests/testfixture.cpp @@ -13,17 +13,10 @@ #include "ibusimcontext.h" #include "keycodes.h" #include "keymanutil.h" +#include "KeymanSystemServiceClient.h" #include "kmx_test_source.hpp" #include "testmodule.h" -#ifdef GDK_WINDOWING_X11 -#include -#include -#endif -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - typedef struct { IBusBus *bus; GtkIMContext *context; @@ -42,12 +35,7 @@ static gboolean use_wayland = FALSE; static GdkWindow *window = NULL; static GMainLoop *thread_loop = NULL; static GTypeModule *module = NULL; -#ifdef GDK_WINDOWING_X11 -static Display *display = NULL; -#endif -#ifdef GDK_WINDOWING_WAYLAND -static GdkWaylandDisplay *wldisplay = NULL; -#endif +gboolean testing = TRUE; static void module_register(GTypeModule *module) { @@ -69,18 +57,6 @@ ibus_keyman_tests_fixture_set_up(IBusKeymanTestsFixture *fixture, gconstpointer window = gtk_widget_get_window(widget); } - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && !display && GDK_IS_X11_DISPLAY(gdkDisplay)) { - display = GDK_DISPLAY_XDISPLAY(gdkDisplay); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && !wldisplay && GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - wldisplay = GDK_WAYLAND_DISPLAY(gdkDisplay); - } -#endif - if (!loaded) { ibus_init(); @@ -114,35 +90,12 @@ ibus_keyman_tests_fixture_tear_down(IBusKeymanTestsFixture *fixture, gconstpoint static void set_caps_lock_state(IBusKeymanTestsFixture *fixture, bool caps_lock_on) { -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XkbLockModifiers(display, XkbUseCoreKbd, LockMask, caps_lock_on ? LockMask : 0); - XSync(display, False); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && wldisplay) { - // TODO - } -#endif + set_capslock_indicator((guint32)caps_lock_on); } static bool get_caps_lock_state(IBusKeymanTestsFixture *fixture) { -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XKeyboardState state; - XGetKeyboardControl(display, &state); - return state.led_mask & 1; - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && wldisplay) { - // TODO - } -#endif - - return false; + return get_capslock_indicator(); } static void @@ -493,23 +446,6 @@ main(int argc, char *argv[]) { return 2; } - if (!argWayland && !argX11) { - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - argWayland = true; - } else -#endif -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY(gdkDisplay)) { - argX11 = true; - } else -#endif - { - g_error("Neither X11 nor Wayland display!"); - } - } - use_wayland = argWayland; int nTests = argc - iArg; char **tests = &argv[iArg]; @@ -528,13 +464,6 @@ main(int argc, char *argv[]) { skipReason = "mnemonic keyboards are not yet supported on Linux (#3345)"; } - // Wayland related tests - #4273 - if (use_wayland && ( - strcmp(filename->str, "k_031___caps_lock") == 0 || - strcmp(filename->str, "k_032___caps_control") == 0)) { - skipReason = "capslock related keyboards are not yet working with Wayland (#4273)"; - } - if (runSurroundingTextTests) { add_test(directory, filename->str, TRUE, skipReason, use_wayland); } @@ -549,20 +478,6 @@ main(int argc, char *argv[]) { // Run tests int retVal = g_test_run(); - // Cleanup -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XCloseDisplay(display); - display = NULL; - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland) { - // REVIEW: do we have to call something for cleanup? - wldisplay = NULL; - } -#endif - test_module_unuse(module); return retVal; } diff --git a/linux/keyman-config/.editorconfig b/linux/keyman-config/.editorconfig index 4b530139d7b44db0fbccb2a55ff018d9c137f63b..f65775da76c6b240b5e899aba70b3e44efb76245 100644 --- a/linux/keyman-config/.editorconfig +++ b/linux/keyman-config/.editorconfig @@ -7,4 +7,7 @@ indent_size = 4 indent_style = tab [Makefile] -indent_style = tab \ No newline at end of file +indent_style = tab + +[*.sh] +indent_size = 2 diff --git a/linux/keyman-config/Makefile b/linux/keyman-config/Makefile index b18b1f133d4cd5294f4f978a1900fb72f7df59a0..41eaab30efb727be2c2e527059e32a1f2d7aa9e4 100644 --- a/linux/keyman-config/Makefile +++ b/linux/keyman-config/Makefile @@ -6,7 +6,14 @@ default: clean version man langtags langtags: cd buildtools && python3 ./build-langtags.py -install: # run as sudo +install: + if [ -n "${SUDO_USER}" ]; then \ + make install-sudo; \ + else \ + make install-temp; \ + fi + +install-sudo: # run as sudo pip3 install qrcode sentry-sdk # eventually change this to: pip3 install . python3 setup.py install diff --git a/linux/keyman-config/build.sh b/linux/keyman-config/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..38cf22e917b40ab8a37c60b15a5748681bc5c66e --- /dev/null +++ b/linux/keyman-config/build.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +builder_describe \ + "Build keyman-config." \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "install install artifacts" \ + "uninstall uninstall artifacts" \ + +builder_parse "$@" + +cd "$THIS_SCRIPT_PATH" + +builder_describe_outputs \ + build "/linux/keyman-config/keyman_config/standards/lang_tags_map.py" + +builder_run_action clean make clean +builder_run_action configure # nothing to do +builder_run_action build make +builder_run_action test ./run-tests.sh +builder_run_action install make install +builder_run_action uninstall make uninstall diff --git a/linux/keyman-config/keyman_config/install_kmp.py b/linux/keyman-config/keyman_config/install_kmp.py index 0caa9ac0fc180fad37151b18800d2ad6d47f5551..fcf8eabc5df0c988bc90ee92647acc7c1ee232ac 100755 --- a/linux/keyman-config/keyman_config/install_kmp.py +++ b/linux/keyman-config/keyman_config/install_kmp.py @@ -281,8 +281,11 @@ class InstallKmp(): def extract_kmp(kmpfile, directory): - with zipfile.ZipFile(kmpfile, "r") as zip_ref: - zip_ref.extractall(directory) + try: + with zipfile.ZipFile(kmpfile, "r") as zip_ref: + zip_ref.extractall(directory) + except zipfile.BadZipFile as e: + raise InstallError(InstallStatus.Abort, e) def process_keyboard_data(keyboardID, packageDir) -> None: diff --git a/linux/keyman-config/keyman_config/install_window.py b/linux/keyman-config/keyman_config/install_window.py index 9ae0d41cc01ab7a2ea1052de1c074b7ebaff8950..b0684d79f984936fafb64f4665c2748d01e1aeb3 100755 --- a/linux/keyman-config/keyman_config/install_window.py +++ b/linux/keyman-config/keyman_config/install_window.py @@ -49,6 +49,7 @@ class InstallKmpWindow(Gtk.Dialog): def __init__(self, kmpfile, viewkmp=None, language=None): logging.debug("InstallKmpWindow: kmpfile: %s", kmpfile) + self.is_error = False self.kmpfile = kmpfile self.viewwindow = viewkmp self.accelerators = None @@ -67,7 +68,13 @@ class InstallKmpWindow(Gtk.Dialog): mainhbox = Gtk.Box() with tempfile.TemporaryDirectory() as tmpdirname: - extract_kmp(kmpfile, tmpdirname) + try: + extract_kmp(kmpfile, tmpdirname) + except InstallError as e: + self._handle_install_error(e, kmpfile) + self.is_error = True + return + info, system, options, keyboards, files = get_metadata(tmpdirname) if not keyboards: # Likely not a keyboard .kmp file @@ -357,27 +364,30 @@ class InstallKmpWindow(Gtk.Dialog): dialog.run() dialog.destroy() except InstallError as e: - if e.status == InstallStatus.Abort: - message = _("Keyboard {name} could not be installed.").format(name=self.kbname) \ - + "\n\n" + _("Error Message:") + "\n %s" % (e.message) - logging.error(message) - message_type = Gtk.MessageType.ERROR - else: - message = _("Keyboard {name} could not be installed.").format(name=self.kbname) \ - + "\n\n" + _("Warning Message:") + "\n %s" % (e.message) - logging.warning(message) - message_type = Gtk.MessageType.WARNING - dialog = Gtk.MessageDialog( - self, 0, message_type, - Gtk.ButtonsType.OK, message) - dialog.run() - dialog.destroy() + self._handle_install_error(e, self.kbname) self.close() def on_cancel_clicked(self, button): logging.info("Cancel install keyboard") self.response(Gtk.ResponseType.CANCEL) + def _handle_install_error(self, e, kbname): + if e.status == InstallStatus.Abort: + message = _("Keyboard {name} could not be installed.").format(name=kbname) \ + + "\n\n" + _("Error Message:") + "\n %s" % (e.message) + logging.error(message) + message_type = Gtk.MessageType.ERROR + else: + message = _("Keyboard {name} could not be installed.").format(name=kbname) \ + + "\n\n" + _("Warning Message:") + "\n %s" % (e.message) + logging.warning(message) + message_type = Gtk.MessageType.WARNING + dialog = Gtk.MessageDialog( + self, 0, message_type, + Gtk.ButtonsType.OK, message) + dialog.run() + dialog.destroy() + def main(argv): if len(sys.argv) != 2: diff --git a/linux/keyman-config/keyman_config/view_installed.py b/linux/keyman-config/keyman_config/view_installed.py index 671ca2d27924e096d2bb6717c91b1c50f81ec127..01b1a59b89b3e413619c87abd56c479667abeab2 100755 --- a/linux/keyman-config/keyman_config/view_installed.py +++ b/linux/keyman-config/keyman_config/view_installed.py @@ -41,7 +41,7 @@ class ViewInstalledWindowBase(Gtk.Window): def on_close_clicked(self, button): logging.debug("Close application clicked") - Gtk.main_quit() + self.close() def on_refresh_clicked(self, button): logging.debug("Refresh application clicked") @@ -82,6 +82,8 @@ class ViewInstalledWindowBase(Gtk.Window): def install_file(self, kmpfile, language=None): installDlg = InstallKmpWindow(kmpfile, viewkmp=self, language=language) + if installDlg.is_error: + return Gtk.ResponseType.CANCEL result = installDlg.run() installDlg.destroy() return result diff --git a/linux/keyman-system-service/README.md b/linux/keyman-system-service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..358004af8fccd109d83ae9c69c79993c7982c06c --- /dev/null +++ b/linux/keyman-system-service/README.md @@ -0,0 +1,28 @@ +# keyman-system-service + +A DBus system service that allows to access /dev/input/* devices +to toggle capslock and perform other keyboard related actions when +running under Wayland. + +See , +, +, +. + +## Notes + +- If we need to limit access to the interface to only Keyman, we can use + [polkit](https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html) + which allows to add a rule with an action that checks for the program. + +- to see the log file + + ```bash + journalctl -f -u systemd-keyman + ``` + +- to monitor method calls + + ```bash + sudo busctl monitor com.keyman.SystemService1 + ``` diff --git a/linux/keyman-system-service/build.sh b/linux/keyman-system-service/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..9b248c51130d69f2f01a3bdf0b55023f6cb30faf --- /dev/null +++ b/linux/keyman-system-service/build.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +builder_describe \ + "Build keyman-system-service." \ + ":service" \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "install install artifacts" \ + "uninstall uninstall artifacts" + +builder_parse "$@" + +cd "$THIS_SCRIPT_PATH/" + +if builder_is_debug_build; then + MESON_TARGET=debug + export CPPFLAGS=-DG_MESSAGES_DEBUG + export CFLAGS="-O0" + export CXXFLAGS="-O0" +else + MESON_TARGET=release +fi +MESON_PATH="build/$(uname -m)/$MESON_TARGET" + +builder_describe_outputs \ + configure "${MESON_PATH}/build.ninja" \ + build "${MESON_PATH}/src/keyman-system-service" + +builder_run_action clean rm -rf "$THIS_SCRIPT_PATH/build/" + +# shellcheck disable=SC2086 +builder_run_action configure meson setup "$MESON_PATH" --werror --buildtype $MESON_TARGET "${builder_extra_params[@]}" + +if builder_start_action build; then + cd "$MESON_PATH" + ninja + builder_finish_action success build +fi + +if builder_start_action test; then + cd "$MESON_PATH" + meson test --print-errorlogs $builder_verbose + builder_finish_action success test +fi + +if builder_start_action install; then + cd "$MESON_PATH" + ninja install + builder_finish_action success install +fi + +if builder_start_action uninstall; then + cd "$MESON_PATH" + ninja uninstall + builder_finish_action success uninstall +fi diff --git a/linux/keyman-system-service/meson.build b/linux/keyman-system-service/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..596ba48bcd443f4afde80d691c3ab09e23deacbe --- /dev/null +++ b/linux/keyman-system-service/meson.build @@ -0,0 +1,11 @@ +project('keyman-system-service', 'c', 'cpp', + version: run_command('cat', '../../VERSION.md', check: true).stdout().strip(), + license: 'GPL-2+', + meson_version: '>=1.0') + +evdev = dependency('libevdev', version: '>= 1.9') +systemd = dependency('libsystemd') + +subdir('resources') +subdir('src') +subdir('tests') diff --git a/linux/keyman-system-service/resources/com.keyman.SystemService1.conf b/linux/keyman-system-service/resources/com.keyman.SystemService1.conf new file mode 100644 index 0000000000000000000000000000000000000000..e2c564a94bc0e467c0f91c966d58c4766dde488d --- /dev/null +++ b/linux/keyman-system-service/resources/com.keyman.SystemService1.conf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/linux/keyman-system-service/resources/com.keyman.SystemService1.service b/linux/keyman-system-service/resources/com.keyman.SystemService1.service new file mode 100644 index 0000000000000000000000000000000000000000..3966fb1bbcb9003cffa49efa358eabd5ef591ff6 --- /dev/null +++ b/linux/keyman-system-service/resources/com.keyman.SystemService1.service @@ -0,0 +1,7 @@ +# /usr/share/dbus-1/system-services/com.keyman.SystemService1.service + +[D-BUS Service] +Name=com.keyman.SystemService1 +Exec=/bin/false +User=root +SystemdService=systemd-keyman.service diff --git a/linux/keyman-system-service/resources/meson.build b/linux/keyman-system-service/resources/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c28cb81e585a540a0ae407509aec37ecfabc522b --- /dev/null +++ b/linux/keyman-system-service/resources/meson.build @@ -0,0 +1,3 @@ +install_data('com.keyman.SystemService1.conf', install_dir: get_option('datadir') / 'dbus-1/system.d/') +install_data('com.keyman.SystemService1.service', install_dir: get_option('datadir') / 'dbus-1/system-services/') +install_data('systemd-keyman.service', install_dir: get_option('prefix') / 'lib/systemd/system/') diff --git a/linux/keyman-system-service/resources/systemd-keyman.service b/linux/keyman-system-service/resources/systemd-keyman.service new file mode 100644 index 0000000000000000000000000000000000000000..7cb67508a89076395ba0b1b7b9e16a3f04fad3de --- /dev/null +++ b/linux/keyman-system-service/resources/systemd-keyman.service @@ -0,0 +1,51 @@ +# systemd service file +# /usr/lib/systemd/system/systemd-keyman.service +# See https://stackoverflow.com/a/31725112 + +[Unit] +Description=Keyman System Service + +[Service] +Type=dbus +BusName=com.keyman.SystemService1 +ExecStart=/usr/libexec/keyman-system-service +Restart=on-failure + +# Filesystem lockdown +ProtectSystem=strict +ProtectKernelTunables=true +ProtectControlGroups=true +ReadWritePaths=/dev/input/ +PrivateTmp=true + +# Network +PrivateNetwork=true +IPAddressDeny=any +RestrictAddressFamilies=AF_UNIX + +# Execute Mappings +MemoryDenyWriteExecute=true + +# Modules +ProtectKernelModules=true + +# Real-time +RestrictRealtime=true + +# Privilege escalation +NoNewPrivileges=true + +# Capabilities +CapabilityBoundingSet= + +# System call interfaces +LockPersonality=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service + +# Namespaces +PrivateUsers=yes +RestrictNamespaces=yes + +# Locked memory +LimitMEMLOCK=0 diff --git a/linux/keyman-system-service/src/KeyboardDevice.cpp b/linux/keyman-system-service/src/KeyboardDevice.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a744a96b8282e593b5175caa3e06a3e1d551241c --- /dev/null +++ b/linux/keyman-system-service/src/KeyboardDevice.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include "KeyboardDevice.h" + +using namespace std; + +KeyboardDevice::KeyboardDevice() +{ + dev = nullptr; + fd = -1; + hasCapsLockLed = -1; + debug = false; +} + +KeyboardDevice::~KeyboardDevice() +{ + if (dev) { + libevdev_free(dev); + } + if (fd != -1) { + close(fd); + } +} + +bool KeyboardDevice::Initialize(const char* name) +{ + string path("/dev/input/"); + path.append(name); + + fd = open(path.c_str(), O_RDWR); + if (fd < 0) { + std::cerr << "Failed to open device " << path << ": " << strerror(errno) << std::endl; + return false; + } + + int rc = libevdev_new_from_fd(fd, &dev); + if (rc < 0) { + std::cerr << "Failed to init libevdev for " << path << ": " << strerror(-rc) << std::endl; + close(fd); + fd = -1; + return false; + } + + return true; +} + +bool KeyboardDevice::HasCapsLockLed() +{ + if (hasCapsLockLed < 0) { + if (!libevdev_has_event_code(dev, EV_LED, LED_CAPSL)) { + hasCapsLockLed = 0; + if (debug) { + std::cerr << "Device '" << libevdev_get_name(dev) << "' doesn't have " + << libevdev_event_code_get_name(EV_LED, LED_CAPSL) << "." << std::endl; + } + } else { + hasCapsLockLed = 1; + } + } + return hasCapsLockLed == 1; +} + +void KeyboardDevice::SetCapsLockLed(bool on) +{ + if (!HasCapsLockLed()) { + return; + } + + int rc = libevdev_kernel_set_led_value(dev, LED_CAPSL, on ? LIBEVDEV_LED_ON : LIBEVDEV_LED_OFF); + if (rc != 0) { + std::cerr << "Failed to set LED " << libevdev_event_code_get_name(EV_LED, LED_CAPSL) + << ": " << strerror(-rc) << std::endl; + } +} + +bool KeyboardDevice::GetCapsLockLed() +{ + if (!HasCapsLockLed()) { + return false; + } + + int val = libevdev_get_event_value(dev, EV_LED, LED_CAPSL); + return val; +} diff --git a/linux/keyman-system-service/src/KeyboardDevice.h b/linux/keyman-system-service/src/KeyboardDevice.h new file mode 100644 index 0000000000000000000000000000000000000000..af9ce119f339db7fb2162a2f6e4a310f9330b15d --- /dev/null +++ b/linux/keyman-system-service/src/KeyboardDevice.h @@ -0,0 +1,25 @@ +#ifndef __KEYBOARDDEVICE_H__ +#define __KEYBOARDDEVICE_H__ + +#include + +class KeyboardDevice +{ + public: + KeyboardDevice(); + virtual ~KeyboardDevice(); + + bool Initialize(const char* name); + bool HasCapsLockLed(); + void SetCapsLockLed(bool on); + bool GetCapsLockLed(); + void SetDebug(bool on) { debug = on; } + + private: + struct libevdev *dev; + int fd; + int hasCapsLockLed; + bool debug; +}; + +#endif // __KEYBOARDDEVICE_H__ diff --git a/linux/keyman-system-service/src/KeymanSystemService.cpp b/linux/keyman-system-service/src/KeymanSystemService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..deceafd0c39193c3693c41ab54bdb10330f50107 --- /dev/null +++ b/linux/keyman-system-service/src/KeymanSystemService.cpp @@ -0,0 +1,195 @@ +// This file implements a service which will run on the system dbus, +// based on the sd-bus library, see +// https://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "KeymanSystemService.h" +#include "KeyboardDevice.h" + +#define KEYMAN_BUS_NAME "com.keyman.SystemService1" +#define KEYMAN_INTERFACE_NAME "com.keyman.SystemService1.System" +#define KEYMAN_OBJECT_PATH "/com/keyman/SystemService1/System" + +using namespace std; + +static int32_t +on_set_caps_lock_indicator( + sd_bus_message *msg, + void *user_data, + sd_bus_error *ret_error +) { + int32_t ret; + uint32_t state; + + *ret_error = SD_BUS_ERROR_NULL; + + ret = sd_bus_message_read_basic(msg, 'b', &state); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to parse parameter: %s", strerror(-ret)); + return ret; + } + + KeymanSystemService *service = static_cast(user_data); + service->SetCapsLockIndicatorOnDevices(state); + return sd_bus_reply_method_return(msg, ""); +} + +static int32_t +on_get_caps_lock_indicator( + sd_bus_message *msg, + void *user_data, + sd_bus_error *ret_error +) { + uint32_t state; + + *ret_error = SD_BUS_ERROR_NULL; + + KeymanSystemService *service = static_cast(user_data); + state = service->GetCapsLockIndicatorOnDevices(); + return sd_bus_reply_method_return(msg, "b", state); +} + +static const sd_bus_vtable system_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("SetCapsLockIndicator", "b", "", on_set_caps_lock_indicator, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetCapsLockIndicator", "", "b", on_get_caps_lock_indicator, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +KeymanSystemService::KeymanSystemService() +{ + int ret; + + GetKbdDevices(); + +#ifdef KEYMAN_TESTING + ret = sd_bus_open_user(&bus); +#else + ret = sd_bus_open_system(&bus); +#endif + + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to connect to system bus: %s", strerror(-ret)); + failed = true; + return; + } + + // Install the object + ret = sd_bus_add_object_vtable(bus, + &slot, + KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, + system_service_vtable, + this); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to issue method call: %s", strerror(-ret)); + failed = true; + return; + } + + // Take a well-known service name so that clients can find us + ret = sd_bus_request_name(bus, KEYMAN_BUS_NAME, 0); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to acquire service name: %s", strerror(-ret)); + failed = true; + return; + } +} + +KeymanSystemService::~KeymanSystemService() +{ + if (slot) sd_bus_slot_unref(slot); + if (bus) sd_bus_unref(bus); + + if (kbd_devices) { + for (KeyboardDevice *device : *kbd_devices) { + delete device; + } + delete kbd_devices; + } +} + +int KeymanSystemService::Loop() +{ + for (;;) { + // Process requests + int ret = sd_bus_process(bus, NULL); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to process bus: %s", strerror(-ret)); + failed = true; + return ret; + } + if (ret > 0) { + // we processed a request, try to process another one, right-away + continue; + } + + // Wait for the next request to process + ret = sd_bus_wait(bus, (uint64_t)-1); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to wait on bus: %s", strerror(-ret)); + failed = true; + return ret; + } + } + return 0; +} + +void KeymanSystemService::GetKbdDevices() { + kbd_devices = new std::list(); + + DIR *dirstream; + struct dirent *dir; + dirstream = opendir("/dev/input"); + if (dirstream) { + while ((dir = readdir(dirstream)) != NULL) { + // we're looking for a character device like `event1` + if (dir->d_type == DT_CHR && strncmp(dir->d_name, "event", 5) == 0) { + KeyboardDevice *kbdDevice = new KeyboardDevice(); + if (!kbdDevice->Initialize(dir->d_name) || !kbdDevice->HasCapsLockLed()) { + delete kbdDevice; + continue; + } + kbd_devices->push_back(kbdDevice); + } + } + closedir(dirstream); + } +} + +// Set the CapsLock indicator on all keyboard devices. +void +KeymanSystemService::SetCapsLockIndicatorOnDevices(uint32_t state) { + for (KeyboardDevice *kbdDevice : *kbd_devices) { + if (!kbdDevice->HasCapsLockLed()) { + syslog(LOG_USER | LOG_NOTICE, "%s: No kbdDevice or no CapsLockLed", __FUNCTION__); + continue; + } + syslog(LOG_USER | LOG_NOTICE, "%s: Caps Lock indicator: %s", __FUNCTION__, (state != 0 ? "ON" : "OFF")); + kbdDevice->SetCapsLockLed(state != 0); + } +} + +// Get the CapsLock indicator state from the list of keyboard devices. +// This will return true if any keyboard has the CapsLock indicator lit. +uint32_t +KeymanSystemService::GetCapsLockIndicatorOnDevices() { + bool state = false; + for (KeyboardDevice *kbdDevice : *kbd_devices) { + if (!kbdDevice->HasCapsLockLed()) { + syslog(LOG_USER | LOG_NOTICE, "%s: No kbdDevice or no CapsLockLed", __FUNCTION__); + continue; + } + state = state || kbdDevice->GetCapsLockLed(); + } + return state; +} diff --git a/linux/keyman-system-service/src/KeymanSystemService.h b/linux/keyman-system-service/src/KeymanSystemService.h new file mode 100644 index 0000000000000000000000000000000000000000..2686bfb99f95315ac28ca022ed846e9392e0444e --- /dev/null +++ b/linux/keyman-system-service/src/KeymanSystemService.h @@ -0,0 +1,33 @@ +#ifndef __KEYMANSYSTEMSERVICE_H__ +#define __KEYMANSYSTEMSERVICE_H__ + +#include +#include +#include "KeyboardDevice.h" + +using namespace std; + +class KeymanSystemService { +private: + std::list* kbd_devices = NULL; + sd_bus_slot *slot = NULL; + sd_bus *bus = NULL; + bool failed = false; + + void GetKbdDevices(); + +public: + KeymanSystemService(); + ~KeymanSystemService(); + + bool + Failed() { + return failed; + } + + int Loop(); + void SetCapsLockIndicatorOnDevices(uint32_t state); + uint32_t GetCapsLockIndicatorOnDevices(); +}; + +#endif // __KEYMANSYSTEMSERVICE_H__ diff --git a/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml b/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml new file mode 100644 index 0000000000000000000000000000000000000000..b75a1f23904be79d7483b5fa10cae0f03acb9b96 --- /dev/null +++ b/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/linux/keyman-system-service/src/main.cpp b/linux/keyman-system-service/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a2bb2d0de0e11f8498c87c9882b45daf71271d5 --- /dev/null +++ b/linux/keyman-system-service/src/main.cpp @@ -0,0 +1,10 @@ +#include "KeymanSystemService.h" + +int main(int argc, char *argv[]) { + KeymanSystemService dbusService; + if (dbusService.Failed()) { + return 1; + } + + return dbusService.Loop(); +} diff --git a/linux/keyman-system-service/src/meson.build b/linux/keyman-system-service/src/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..51820ab9fa9eecf70f975c7e731b1373d34251b7 --- /dev/null +++ b/linux/keyman-system-service/src/meson.build @@ -0,0 +1,17 @@ +service_files = files( + 'KeyboardDevice.cpp', + 'KeymanSystemService.cpp', + 'main.cpp', +) + +deps = [evdev, systemd] + +exe = executable( + 'keyman-system-service', + sources: [service_files], + dependencies: deps, + install: true, + install_dir: get_option('libexecdir'), +) + +install_data('com.keyman.SystemService1.System.xml', install_dir: get_option('datadir') / 'dbus-1/interfaces') diff --git a/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp b/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbaa6aafab99ad620c1d3566ab5c463561f653d9 --- /dev/null +++ b/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp @@ -0,0 +1,45 @@ +// A KeyboardDevice mock. +#include +#include +#include +#include +#include +#include + +#ifndef KeyboardDevice +#define KeyboardDevice KeyboardDeviceMock +#endif + +#include "KeyboardDevice.h" + +using namespace std; + +KeyboardDeviceMock::KeyboardDeviceMock() { + dev = nullptr; + fd = -1; + hasCapsLockLed = 1; + debug = false; +} + +KeyboardDeviceMock::~KeyboardDeviceMock() { +} + +bool +KeyboardDeviceMock::Initialize(const char* name) { + return true; +} + +bool +KeyboardDeviceMock::HasCapsLockLed() { + return true; +} + +void +KeyboardDeviceMock::SetCapsLockLed(bool on) { + hasCapsLockLed = on ? 1 : 0; +} + +bool +KeyboardDeviceMock::GetCapsLockLed() { + return hasCapsLockLed == 1; +} diff --git a/linux/keyman-system-service/tests/README.md b/linux/keyman-system-service/tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..af77363b65794c4c80ba897b042bfa55eef68bae --- /dev/null +++ b/linux/keyman-system-service/tests/README.md @@ -0,0 +1,11 @@ +# keyman-system-service tests + +This directory defines a keyman-test-service. This is mostly identical to +keyman-system-service. The differences are: + +- it runs on the session bus instead of system bus +- KeyboardDevice (which in production interacts with evdev) is mocked + so that the tests don't require root permissions. + +The keyman-test-service gets called/used by the ibus-keyman integration +tests. diff --git a/linux/keyman-system-service/tests/meson.build b/linux/keyman-system-service/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f90e12d16a23a8992de761ae3aa9626fecb0f36d --- /dev/null +++ b/linux/keyman-system-service/tests/meson.build @@ -0,0 +1,27 @@ +test_service_files = files( + '../src/main.cpp', + '../src/KeymanSystemService.cpp', + 'KeyboardDeviceMock.cpp', +) + +deps = [evdev, systemd] + +test_c_args = [ + '-DKEYMAN_TESTING', + '-DKeyboardDevice=KeyboardDeviceMock' +] + +exe = executable( + 'keyman-test-service', + sources: [test_service_files], + c_args: test_c_args, + cpp_args: test_c_args, + dependencies: deps, + include_directories: [ '../src' ] +) + +# we currently don't have any unit tests for keyman-system-service. +# The keyman-test-service we build here gets used by the ibus-keyman +# integration tests. + +subdir('services') diff --git a/linux/keyman-system-service/tests/services/README.md b/linux/keyman-system-service/tests/services/README.md new file mode 100644 index 0000000000000000000000000000000000000000..17c6950c556659740a761d4d31f8f5cf2d76bfd7 --- /dev/null +++ b/linux/keyman-system-service/tests/services/README.md @@ -0,0 +1,5 @@ +# tests/services + +This directory contains the service file for our keyman test service as +well as some additional files for services that are required for things +to properly work in our test environment. diff --git a/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in b/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in new file mode 100644 index 0000000000000000000000000000000000000000..8294093a7f88a4da344a25705667c66671e9744a --- /dev/null +++ b/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.a11y.Bus +Exec=/usr/libexec/at-spi-bus-launcher diff --git a/linux/keyman-system-service/tests/services/gvfs-daemon.service.in b/linux/keyman-system-service/tests/services/gvfs-daemon.service.in new file mode 100644 index 0000000000000000000000000000000000000000..b5357bdda3aa3766b26a86e17020ddecf468e8b2 --- /dev/null +++ b/linux/keyman-system-service/tests/services/gvfs-daemon.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gtk.vfs.Daemon +Exec=/usr/libexec/gvfsd diff --git a/linux/keyman-system-service/tests/services/keyman-test.service.in b/linux/keyman-system-service/tests/services/keyman-test.service.in new file mode 100644 index 0000000000000000000000000000000000000000..8946119f234d18af79489daf1dc480f74881a532 --- /dev/null +++ b/linux/keyman-system-service/tests/services/keyman-test.service.in @@ -0,0 +1,5 @@ +# Service definition file for keyman-test-service + +[D-BUS Service] +Name=com.keyman.SystemService1 +Exec=@installed_tests_dir@/keyman-test-service diff --git a/linux/keyman-system-service/tests/services/meson.build b/linux/keyman-system-service/tests/services/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e741aa5fbffd4f7d69f71625f5c1dedd4c40de02 --- /dev/null +++ b/linux/keyman-system-service/tests/services/meson.build @@ -0,0 +1,16 @@ +dbus_service_files = [ + 'at-spi-dbus-bus.service', + 'gvfs-daemon.service', + 'keyman-test.service', +] + +config_data = configuration_data() +config_data.set('installed_tests_dir', meson.current_build_dir() / '..') + +foreach service_file : dbus_service_files + configure_file( + input: service_file + '.in', + output: service_file, + configuration: config_data, + ) +endforeach diff --git a/linux/scripts/cow.sh b/linux/scripts/cow.sh index 6e3e778685964b2de7731cd5b4faa71d899facee..e175a666ef606b03a13818df5230259b9847de57 100755 --- a/linux/scripts/cow.sh +++ b/linux/scripts/cow.sh @@ -3,7 +3,7 @@ # If needed set cowbuilder up for building Keyman Debian packages # Then cowbuilder update -distributions='focal jammy kinetic' +distributions='focal jammy kinetic lunar' if ! dpkg-query -l cowbuilder; then echo "installing pbuilder and cowbuilder" diff --git a/linux/scripts/dist.sh b/linux/scripts/dist.sh index 87003f0def416d2210223ce2b6c3c419116b15a8..f8d63b63e353db64aba653e209034cb467080084 100755 --- a/linux/scripts/dist.sh +++ b/linux/scripts/dist.sh @@ -17,7 +17,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" BASEDIR=$(pwd) -if [ "$1" == "origdist" ]; then +if [[ ! -z ${1+x} ]] && [ "$1" == "origdist" ]; then create_origdist=1 shift fi @@ -33,19 +33,33 @@ dch keyman --newversion "${VERSION}" --force-bad-version --nomultimaint dpkg-source --tar-ignore=*~ --tar-ignore=.git --tar-ignore=.gitattributes \ --tar-ignore=.gitignore --tar-ignore=experiments --tar-ignore=debian \ --tar-ignore=.github --tar-ignore=.vscode --tar-ignore=android \ - --tar-ignore=common/models --tar-ignore=common/predictive-text \ - --tar-ignore=common/resources --tar-ignore=common/schemas \ - --tar-ignore=common/test --tar-ignore=common/web --tar-ignore=common/windows \ + --tar-ignore=.devcontainer \ + --tar-ignore=artifacts \ + \ + --tar-ignore=common/models \ + --tar-ignore=common/predictive-text \ + --tar-ignore=common/resources \ + --tar-ignore=common/schemas \ + --tar-ignore=common/test/keyboards/build.* \ + --tar-ignore=common/test/predictive-text \ + --tar-ignore=common/test/resources \ + --tar-ignore=common/web \ + --tar-ignore=common/windows \ + \ --tar-ignore=core/build \ --tar-ignore=developer --tar-ignore=docs --tar-ignore=ios \ --tar-ignore=linux/keyman-config/buildtools/build-langtags.py --tar-ignore=__pycache__ \ --tar-ignore=linux/help --tar-ignore=linux/Jenkinsfile \ --tar-ignore=mac --tar-ignore=node_modules --tar-ignore=oem \ - --tar-ignore=linux/build --tar-ignore=core/build \ + --tar-ignore=linux/build \ + --tar-ignore=linux/builddebs \ + --tar-ignore=linux/ibus-keyman/build \ + --tar-ignore=linux/keyman-system-service/build \ --tar-ignore=resources/devbox --tar-ignore=resources/git-hooks \ --tar-ignore=resources/scopes \ --tar-ignore=resources/build/*.lua --tar-ignore=resources/build/jq* \ --tar-ignore=results \ + --tar-ignore=tmp \ --tar-ignore=web --tar-ignore=windows --tar-ignore=keyman_1* \ --tar-ignore=dist --tar-ignore=.pbuilderrc --tar-ignore=VERSION -Zgzip -b . mv ../keyman_"${VERSION}".tar.gz linux/dist/keyman-"${VERSION}".tar.gz @@ -53,7 +67,7 @@ echo "3.0 (quilt)" > debian/source/format cd "$BASEDIR" # create orig.tar.gz -if [ -n "$create_origdist" ]; then +if [ ! -z "${create_origdist+x}" ]; then cd dist pkgvers="keyman-$VERSION" tar xfz keyman-"${VERSION}".tar.gz diff --git a/linux/scripts/launchpad.sh b/linux/scripts/launchpad.sh index 587334245cd9127fcb6020032b87efa96e1a26ea..30495d2b0e1567278819415fcd9edf5a4a9de098 100755 --- a/linux/scripts/launchpad.sh +++ b/linux/scripts/launchpad.sh @@ -10,24 +10,20 @@ # DIST="" only upload for this distribution # PACKAGEVERSION="" string to append to the package version. Default to "1~sil1" -set -e +set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +# shellcheck source=resources/build/build-utils.sh . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. $(dirname "$THIS_SCRIPT")/package-build.inc.sh +# shellcheck source=linux/scripts/package-build.inc.sh +. "$(dirname "$THIS_SCRIPT")/package-build.inc.sh" checkPrerequisites -if [ "${UPLOAD}" == "yes" ]; then - SIM="" -else - SIM="-s" -fi - if [ "${TIER}" == "stable" ]; then ppa="ppa:keymanapp/keyman" elif [ "${TIER}" == "beta" ]; then @@ -37,32 +33,22 @@ else fi echo "ppa: ${ppa}" -if [ "${DIST:-}" != "" ]; then - distributions="${DIST}" -else - distributions="focal jammy kinetic" -fi - -if [ "${PACKAGEVERSION:-}" != "" ]; then - packageversion="${PACKAGEVERSION}" -else - packageversion="1~sil1" -fi - +distributions="${DIST:-focal jammy kinetic lunar}" +packageversion="${PACKAGEVERSION:-1~sil1}" BASEDIR=$(pwd) rm -rf launchpad mkdir -p launchpad -for proj in ${projects}; do +for proj in ${projects:-}; do downloadSource launchpad - cd "${proj}-${version}" + cd "${proj}-${version:-}" pwd - cp debian/changelog ../${proj}-changelog + cp debian/changelog "../${proj}-changelog" for dist in ${distributions}; do - cp ../${proj}-changelog debian/changelog + cp "../${proj}-changelog" debian/changelog dch -v "${version}-${packageversion}~${dist}" "source package for PPA" dch -D "${dist}" -r "" debuild -d -S -sa -Zxz diff --git a/linux/scripts/package-build.inc.sh b/linux/scripts/package-build.inc.sh index 7134f6719f6932335812441cb6d6015fcba5929e..39ea70f7d1719859c9bba2a3de0240b013628b52 100644 --- a/linux/scripts/package-build.inc.sh +++ b/linux/scripts/package-build.inc.sh @@ -1,14 +1,14 @@ #!/bin/bash function checkPrerequisites() { - if [ "${UPLOAD}" == "yes" ]; then + if [ "${UPLOAD:=}" == "yes" ]; then SIM="" else SIM="-s" fi # Check the tier - if [[ -z "${TIER}" ]]; then + if [[ -z "${TIER:=}" ]]; then echo "TIER.md or \${TIER} must be set to (alpha, beta, stable) to use this script" exit 1 fi @@ -18,18 +18,15 @@ function checkPrerequisites() { exit 1 fi - if [ "${PROJECT}" != "" ]; then - projects="${PROJECT}" - else - projects="keyman" - fi + # shellcheck disable=SC2034 + projects="${PROJECT:=keyman}" } function downloadSource() { local packageDir packageDir=$1 - if [ "${proj}" == "keyman" ]; then + if [ "${proj:=}" == "keyman" ]; then cd "${BASEDIR}" || exit fi @@ -51,7 +48,7 @@ function downloadSource() { mv "${proj}"*.asc "${BASEDIR}/${packageDir}" rm "${proj}"*.debian.tar.xz cd "${BASEDIR}/${packageDir}" || exit - wget -N https://downloads.keyman.com/linux/${TIER}/${dirversion}/SHA256SUMS + wget -N "https://downloads.keyman.com/linux/${TIER}/${dirversion}/SHA256SUMS" sha256sum -c --ignore-missing SHA256SUMS |grep "${proj}" } diff --git a/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings index 06182ea47cbb20a18a09f6261771c207bc11f1fa..35de92b8d831632491abd14ea201191a9a9adfc9 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings +++ b/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings @@ -68,4 +68,4 @@ "copyright-label" = "የባለቤትነት መብት፦"; /* message displayed to alert user to need grant accessibility permission */ -"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system."; +"privacy-alert-text" = "ኪይማን በአግባቡ ለመስራት የተደራሽነት ባህሪያትን ይፈልጋል፦ \n\nበስርዓት ምርጫዎች፣ ደህንነት እና ግላዊነት ውስጥ መዳረሻ ፍቀድ። \nስርዓት እንደገና አስጀምር።"; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/zh-Hans.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/zh-Hans.lproj/Localizable.strings index 32a42e3726f9686e0368ee8c16f68e4b5f1c307b..fb910cdf4c9892ce826cf98cfaf0e8038ecd10f0 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/zh-Hans.lproj/Localizable.strings +++ b/mac/Keyman4MacIM/Keyman4MacIM/zh-Hans.lproj/Localizable.strings @@ -68,4 +68,4 @@ "copyright-label" = "版权:"; /* message displayed to alert user to need grant accessibility permission */ -"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system."; +"privacy-alert-text" = "Keyman 需要以下辅助功能以正常工作:\n\n在系统首选项、安全和隐私中授予访问权限。\n重新启动系统。"; diff --git a/mac/Keyman4MacIM/localdeploy.sh b/mac/Keyman4MacIM/localdeploy.sh index 6b56f7fc3e57ad80077b8de7168798d8d6969f3d..b09bf36bf6949c95a8c3614e720604f6a73d4719 100755 --- a/mac/Keyman4MacIM/localdeploy.sh +++ b/mac/Keyman4MacIM/localdeploy.sh @@ -42,7 +42,7 @@ if [[ -d "$KM4MIM_INSTALL_PATH" ]]; then rm -Rf "$KM4MIM_INSTALL_PATH" fi echo "Copying $KM4MIM_APP_PATH to $IMLIBRARY_PATH..." -cp -R "$KM4MIM_APP_PATH" "$IMLIBRARY_PATH" +/bin/cp -R "$KM4MIM_APP_PATH" "$IMLIBRARY_PATH" if [[ -d "$KM4MIM_INSTALL_PATH" ]]; then exit 0 diff --git a/mac/Keyman4MacIM/make-km-dmg.sh b/mac/Keyman4MacIM/make-km-dmg.sh index 645abb72edbf1e1e463a6bb3470889a4106395be..b79f40e8b4842ad861c493383e892a5fc8270cb6 100755 --- a/mac/Keyman4MacIM/make-km-dmg.sh +++ b/mac/Keyman4MacIM/make-km-dmg.sh @@ -174,7 +174,7 @@ displayInfo "Copying files from \"$SOURCE_KM_APP\"..." find "$DEST_KM_APP" -mindepth 1 -maxdepth 1 echo "---------" find "$DEST_KM_APP" -mindepth 1 -maxdepth 1 -print0 | xargs -0 rm -rf -cp -fR "$SOURCE_KM_APP/" "$DEST_KM_APP" +/bin/cp -fR "$SOURCE_KM_APP/" "$DEST_KM_APP" echo "---- Listing info in /Volumes/Keyman/ ----" echo "DEST_KM_APP=$DEST_KM_APP" diff --git a/mac/Keyman4MacIM/write-download_info.sh b/mac/Keyman4MacIM/write-download_info.sh index 716caea1a9b5f87a7cbb6bd942cbc57cc7e1fbc5..d185185ffbf46095626b347c228d771f73e05a5f 100755 --- a/mac/Keyman4MacIM/write-download_info.sh +++ b/mac/Keyman4MacIM/write-download_info.sh @@ -81,7 +81,7 @@ DOWNLOAD_INFO_FILEPATH="${DMG_FILEPATH}.download_info" if [[ ! -f "$DMG_FILEPATH" ]]; then builder_die "Cannot compute file size or MD5 for non-existent DMG file: $DMG_FILEPATH" fi -DMG_FILE_SIZE=$(stat -f"%z" "$DMG_FILEPATH") +DMG_FILE_SIZE=$(/usr/bin/stat -f"%z" "$DMG_FILEPATH") DMG_MD5=$(md5 -q "$DMG_FILEPATH") if [[ -f "$DOWNLOAD_INFO_FILEPATH" ]]; then diff --git a/mac/build.sh b/mac/build.sh index ee8e349f80af4eb6823a9852d7eb791a25cdb693..5b6a3f525b8e4e9296e4be223f9fa0df6fe521f6 100755 --- a/mac/build.sh +++ b/mac/build.sh @@ -109,9 +109,6 @@ KMIM_WORKSPACE_PATH="$KM4MIM_BASE_PATH/$IM_NAME.xcworkspace" # APP_BUNDLE_PATH=$APP_RESOURCES/Keyman.bundle # APP_BUILD_PATH=keyman/Keyman/build/ - -KME4M_OUTPUT_FOLDER=$KME4M_BUILD_PATH/libKeyman - ### PROCESS COMMAND-LINE ARGUMENTS ### # Default is debug build of Engine and (code-signed) Input Method @@ -135,6 +132,7 @@ QUIET=false NOTARIZE=false SKIP_BUILD=false UPLOAD_SENTRY=false +QUIET_FLAG= # Import local environment variables for build if [[ -f $(dirname "$THIS_SCRIPT")/localenv.sh ]]; then @@ -262,6 +260,7 @@ displayInfo "" \ "BUILD_ACTIONS: $BUILD_ACTIONS" \ "TEST_ACTION: $TEST_ACTION" \ "UPLOAD_SENTRY: $UPLOAD_SENTRY" \ + "QUIET_FLAG: $QUIET_FLAG" \ "" ### Validate notarization environment variables ### @@ -276,11 +275,11 @@ if $LOCALDEPLOY && ! $NOTARIZE ; then fi if $PREPRELEASE || $NOTARIZE ; then - if [ ! $DO_CODESIGN ] || [ -z "${CERTIFICATE_ID}" ]; then + if [ ! $DO_CODESIGN ] || [ -z "${CERTIFICATE_ID+x}" ]; then builder_die "Code signing must be configured for deployment. See build.sh -help for details." fi - if [ -z "${APPSTORECONNECT_PROVIDER}" ] || [ -z "${APPSTORECONNECT_USERNAME}" ] || [ -z "${APPSTORECONNECT_PASSWORD}" ]; then + if [ -z "${APPSTORECONNECT_PROVIDER+x}" ] || [ -z "${APPSTORECONNECT_USERNAME+x}" ] || [ -z "${APPSTORECONNECT_PASSWORD+x}" ]; then builder_die "Appstoreconnect Apple ID credentials must be configured in environment. See build.sh -help for details." fi fi @@ -391,7 +390,7 @@ if $DO_KEYMANIM ; then ENTITLEMENTS_FILE=Keyman.entitlements fi - if [ -z "$DEVELOPMENT_TEAM" ]; then + if [ -z "${DEVELOPMENT_TEAM+x}" ]; then DEVELOPMENT_TEAM=3YE4W86L3G fi @@ -414,7 +413,7 @@ if $DO_KEYMANIM ; then if which sentry-cli >/dev/null; then cd "$KM4MIM_BASE_PATH" - sentry-cli upload-dif "build/${CONFIGURATION}" + sentry-cli upload-dif "build/$CONFIG" else builder_die "Error: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases" fi @@ -435,7 +434,7 @@ fi if $PREPRELEASE || $NOTARIZE; then builder_heading "Notarizing app" - if [ "${CODESIGNING_SUPPRESSION}" != "" ] && [ -z "${CERTIFICATE_ID}" ]; then + if [ "${CODESIGNING_SUPPRESSION}" != "" ] && [ -z "${CERTIFICATE_ID+x}" ]; then builder_die "Notarization and signed executable is required for deployment, even locally. Specify CERTIFICATE_ID environment variable for custom certificate." else TARGET_PATH="$KM4MIM_BASE_PATH/build/$CONFIG" @@ -446,7 +445,7 @@ if $PREPRELEASE || $NOTARIZE; then # Note: get-task-allow entitlement must be *off* in our release build (to do this, don't include base entitlements in project build settings) # We may need to re-run the code signing if a custom certificate has been passed in - if [ ! -z "${CERTIFICATE_ID}" ]; then + if [ ! -z "${CERTIFICATE_ID+x}" ]; then builder_heading "Signing with custom certificate (CERTIFICATE_ID environment variable)." codesign --force --options runtime --entitlements Keyman4MacIM/Keyman.entitlements --deep --sign "${CERTIFICATE_ID}" "$TARGET_APP_PATH" fi diff --git a/mac/setup/build.sh b/mac/setup/build.sh index 11fbb66df808f0c825407828bfac17d67c912358..40860222daf7d884a4b6f2ced65160d6059e6c7f 100755 --- a/mac/setup/build.sh +++ b/mac/setup/build.sh @@ -62,10 +62,10 @@ mkdir -p "$TARGETPATH" rm -rf "$TARGETAPP" || true # Copy new install image to output folder -cp -R "$SOURCEAPP" "$TARGETPATH/" +/bin/cp -R "$SOURCEAPP" "$TARGETPATH/" # Copy Keyman.app into install image -cp -R "$KEYMANAPP" "$TARGETAPP/Contents/MacOS/Keyman.app" +/bin/cp -R "$KEYMANAPP" "$TARGETAPP/Contents/MacOS/Keyman.app" # Copy textinputsource into install image cp textinputsource/textinputsource "$TARGETAPP/Contents/MacOS/textinputsource" diff --git a/oem/firstvoices/android/app/build.gradle b/oem/firstvoices/android/app/build.gradle index 737d3462c3071e5fb16a498d5efafc2dee2e4a6d..d03c5634ae7ee94771f6f161fb110b30c7f31b53 100644 --- a/oem/firstvoices/android/app/build.gradle +++ b/oem/firstvoices/android/app/build.gradle @@ -9,11 +9,7 @@ apply from: "$rootPath/version.gradle" android { compileSdkVersion 33 - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } + namespace="com.firstvoices.keyboards" defaultConfig { applicationId "com.firstvoices.keyboards" @@ -70,6 +66,12 @@ android { } } +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + // how to configure the sentry android gradle plugin sentry { // Disables or enables the automatic configuration of Native symbols diff --git a/oem/firstvoices/android/app/src/main/AndroidManifest.xml b/oem/firstvoices/android/app/src/main/AndroidManifest.xml index 40d4eb215ddd91a828ca1db6f52ddf3dd3128602..2249f303501b92295d6a54fe78f527baee307469 100644 --- a/oem/firstvoices/android/app/src/main/AndroidManifest.xml +++ b/oem/firstvoices/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/oem/firstvoices/android/build.sh b/oem/firstvoices/android/build.sh index e118afa875960b1d6fa8ff7af8571ce539147da0..75bcfc672a07ce01e6550c6191a1dddc6b119add 100755 --- a/oem/firstvoices/android/build.sh +++ b/oem/firstvoices/android/build.sh @@ -74,9 +74,6 @@ if builder_start_action clean; then fi if builder_start_action configure; then - # Copy Keyman Engine for Android - cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/${CONFIG}/keyman-engine.aar" "$KEYMAN_ROOT/oem/firstvoices/android/app/libs/keyman-engine.aar" - KEYBOARDS_CSV="$KEYMAN_ROOT/oem/firstvoices/keyboards.csv" KEYBOARDS_CSV_TARGET="$KEYMAN_ROOT/oem/firstvoices/android/app/src/main/assets/keyboards.csv" @@ -90,6 +87,9 @@ if builder_start_action configure; then fi if builder_start_action build; then + # Copy Keyman Engine for Android + cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/oem/firstvoices/android/app/libs/keyman-engine.aar" + echo "BUILD_FLAGS: $BUILD_FLAGS" ./gradlew $DAEMON_FLAG clean $BUILD_FLAGS diff --git a/oem/firstvoices/android/gradle/wrapper/gradle-wrapper.properties b/oem/firstvoices/android/gradle/wrapper/gradle-wrapper.properties index 41dfb87909a877d96c3af4adccce4c7a301b55a2..ae04661ee733431762e7ccf8ab9b7409ed44960c 100644 --- a/oem/firstvoices/android/gradle/wrapper/gradle-wrapper.properties +++ b/oem/firstvoices/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/oem/firstvoices/ios/Cartfile b/oem/firstvoices/ios/Cartfile index ec3127efc7f8555f418ada6a1254f76698566929..10efbc3d5a00674b34fa904f3376c63c23b95f93 100644 --- a/oem/firstvoices/ios/Cartfile +++ b/oem/firstvoices/ios/Cartfile @@ -1,4 +1,4 @@ -github "DaveWoodCom/XCGLogger" ~> 6.1.0 +github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" -github "getsentry/sentry-cocoa" ~> 6.2.1 +github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/oem/firstvoices/ios/Cartfile.resolved b/oem/firstvoices/ios/Cartfile.resolved index 16cb6213b6c2f2a085fd61f59c813a6f2292ec3d..5ac3a55318988538f2f6e3ebf339ac837e43effd 100644 --- a/oem/firstvoices/ios/Cartfile.resolved +++ b/oem/firstvoices/ios/Cartfile.resolved @@ -1,4 +1,4 @@ -github "DaveWoodCom/XCGLogger" "6.1.0" github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.0.0" -github "getsentry/sentry-cocoa" "6.2.1" +github "getsentry/sentry-cocoa" "8.7.0" +github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" diff --git a/oem/firstvoices/ios/build_common.sh b/oem/firstvoices/ios/build_common.sh index 94e318ca7ee2a16ed937520409f3012bf22c1307..04c5ee24cec91cb2bb7592447b349fbb3d06ed30 100755 --- a/oem/firstvoices/ios/build_common.sh +++ b/oem/firstvoices/ios/build_common.sh @@ -154,7 +154,7 @@ if [ $DO_UPDATE = true ]; then fi # Copy resources. - cp -Rf "$KEYMAN_ENGINE_FRAMEWORK_SRC" "$KEYMAN_ENGINE_FRAMEWORK_DST" + /bin/cp -Rf "$KEYMAN_ENGINE_FRAMEWORK_SRC" "$KEYMAN_ENGINE_FRAMEWORK_DST" fi # First things first - update our dependencies. diff --git a/package-lock.json b/package-lock.json index ff4000f1803d313d653ad5974b248e49a255197e..5a6663b179eb3e80b883f4b208695a4cdd023e97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "resources/gosh", "resources/build/version", "core/include/ldml", - "developer/src/kmc-keyboard", + "developer/src/common/web/test-helpers", + "developer/src/kmc-kmn", + "developer/src/kmc-ldml", "developer/src/kmc-model", "developer/src/kmc-model-info", "developer/src/kmc-package", @@ -24,12 +26,19 @@ ], "dependencies": { "@keymanapp/common-types": "file:common/web/types", + "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers", "@keymanapp/hextobin": "file:common/tools/hextobin", "@keymanapp/keyman-version": "file:common/web/keyman-version", "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.59.1", "chai": "^4.3.4", + "eslint": "^8.39.0", + "eslint-config-standard-with-typescript": "^34.0.1", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.7.0", + "eslint-plugin-promise": "^6.1.1", "mocha": "^10.0.0", "mocha-teamcity-reporter": "^4.0.0", "ts-node": "^10.9.1", @@ -734,13 +743,67 @@ "name": "@keymanapp/ldml-keyboard-constants", "license": "MIT" }, + "developer/src/common/web/test-helpers": { + "name": "@keymanapp/developer-test-helpers", + "license": "MIT", + "dependencies": { + "@keymanapp/common-types": "*" + }, + "devDependencies": { + "@types/node": "^10.14.6", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/common/web/test-helpers/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "developer/src/common/web/test-helpers/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "developer/src/common/web/test-helpers/node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, "developer/src/kmc": { "name": "@keymanapp/kmc", "license": "MIT", "dependencies": { "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", - "@keymanapp/kmc-keyboard": "*", + "@keymanapp/kmc-kmn": "*", + "@keymanapp/kmc-ldml": "*", "@keymanapp/kmc-model": "*", "@keymanapp/kmc-model-info": "*", "@keymanapp/kmc-package": "*", @@ -769,15 +832,18 @@ }, "developer/src/kmc-keyboard": { "name": "@keymanapp/kmc-keyboard", + "extraneous": true, "license": "MIT", "dependencies": { "@keymanapp/keyman-version": "*", + "@keymanapp/ldml-keyboard-constants": "*", "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50", "semver": "^7.3.7", "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { + "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", @@ -791,34 +857,77 @@ "typescript": "^4.9.5" } }, - "developer/src/kmc-keyboard/node_modules/@types/mocha": { - "version": "5.2.7", + "developer/src/kmc-kmn": { + "name": "@keymanapp/kmc-kmn", + "license": "MIT", + "dependencies": { + "@keymanapp/keyman-version": "*" + }, + "devDependencies": { + "@keymanapp/developer-test-helpers": "*", + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.6", + "@types/semver": "^7.3.12", + "@types/sinon": "^10.0.13", + "@types/sinon-chai": "^3.2.9", + "@types/xml2js": "^0.4.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "sinon": "^15.0.2", + "sinon-chai": "^3.7.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/kmc-kmn/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, - "license": "MIT" + "dependencies": { + "type-detect": "4.0.8" + } }, - "developer/src/kmc-keyboard/node_modules/@types/node": { - "version": "10.17.60", + "developer/src/kmc-kmn/node_modules/@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", "dev": true, - "license": "MIT" + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } }, - "developer/src/kmc-keyboard/node_modules/ajv": { - "version": "8.11.2", - "license": "MIT", + "developer/src/kmc-kmn/node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type-detect": "4.0.8" } }, - "developer/src/kmc-keyboard/node_modules/ansi-styles": { + "developer/src/kmc-kmn/node_modules/@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -826,10 +935,11 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/chalk": { + "developer/src/kmc-kmn/node_modules/chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -839,23 +949,32 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/color-convert": { + "developer/src/kmc-kmn/node_modules/color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, - "developer/src/kmc-keyboard/node_modules/color-name": { + "developer/src/kmc-kmn/node_modules/color-name": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true }, - "developer/src/kmc-keyboard/node_modules/js-yaml": { + "developer/src/kmc-kmn/node_modules/js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -863,14 +982,11 @@ "js-yaml": "bin/js-yaml.js" } }, - "developer/src/kmc-keyboard/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "developer/src/kmc-keyboard/node_modules/minimatch": { + "developer/src/kmc-kmn/node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -878,10 +994,11 @@ "node": "*" } }, - "developer/src/kmc-keyboard/node_modules/mocha": { + "developer/src/kmc-kmn/node_modules/mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -921,10 +1038,11 @@ "url": "https://opencollective.com/mochajs" } }, - "developer/src/kmc-keyboard/node_modules/mocha/node_modules/escape-string-regexp": { + "developer/src/kmc-kmn/node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -932,10 +1050,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "developer/src/kmc-keyboard/node_modules/mocha/node_modules/supports-color": { + "developer/src/kmc-kmn/node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -946,15 +1065,87 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "developer/src/kmc-keyboard/node_modules/ms": { + "developer/src/kmc-kmn/node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", "dev": true, - "license": "MIT" + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "developer/src/kmc-kmn/node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "developer/src/kmc-kmn/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "developer/src/kmc-kmn/node_modules/sinon": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.2.tgz", + "integrity": "sha512-PCVP63XZkg0/LOqQH5rEU4LILuvTFMb5tNxTHfs6VUMNnZz2XrnGSTZbAGITjzwQWbl/Bl/8hi4G3zZWjyBwHg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } }, - "developer/src/kmc-keyboard/node_modules/supports-color": { + "developer/src/kmc-kmn/node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "developer/src/kmc-kmn/node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "developer/src/kmc-kmn/node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -962,15 +1153,16 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/supports-color/node_modules/has-flag": { + "developer/src/kmc-kmn/node_modules/supports-color/node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/ts-node": { + "developer/src/kmc-kmn/node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", @@ -996,7 +1188,7 @@ "typescript": ">=2.7" } }, - "developer/src/kmc-keyboard/node_modules/ts-node/node_modules/diff": { + "developer/src/kmc-kmn/node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", @@ -1005,42 +1197,22 @@ "node": ">=0.3.1" } }, - "developer/src/kmc-model": { - "name": "@keymanapp/kmc-model", - "license": "MIT", - "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/models-types": "*", - "typescript": "^4.9.5", - "xml2js": "^0.4.19" - }, - "devDependencies": { - "@keymanapp/models-templates": "*", - "@keymanapp/models-wordbreakers": "*", - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.7", - "@types/node": "^10.14.6", - "@types/xml2js": "^0.4.5", - "c8": "^7.12.0", - "chai": "^4.3.4", - "chalk": "^2.4.2", - "esbuild": "^0.15.7", - "mocha": "^10.0.0", - "ts-node": "^10.9.1" - } - }, - "developer/src/kmc-model-info": { - "name": "@keymanapp/kmc-model-info", + "developer/src/kmc-ldml": { "license": "MIT", "dependencies": { "@keymanapp/keyman-version": "*", - "@keymanapp/kmc-package": "*", - "@keymanapp/models-types": "*" + "@keymanapp/ldml-keyboard-constants": "*", + "ajv": "^8.11.0", + "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50", + "semver": "^7.3.7", + "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { + "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", + "@types/semver": "^7.3.12", "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", @@ -1050,20 +1222,38 @@ "typescript": "^4.9.5" } }, - "developer/src/kmc-model-info/node_modules/@types/mocha": { + "developer/src/kmc-ldml/node_modules/@types/mocha": { "version": "5.2.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true }, - "developer/src/kmc-model-info/node_modules/@types/node": { + "developer/src/kmc-ldml/node_modules/@types/node": { "version": "10.17.60", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true }, - "developer/src/kmc-model-info/node_modules/ansi-styles": { + "developer/src/kmc-ldml/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "developer/src/kmc-ldml/node_modules/ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -1071,10 +1261,11 @@ "node": ">=4" } }, - "developer/src/kmc-model-info/node_modules/chalk": { + "developer/src/kmc-ldml/node_modules/chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1084,23 +1275,26 @@ "node": ">=4" } }, - "developer/src/kmc-model-info/node_modules/color-convert": { + "developer/src/kmc-ldml/node_modules/color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, - "developer/src/kmc-model-info/node_modules/color-name": { + "developer/src/kmc-ldml/node_modules/color-name": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, - "developer/src/kmc-model-info/node_modules/js-yaml": { + "developer/src/kmc-ldml/node_modules/js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1108,10 +1302,16 @@ "js-yaml": "bin/js-yaml.js" } }, - "developer/src/kmc-model-info/node_modules/minimatch": { + "developer/src/kmc-ldml/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "developer/src/kmc-ldml/node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1119,10 +1319,11 @@ "node": "*" } }, - "developer/src/kmc-model-info/node_modules/mocha": { + "developer/src/kmc-ldml/node_modules/mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -1162,10 +1363,11 @@ "url": "https://opencollective.com/mochajs" } }, - "developer/src/kmc-model-info/node_modules/mocha/node_modules/escape-string-regexp": { + "developer/src/kmc-ldml/node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1173,10 +1375,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "developer/src/kmc-model-info/node_modules/mocha/node_modules/supports-color": { + "developer/src/kmc-ldml/node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1187,7 +1390,250 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "developer/src/kmc-model-info/node_modules/ms": { + "developer/src/kmc-ldml/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "developer/src/kmc-ldml/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-ldml/node_modules/supports-color/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-ldml/node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "developer/src/kmc-ldml/node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "developer/src/kmc-model": { + "name": "@keymanapp/kmc-model", + "license": "MIT", + "dependencies": { + "@keymanapp/common-types": "*", + "@keymanapp/keyman-version": "*", + "@keymanapp/models-types": "*", + "typescript": "^4.9.5", + "xml2js": "^0.4.19" + }, + "devDependencies": { + "@keymanapp/developer-test-helpers": "*", + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.6", + "@types/xml2js": "^0.4.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "esbuild": "^0.15.7", + "mocha": "^10.0.0", + "ts-node": "^10.9.1" + } + }, + "developer/src/kmc-model-info": { + "name": "@keymanapp/kmc-model-info", + "license": "MIT", + "dependencies": { + "@keymanapp/common-types": "*", + "@keymanapp/models-types": "*" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.6", + "@types/xml2js": "^0.4.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/kmc-model-info/node_modules/@types/mocha": { + "version": "5.2.7", + "dev": true, + "license": "MIT" + }, + "developer/src/kmc-model-info/node_modules/@types/node": { + "version": "10.17.60", + "dev": true, + "license": "MIT" + }, + "developer/src/kmc-model-info/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-model-info/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "developer/src/kmc-model-info/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "developer/src/kmc-model-info/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "developer/src/kmc-model-info/node_modules/js-yaml": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "developer/src/kmc-model-info/node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "developer/src/kmc-model-info/node_modules/mocha": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "developer/src/kmc-model-info/node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "developer/src/kmc-model-info/node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "developer/src/kmc-model-info/node_modules/ms": { "version": "2.1.3", "dev": true, "license": "MIT" @@ -1316,9 +1762,11 @@ "name": "@keymanapp/kmc-package", "license": "MIT", "dependencies": { + "@keymanapp/common-types": "*", "jszip": "^3.7.0" }, "devDependencies": { + "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", @@ -1859,26 +2307,132 @@ "node": ">=12" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, - "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", + "node_modules/@eslint-community/regexpp": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", + "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.1", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/js": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", + "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1911,6 +2465,10 @@ "resolved": "common/web/gesture-recognizer", "link": true }, + "node_modules/@keymanapp/developer-test-helpers": { + "resolved": "developer/src/common/web/test-helpers", + "link": true + }, "node_modules/@keymanapp/hextobin": { "resolved": "common/tools/hextobin", "link": true @@ -1931,8 +2489,12 @@ "resolved": "developer/src/kmc", "link": true }, - "node_modules/@keymanapp/kmc-keyboard": { - "resolved": "developer/src/kmc-keyboard", + "node_modules/@keymanapp/kmc-kmn": { + "resolved": "developer/src/kmc-kmn", + "link": true + }, + "node_modules/@keymanapp/kmc-ldml": { + "resolved": "developer/src/kmc-ldml", "link": true }, "node_modules/@keymanapp/kmc-model": { @@ -1995,6 +2557,41 @@ "resolved": "common/web/utils", "link": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@npmcli/fs": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", @@ -2367,12 +2964,21 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" } }, "node_modules/@sinonjs/formatio": { @@ -2554,6 +3160,18 @@ "version": "3.0.0", "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/@types/keyv": { "version": "3.1.4", "license": "MIT", @@ -2623,6 +3241,16 @@ "@types/sinonjs__fake-timers": "*" } }, + "node_modules/@types/sinon-chai": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", + "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "dev": true, + "dependencies": { + "@types/chai": "*", + "@types/sinon": "*" + } + }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", @@ -2653,115 +3281,397 @@ "@types/node": "*" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", + "integrity": "sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==", "dev": true, - "license": "ISC" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/type-utils": "5.59.1", + "@typescript-eslint/utils": "5.59.1", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">= 0.6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/acorn": { - "version": "8.7.0", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "ms": "2.1.2" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/acorn-walk": { - "version": "8.2.0", + "node_modules/@typescript-eslint/parser": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", + "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==", "dev": true, - "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", + "debug": "^4.3.4" + }, "engines": { - "node": ">=0.4.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/agent-base": { - "version": "6.0.2", - "license": "MIT", + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "debug": "4" + "ms": "2.1.2" }, "engines": { - "node": ">= 6.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", - "optional": true, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz", + "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==", + "dev": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1" }, "engines": { - "node": ">= 8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "optional": true, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz", + "integrity": "sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==", + "dev": true, "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@typescript-eslint/typescript-estree": "5.59.1", + "@typescript-eslint/utils": "5.59.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/ansi-colors": { - "version": "4.1.1", + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">=6" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/ansi-regex": { - "version": "2.1.1", + "node_modules/@typescript-eslint/types": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz", + "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz", + "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.1.tgz", + "integrity": "sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz", + "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", "dev": true, "license": "ISC", "dependencies": { @@ -2816,6 +3726,19 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "license": "MIT" @@ -2827,6 +3750,70 @@ "dev": true, "license": "MIT" }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "dev": true, @@ -2839,6 +3826,18 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.0", "devOptional": true, @@ -3013,6 +4012,15 @@ "version": "1.1.1", "license": "MIT" }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -3166,6 +4174,28 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "5.3.1", "license": "MIT", @@ -3567,6 +4597,12 @@ "node": ">=0.12" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/defer-to-connect": { "version": "2.0.1", "license": "MIT", @@ -3581,6 +4617,22 @@ "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delegates": { "version": "1.0.0", "devOptional": true, @@ -3614,8 +4666,20 @@ "node": ">=0.3.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3757,101 +4821,682 @@ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "optional": true, "engines": { - "node": ">=6" + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-shim": { + "version": "0.35.6", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.15.18", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.18", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", + "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.2", + "@eslint/js": "8.39.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.0", + "espree": "^9.5.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "34.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-34.0.1.tgz", + "integrity": "sha512-J7WvZeLtd0Vr9F+v4dZbqJCLD16cbIy4U+alJMq4MiXdpipdBM3U5NkXaGUjePc4sb1ZE01U9g6VuTBpHHz1fg==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^5.43.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.43.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true - }, - "node_modules/es6-promise": { - "version": "4.2.8", + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/es6-promisify": { - "version": "5.0.0", + "node_modules/espree": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", + "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", "dev": true, - "license": "MIT", "dependencies": { - "es6-promise": "^4.0.3" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/es6-shim": { - "version": "0.35.6", - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.15.18", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "estraverse": "^5.1.0" }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "node": ">=0.10" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "cpu": [ - "x64" - ], + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": ">=4.0" } }, - "node_modules/escalade": { - "version": "3.1.1", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "dependencies": { + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=4.0" } }, "node_modules/esutils": { @@ -4045,6 +5690,43 @@ "version": "3.1.3", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "license": "MIT", @@ -4057,6 +5739,18 @@ "dev": true, "license": "MIT" }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -4124,6 +5818,19 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/flatted": { "version": "3.2.5", "dev": true, @@ -4148,6 +5855,15 @@ } } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "2.0.0", "dev": true, @@ -4237,6 +5953,33 @@ "dev": true, "license": "MIT" }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gauge": { "version": "4.0.4", "devOptional": true, @@ -4318,6 +6061,20 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "5.2.0", "license": "MIT", @@ -4331,6 +6088,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/git-diff": { "version": "2.0.6", "dev": true, @@ -4440,11 +6213,73 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/google-closure-compiler-java": { "version": "20200224.0.0", "dev": true, "license": "Apache-2.0" }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "11.8.5", "license": "MIT", @@ -4473,6 +6308,12 @@ "devOptional": true, "license": "ISC" }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/growl": { "version": "1.10.5", "dev": true, @@ -4481,22 +6322,82 @@ "node": ">=4.x" } }, - "node_modules/has": { - "version": "1.0.3", + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, - "license": "MIT", "dependencies": { - "function-bind": "^1.1.1" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-unicode": { @@ -4629,15 +6530,40 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/immediate": { "version": "3.0.6", "license": "MIT" }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "optional": true, + "devOptional": true, "engines": { "node": ">=0.8.19" } @@ -4670,6 +6596,20 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/interpret": { "version": "1.4.0", "dev": true, @@ -4691,6 +6631,32 @@ "node": ">= 0.10" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -4702,6 +6668,34 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.11.0", "dev": true, @@ -4713,6 +6707,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "2.2.1", "license": "MIT", @@ -4746,9 +6755,10 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -4762,6 +6772,18 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "optional": true }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -4770,6 +6792,30 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "5.0.0", "license": "MIT", @@ -4777,11 +6823,39 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-running": { "version": "2.1.0", "dev": true, "license": "BSD" }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "1.1.0", "license": "MIT", @@ -4789,6 +6863,55 @@ "node": ">=0.10.0" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "dev": true, @@ -4800,6 +6923,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "license": "MIT", @@ -4862,6 +6997,16 @@ "node": ">=8" } }, + "node_modules/js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -4877,6 +7022,30 @@ "version": "3.0.1", "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonfile": { "version": "6.1.0", "dev": true, @@ -5187,6 +7356,19 @@ "json-buffer": "3.0.1" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lie": { "version": "3.3.0", "license": "MIT", @@ -5229,6 +7411,12 @@ "version": "4.4.2", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lodash.set": { "version": "4.3.2", "license": "MIT" @@ -5425,6 +7613,15 @@ "version": "1.0.1", "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "license": "MIT", @@ -5432,6 +7629,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "2.6.0", "dev": true, @@ -6004,6 +8214,18 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "license": "MIT", @@ -6266,11 +8488,64 @@ "node": ">=0.10.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/octokit-pagination-methods": { @@ -6309,6 +8584,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/os-name": { "version": "3.1.0", "license": "MIT", @@ -6388,6 +8680,18 @@ "version": "1.0.11", "license": "(MIT AND Zlib)" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -6427,6 +8731,15 @@ "version": "0.1.7", "license": "MIT" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pathval": { "version": "1.1.1", "dev": true, @@ -6451,9 +8764,10 @@ "license": "MIT" }, "node_modules/picomatch": { - "version": "2.2.3", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6461,6 +8775,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "license": "MIT" @@ -6570,6 +8893,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/quick-lru": { "version": "5.1.1", "license": "MIT", @@ -6646,6 +8989,35 @@ "node": ">= 0.10" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/require-directory": { "version": "2.1.1", "license": "MIT", @@ -6701,6 +9073,15 @@ "version": "1.2.1", "license": "MIT" }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/responselike": { "version": "2.0.0", "license": "MIT", @@ -6723,6 +9104,16 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.3.0", "dev": true, @@ -6742,6 +9133,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -6760,6 +9174,20 @@ ], "license": "MIT" }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" @@ -6907,6 +9335,20 @@ "node": ">= 4.0.0" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "license": "ISC" @@ -6923,6 +9365,40 @@ "diff": "^5.0.0", "nise": "^5.1.2", "supports-color": "^7.2.0" + } + }, + "node_modules/sinon-chai": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", + "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", + "dev": true, + "peerDependencies": { + "chai": "^4.0.0", + "sinon": ">=4.0.0" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "3.5.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" }, "funding": { "type": "opencollective", @@ -6938,6 +9414,15 @@ "type-detect": "4.0.8" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -7167,6 +9652,51 @@ "node_modules/string.prototype.startswith": { "version": "0.2.0" }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "3.0.1", "dev": true, @@ -7178,6 +9708,15 @@ "node": ">=0.10.0" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "license": "MIT", @@ -7290,6 +9829,12 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/through": { "version": "2.3.8", "dev": true, @@ -7428,10 +9973,37 @@ "node": ">=8" } }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "1.14.1", "license": "0BSD" }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, "node_modules/tunnel": { "version": "0.0.6", "license": "MIT", @@ -7439,6 +10011,18 @@ "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "dev": true, @@ -7447,6 +10031,18 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "license": "MIT", @@ -7458,6 +10054,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray": { "version": "0.0.6", "license": "MIT" @@ -7497,6 +10107,21 @@ "dev": true, "license": "MIT" }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -7641,10 +10266,46 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "license": "ISC" }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wide-align": { "version": "1.1.3", "dev": true, @@ -7666,6 +10327,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { "version": "6.1.0", "dev": true, diff --git a/package.json b/package.json index f7892d638516864a6ce243a455d05ab472bcb9d8..7a2b53d50d53a777f786760652e187d9e6d5da02 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,22 @@ "mocha": "^10.0.0", "mocha-teamcity-reporter": "^4.0.0", "ts-node": "^10.9.1", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "eslint": "^8.39.0", + "eslint-config-standard-with-typescript": "^34.0.1", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.7.0", + "eslint-plugin-promise": "^6.1.1" }, "scripts": {}, "workspaces": [ "resources/gosh", "resources/build/version", "core/include/ldml", - "developer/src/kmc-keyboard", + "developer/src/common/web/test-helpers", + "developer/src/kmc-kmn", + "developer/src/kmc-ldml", "developer/src/kmc-model", "developer/src/kmc-model-info", "developer/src/kmc-package", @@ -30,6 +38,7 @@ "@keymanapp/hextobin": "file:common/tools/hextobin", "@keymanapp/keyman-version": "file:common/web/keyman-version", "@keymanapp/common-types": "file:common/web/types", - "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml" + "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml", + "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers" } } diff --git a/resources/build.sh b/resources/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..c65af696c7576bbfcd4575a0b1ebf69dd615d362 --- /dev/null +++ b/resources/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +cd "$THIS_SCRIPT_PATH" + +builder_describe "Keyman resources module" clean configure build test +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +# clean, configure, and build are no-ops for resources/ folder; currently +# only have a test for builder.inc.sh + +if builder_start_action test; then + ./build/test/test.sh + builder_finish_action success test +fi diff --git a/resources/build/build-utils-ci.inc.sh b/resources/build/build-utils-ci.inc.sh index 9aafa9c98e5af878150d98209a0269d377bff6c8..de36ae9e9b606556869c22f2f7492240825070f7 100644 --- a/resources/build/build-utils-ci.inc.sh +++ b/resources/build/build-utils-ci.inc.sh @@ -68,8 +68,12 @@ function builder_pull_has_label() { # parameter, will do a dry run # # Note that `package.json` will be dirty after this command, as the `version` -# field will be added to it. This change should not be committed to the -# repository. +# field will be added to it, and @keymanapp dependency versions will also be +# modified. This change should not be committed to the repository. +# +# builder_publish_to_pack and builder_publish_to_npm are similar: +# * builder_publish_to_npm publishes to the public registry +# * builder_publish_to_pack creates a local tarball which can be used to test # # Usage: # ```bash @@ -77,6 +81,15 @@ function builder_pull_has_label() { # ``` # function builder_publish_to_npm() { + _builder_publish_npm_package publish +} + +function builder_publish_to_pack() { + _builder_publish_npm_package pack +} + +function _builder_publish_npm_package() { + local action=$1 local dist_tag=$TIER dry_run= if [[ $TIER == stable ]]; then @@ -87,17 +100,46 @@ function builder_publish_to_npm() { dry_run=--dry-run fi - # We use --no-git-tag-version because our CI system controls version numbering and - # already tags releases. We also want to have the version of this match the - # release of Keyman Developer -- these two versions should be in sync. Because this - # is a large repo with multiple projects and build systems, it's better for us that - # individual build systems don't take too much ownership of git tagging. :) + # We use --no-git-tag-version because our CI system controls version numbering + # and already tags releases. We also want to have the version of this match + # the release of Keyman Developer -- these two versions should be in sync. + # Because this is a large repo with multiple projects and build systems, it's + # better for us that individual build systems don't take too much ownership of + # git tagging. :) npm version --allow-same-version --no-git-tag-version --no-commit-hooks "$VERSION_WITH_TAG" - # Note: In either case, npm publish MUST be given --access public to publish - # a package in the @keymanapp scope on the public npm package index. + # Update all @keymanapp/* [*]dependencies in package.json to the current + # version-with-tag, so that the published version has precise dependencies, + # and we don't accidentally end up with either older or newer deps here + _builder_npm_set_dependency_version + + # Note: In either case, npm publish MUST be given --access public to publish a + # package in the @keymanapp scope on the public npm package index. # # See `npm help publish` for more details. - echo "Publishing $dry_run npm package $THIS_SCRIPT_IDENTIFIER with tag $dist_tag" - npm publish $dry_run --access public --tag $dist_tag + if [[ $action == pack ]]; then + # We can use --publish-to-pack to locally test a package + # before publishing to the package registry + echo "Packing $dry_run npm package $THIS_SCRIPT_IDENTIFIER with tag $dist_tag" + npm pack $dry_run --access public --tag $dist_tag + else # $action == publish + echo "Publishing $dry_run npm package $THIS_SCRIPT_IDENTIFIER with tag $dist_tag" + npm publish $dry_run --access public --tag $dist_tag + fi } + +# Updates all @keymanapp/* [*]dependencies in package.json to the current +# version-with-tag, so that the published version has precise dependencies, and +# we don't accidentally end up with either older or newer deps. This overwrites +# the local package.json, so it does need to be restored afterwards +function _builder_npm_set_dependency_version() { + cat package.json | "$JQ" --arg VERSION_WITH_TAG "$VERSION_WITH_TAG" \ + ' + . + + (try { dependencies: (.dependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { devDependencies: (.devDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { bundleDependencies: (.bundleDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { optionalDependencies: (.optionalDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + ' > package1.json + mv -f package1.json package.json +} \ No newline at end of file diff --git a/resources/build/build-utils.md b/resources/build/build-utils.md index 5c3897e36b9ae1ea284e7ab33d77be66cef406c1..6c27e5e8a4bd9b3e7980d2f6278c0690f0f527dd 100644 --- a/resources/build/build-utils.md +++ b/resources/build/build-utils.md @@ -125,6 +125,10 @@ variable to ensure that we run the correct versions of npm package commands, so there is no need to hard-code path references or add script wrappers to package.json (`npm run