├── gradle.properties ├── graphics ├── ic_launcher.png ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── feature_graphic.png ├── ic_launcher.svg └── feature_graphic.svg ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-ldpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── xml │ │ ├── backup_rules.xml │ │ └── preferences.xml │ ├── values │ │ ├── theme.xml │ │ └── strings.xml │ ├── layout │ │ ├── result.xml │ │ ├── about.xml │ │ ├── search.xml │ │ └── main.xml │ ├── drawable │ │ └── ic_menu.xml │ ├── drawable-night │ │ └── ic_menu.xml │ └── menu │ │ └── navigation.xml │ ├── java │ └── com │ │ └── ids1024 │ │ └── whitakerswords │ │ ├── AboutFragment.kt │ │ ├── SettingsFragment.kt │ │ ├── SearchAdapter.kt │ │ ├── parse_words.kt │ │ ├── SearchFragment.kt │ │ ├── WhitakersWords.kt │ │ └── WordsWrapper.kt │ └── AndroidManifest.xml ├── settings.gradle ├── .gitignore ├── words ├── Dockerfile ├── WORD.MDV └── build-words.sh ├── .github └── workflows │ └── ci.yml ├── .travis └── bintray.json.in ├── words.LICENSE ├── fetch-words-bintray.sh ├── LICENSE ├── .travis.yml ├── README.md ├── gradlew.bat └── gradlew /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | -------------------------------------------------------------------------------- /graphics/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/graphics/ic_launcher.png -------------------------------------------------------------------------------- /graphics/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/graphics/screenshot1.png -------------------------------------------------------------------------------- /graphics/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/graphics/screenshot2.png -------------------------------------------------------------------------------- /graphics/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/graphics/screenshot3.png -------------------------------------------------------------------------------- /graphics/feature_graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/graphics/feature_graphic.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ids1024/whitakers-words-android/HEAD/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/res/values/theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | bin/ 3 | gen/ 4 | local.properties 5 | .gradle/ 6 | build/ 7 | *.iml 8 | .idea/ 9 | release.keystore 10 | words/words-build 11 | words/wordsall.zip 12 | words.tar.xz 13 | .travis/bintray.json 14 | src/main/assets/words 15 | libs/ 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 26 17:54:49 PDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip 7 | -------------------------------------------------------------------------------- /words/Dockerfile: -------------------------------------------------------------------------------- 1 | # sudo podman run --rm --privileged docker.io/multiarch/qemu-user-static --reset -p yes -c yes 2 | # podman build --arch arm64 --tag words-build . 3 | 4 | # Issue with dos2unix version in jammy 5 | FROM alpine:edge 6 | RUN apk update && apk add gcc-gnat unzip wget dos2unix musl-dev 7 | 8 | -------------------------------------------------------------------------------- /src/main/res/layout/result.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: ci 4 | 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - run: ./build-words-docker.sh 11 | - run: ./gradlew assembleDebug 12 | - run: ./gradlew lint 13 | # - run: ./gradlew dokka 14 | - run: ./gradlew ktlintCheck 15 | -------------------------------------------------------------------------------- /src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/drawable-night/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/layout/about.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /.travis/bintray.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "subject": "ids1024", 4 | "repo": "whitakers-words-android", 5 | "name": "master" 6 | }, 7 | "version": { 8 | "name": "%VERSION%" 9 | }, 10 | "files": [ 11 | { 12 | "includePattern": "words.tar.xz", 13 | "uploadPattern": "words-%VERSION%.tar.xz" 14 | }, 15 | { 16 | "includePattern": "build/outputs/apk/debug/(whitakers-words-android-debug).apk", 17 | "uploadPattern": "$1-%VERSION%.apk" 18 | } 19 | ], 20 | "publish": true 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/AboutFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | 9 | /** 10 | * Fragment for the about page. 11 | */ 12 | class AboutFragment : Fragment() { 13 | override fun onCreateView( 14 | inflater: LayoutInflater, 15 | container: ViewGroup?, 16 | savedInstanceState: Bundle? 17 | ): View { 18 | return inflater.inflate(R.layout.about, container, false) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /words.LICENSE: -------------------------------------------------------------------------------- 1 | WORDS, a Latin dictionary, by Colonel William Whitaker (USAF, Retired) 2 | 3 | Copyright William A. Whitaker (1936–2010) 4 | 5 | This is a free program, which means it is proper to copy it and pass 6 | it on to your friends. Consider it a developmental item for which 7 | there is no charge. However, just for form, it is Copyrighted 8 | (c). Permission is hereby freely given for any and all use of program 9 | and data. You can sell it as your own, but at least tell me. 10 | 11 | This version is distributed without obligation, but the developer 12 | would appreciate comments and suggestions. 13 | 14 | All parts of the WORDS system, source code and data files, are made freely 15 | available to anyone who wishes to use them, for whatever purpose. 16 | -------------------------------------------------------------------------------- /fetch-words-bintray.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | version=$(curl https://bintray.com/api/v1/packages/ids1024/whitakers-words-android/master | \ 6 | python -c 'import json, sys; print(json.load(sys.stdin)["latest_version"])') 7 | 8 | rm -f words.tar.xz 9 | curl -L https://dl.bintray.com/ids1024/whitakers-words-android/words-$version.tar.xz -o words.tar.xz 10 | 11 | rm -rf src/main/assets/words 12 | mkdir -p src/main/assets 13 | tar xf words.tar.xz -C src/main/assets 14 | echo "Words extracted to src/main/assets/words from $version on bintray." 15 | 16 | rm -rf libs 17 | mkdir -p libs/{armeabi-v7a,arm64-v8a} 18 | cp src/main/assets/words/words libs/armeabi-v7a/libwords.so 19 | cp src/main/assets/words/words libs/arm64-v8a/libwords.so 20 | echo "Copied to libs/" 21 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/SettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.os.Bundle 4 | import androidx.preference.Preference 5 | import androidx.preference.PreferenceFragmentCompat 6 | 7 | /** 8 | * Fragment for the settings page. 9 | */ 10 | class SettingsFragment : PreferenceFragmentCompat() { 11 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 12 | setPreferencesFromResource(R.xml.preferences, rootKey) 13 | } 14 | 15 | override fun onPreferenceTreeClick(preference: Preference): Boolean { 16 | return if (preference.key == "light_theme") { 17 | requireActivity().recreate() 18 | true 19 | } else { 20 | false 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/res/layout/search.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 10 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2019 Ian D. Scott 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/SearchAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.text.SpannableStringBuilder 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import java.util.ArrayList 10 | 11 | class SearchAdapter(private var results: ArrayList) : RecyclerView.Adapter() { 12 | 13 | override fun getItemCount(): Int { 14 | return results.size 15 | } 16 | 17 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 18 | holder.text_view.text = results[position] 19 | } 20 | 21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 22 | val view = LayoutInflater.from(parent.context) 23 | .inflate(R.layout.result, parent, false) 24 | return ViewHolder(view) 25 | } 26 | 27 | class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { 28 | val text_view: TextView = view.findViewById(R.id.result_text) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /words/WORD.MDV: -------------------------------------------------------------------------------- 1 | HAVE_STATISTICS_FILE N 2 | WRITE_STATISTICS_FILE N 3 | SHOW_DICTIONARY N 4 | SHOW_DICTIONARY_LINE N 5 | SHOW_DICTIONARY_CODES Y 6 | DO_PEARSE_CODES Y 7 | DO_ONLY_INITIAL_WORD N 8 | FOR_WORD_LIST_CHECK N 9 | DO_ONLY_FIXES N 10 | DO_FIXES_ANYWAY N 11 | USE_PREFIXES Y 12 | USE_SUFFIXES Y 13 | USE_TACKONS Y 14 | DO_MEDIEVAL_TRICKS Y 15 | DO_SYNCOPE Y 16 | DO_TWO_WORDS Y 17 | INCLUDE_UNKNOWN_CONTEXT Y 18 | NO_MEANINGS N 19 | OMIT_ARCHAIC Y 20 | OMIT_MEDIEVAL N 21 | OMIT_UNCOMMON Y 22 | DO_I_FOR_J N 23 | DO_U_FOR_V N 24 | PAUSE_IN_SCREEN_OUTPUT N 25 | NO_SCREEN_ACTIVITY N 26 | UPDATE_LOCAL_DICTIONARY N 27 | UPDATE_MEANINGS N 28 | MINIMIZE_OUTPUT Y 29 | START_FILE_CHARACTER '@' 30 | CHANGE_PARAMETERS_CHARACTER '#' 31 | CHANGE_DEVELOPER_MODES_CHARACTER '!' 32 | -------------------------------------------------------------------------------- /words/build-words.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | export PATH=$PWD/toolchain/bin:$PATH 6 | # TARGET=arm-linux-androideabi- 7 | # QEMU=qemu-arm 8 | ANDROID_SYSROOT="$PWD/ndk-chain/sysroot" 9 | BUILD_ARGS="-cargs -fPIE -largs -pie" 10 | STATIC_ARGS="-bargs -static -largs -static" 11 | 12 | echo "Removing directories..." 13 | rm -rf words words-build 14 | 15 | echo "Downloading words source..." 16 | if [ ! -f wordsall.zip ] 17 | then 18 | wget https://archives.nd.edu/whitaker/old/wordsall.zip 19 | fi 20 | 21 | mkdir words-build 22 | cd words-build 23 | echo "Extracting words..." 24 | unzip ../wordsall.zip 25 | 26 | echo "Fix line endings..." 27 | dos2unix -O ADDONS.LAT > ADDONS.LAT.new 28 | mv ADDONS.LAT.new ADDONS.LAT 29 | 30 | echo "Building words..." 31 | ${TARGET}gnatmake -O3 words $STATIC_ARGS 32 | ${TARGET}gnatmake makedict $STATIC_ARGS 33 | ${TARGET}gnatmake makestem $STATIC_ARGS 34 | ${TARGET}gnatmake makeefil $STATIC_ARGS 35 | ${TARGET}gnatmake makeinfl $STATIC_ARGS 36 | 37 | echo "Building data files in qemu..." 38 | echo G | $QEMU ./makedict 39 | echo G | $QEMU ./makestem 40 | echo G | $QEMU ./makeefil 41 | echo G | $QEMU ./makeinfl | sed '/\*\*\*\*/d' 42 | 43 | echo "Copying output to 'words'..." 44 | mkdir ../words 45 | cp ADDONS.LAT DICTFILE.GEN EWDSFILE.GEN INDXFILE.GEN INFLECTS.SEC STEMFILE.GEN UNIQUES.LAT words ../WORD.MDV ../words 46 | cd .. 47 | 48 | echo "Stripping words..." 49 | ${TARGET}strip words/words 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | sudo: required 3 | 4 | git: 5 | # Do not use shallow clone, so git "rev-list --count" works 6 | depth: false 7 | 8 | services: 9 | - docker 10 | 11 | android: 12 | components: 13 | - build-tools-30.0.3 14 | - android-30 15 | 16 | script: 17 | - ./build-words-docker.sh 18 | - ./gradlew assembleDebug 19 | - ./gradlew lint 20 | - ./gradlew dokka 21 | - ./gradlew ktlintCheck 22 | 23 | before_deploy: 24 | - cd src/main/assets && tar cJf ../../../words.tar.xz words && cd ../../.. 25 | - | 26 | sed s/%VERSION%/$(printf "r%s.%s" \ 27 | "$(git rev-list --count HEAD)" \ 28 | "$(git rev-parse --short HEAD)")/ \ 29 | .travis/bintray.json.in > .travis/bintray.json 30 | 31 | deploy: 32 | - provider: bintray 33 | skip_cleanup: true 34 | file: .travis/bintray.json 35 | user: ids1024 36 | key: 37 | secure: "G0XaLMNJE2DmTbHCQ348vu+R3BREsQl2+smmRuB7T4GME5BSiN/7+acwYa24EiFMZ/1bKeKfKlqPktgiz72ySSQL2Pd624MIh7OPhtWS6RdXKdI8z3OZe7A+cxAV151qWDBpU8KjflcrRXlnsTKS9dW/IIYUTXEWo1qG5zUFSlkDVlP5SLB/7/PHY5A8U7n+Kp1wp/dIMQQyCzjGIr3BqaPYZByQXV2kTxVWMNfVjN9bliptfbZwnqfPrMruSJPiXYLsdP7VSEqazri3zPn9W3tdoku1MIuIEl9HXPVx1OflI/n5nTzOvun054J6kxxcoqFZZNXa879dyh7bJ9vN7KzP1Ksi2CZXh9G1vSaKRBELsoNJhNP9u6n9koZjqkgzzQwTbAvNqzazSXnR0w5PSjRpvSZrHbrjUpv7xAF2W5DljNYI7039Wne/N5V1Gfin+cW+NHD6BXoXKGfR4mWHRMUp3JQjZjbf+a5Js/cYfMivJTfXGj7f4FBbo3BPN9sR77RNeFihbbTLOEFNQFZhgT7Gz8Cj6633dOA/K1/5Ag6NS38QQX1/3kyOOPKDyU7Nd1sg2ntPQFa1mAQb4mnh8qu+J13stDstaGxg3bi/nsBdwZpOo8WYrFux9j7syfymmIkjzaQWBcURlqMe8AG/QAd+ATtV9vJj4v/l5L6MMg4=" 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Whitaker's Words for Android 2 | ============================ 3 | 4 | [![Build Status](https://travis-ci.org/ids1024/whitakers-words-android.svg?branch=master)](https://travis-ci.org/ids1024/whitakers-words-android) 5 | 6 | [`words`](http://archives.nd.edu/whitaker/words.htm) is a dictionary and morphological analysis tool by Colonel William Whitaker for Latin that accepts words of any form and gives the case/tense/etc. along with a short definition. This app provides a native Android interface that wraps the original command line program. 7 | 8 | This app is on [Google Play](https://play.google.com/store/apps/details?id=com.ids1024.whitakerswords). 9 | 10 | License 11 | ------- 12 | 13 | Whitaker's Ada code is under the license in the [`words.LICENSE`](words.LICENSE) file, while all the code here is under the [MIT license](LICENSE). 14 | 15 | Compiling 16 | --------- 17 | 18 | ### Downloading or building the words executable and data files 19 | 20 | Building the Ada code is problematic, since Android's NDK only supports C and C++. The `words` directory has scripts for building a copy of GCC with Ada support, targeting Android. Moreover, it has data files that need to be build, and potentially differ by architecture. So the build script here uses `qemu` to generate those. 21 | 22 | To provide such I toolchain, I've built a [docker image](https://hub.docker.com/r/ids1024/ada-android/), which you can download and use, or [build yourself](https://github.com/ids1024/ada-android-docker) (given enough CPU time and disk space). Prebuilt copies of words, automatically built by Travis CI, are available on [Bintray](https://bintray.com/ids1024/whitakers-words-android/master). 23 | 24 | To compile this app, you'll need to obtain a copy of words in one of two ways: 25 | 26 | Download prebuilt words from Bintray: 27 | 28 | ```bash 29 | ./fetch-words-bintray.sh 30 | ``` 31 | 32 | Or, download the docker image and build words: 33 | 34 | ```bash 35 | ./build-words-docker.sh 36 | ``` 37 | 38 | ### Compiling the app 39 | 40 | This uses the standard gradle build system for Android, so any documentation on that will apply, or you can use Android Studio. 41 | 42 | For instance, `./gradlew installDebug` will build the app and install it on your device. 43 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /graphics/ic_launcher.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 37 | 38 | 40 | caesarcaesar N 3 1 NOM S M 66 | 67 | -------------------------------------------------------------------------------- /graphics/feature_graphic.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 35 | 37 | 40 | caesarcaesar N 3 1 NOM S M 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/parse_words.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.graphics.Color 4 | import android.graphics.Typeface 5 | import android.text.SpannableStringBuilder 6 | import android.text.TextUtils 7 | import android.text.style.ForegroundColorSpan 8 | import android.text.style.StyleSpan 9 | 10 | /** 11 | * Parses plain text from `words` to add basic formatting (such as italics). 12 | */ 13 | fun parse_words(input: String): ArrayList { 14 | val results = ArrayList() 15 | 16 | var processed_result = SpannableStringBuilder() 17 | for (line in input.split("\n".toRegex())) { 18 | val words = line.split(" +".toRegex()) 19 | var handled_line = TextUtils.join(" ", words) 20 | var pearse_code = 0 21 | if (words.isNotEmpty() && words[0].length == 2) { 22 | try { 23 | pearse_code = Integer.parseInt(words[0]) 24 | handled_line = handled_line.substring(3) 25 | } catch (e: NumberFormatException) { 26 | } 27 | } 28 | // Indent meanings 29 | if (pearse_code == 3) { 30 | handled_line = " $handled_line" 31 | } 32 | 33 | if (line.isEmpty() || line == "*") { 34 | if (line == "*") { 35 | processed_result.append("*") 36 | } 37 | val finalresult = processed_result.toString().trim() 38 | if (finalresult.isNotEmpty()) { 39 | results.add(processed_result) 40 | } 41 | processed_result = SpannableStringBuilder() 42 | continue 43 | } 44 | 45 | val startindex = processed_result.length 46 | processed_result.append(handled_line + "\n") 47 | 48 | var span: Any? = null 49 | var endindex = processed_result.length 50 | when (pearse_code) { 51 | // Forms 52 | 1 -> { 53 | span = StyleSpan(Typeface.BOLD) 54 | endindex = startindex + words[1].length 55 | } 56 | // Dictionary forms 57 | 2 -> { 58 | // A HACK(?) for parsing output of searches like 59 | // "quod", which show shorter output for dictionary forms 60 | if (!words[1].startsWith("[")) { 61 | var index = 1 62 | endindex = startindex 63 | do { 64 | endindex += words[index].length + 1 65 | index += 1 66 | } while (words[index - 1].endsWith(",")) 67 | 68 | span = StyleSpan(Typeface.BOLD) 69 | } 70 | } 71 | // Meaning 72 | 3 -> span = StyleSpan(Typeface.ITALIC) 73 | // Not found 74 | 4 -> span = ForegroundColorSpan(Color.RED) 75 | // Addons 76 | 5 -> { 77 | } 78 | // Tricks/syncope/addons? 79 | 6 -> { 80 | } 81 | } 82 | processed_result.setSpan(span, startindex, endindex, 0) 83 | } 84 | val finalresult = processed_result.toString().trim() 85 | if (finalresult.isNotEmpty()) { 86 | results.add(processed_result) 87 | } 88 | 89 | return results 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/SearchFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.content.SharedPreferences 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.appcompat.widget.SearchView.OnQueryTextListener 10 | import androidx.fragment.app.Fragment 11 | import androidx.preference.PreferenceManager 12 | import androidx.recyclerview.widget.DividerItemDecoration 13 | import androidx.recyclerview.widget.LinearLayoutManager 14 | import com.ids1024.whitakerswords.databinding.SearchBinding 15 | import java.io.IOException 16 | 17 | /** 18 | * Fragment providing the search UI. 19 | */ 20 | class SearchFragment(var english_to_latin: Boolean) : Fragment() { 21 | private var search_term: String = "" 22 | private lateinit var preferences: SharedPreferences 23 | private lateinit var words: WordsWrapper 24 | private lateinit var binding: SearchBinding 25 | 26 | constructor() : this(false) 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | 31 | preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) 32 | words = WordsWrapper(requireContext()) 33 | preferences.registerOnSharedPreferenceChangeListener { _, _ -> 34 | words.updateConfigFile() 35 | } 36 | } 37 | 38 | override fun onCreateView( 39 | inflater: LayoutInflater, 40 | container: ViewGroup?, 41 | savedInstanceState: Bundle? 42 | ): View { 43 | binding = SearchBinding.inflate(inflater, container, false) 44 | return binding.root 45 | } 46 | 47 | override fun onDestroyView() { 48 | binding.searchView.setOnQueryTextListener(null) 49 | super.onDestroyView() 50 | } 51 | 52 | override fun onActivityCreated(savedInstanceState: Bundle?) { 53 | super.onActivityCreated(savedInstanceState) 54 | 55 | binding.recyclerView.layoutManager = LinearLayoutManager(context) 56 | binding.recyclerView.addItemDecoration(DividerItemDecoration(binding.recyclerView.context, DividerItemDecoration.VERTICAL)) 57 | 58 | if (english_to_latin) { 59 | binding.searchView.queryHint = resources.getString(R.string.english_to_latin) 60 | } else { 61 | binding.searchView.queryHint = resources.getString(R.string.latin_to_english) 62 | } 63 | 64 | binding.searchView.setOnQueryTextListener(object : OnQueryTextListener { 65 | override fun onQueryTextSubmit(query: String): Boolean { 66 | searchWord(query) 67 | binding.searchView.clearFocus() 68 | return true 69 | } 70 | 71 | override fun onQueryTextChange(query: String): Boolean { 72 | if (preferences.getBoolean("search_on_keypress", true)) { 73 | searchWord(query) 74 | } 75 | return true 76 | } 77 | }) 78 | 79 | if (savedInstanceState != null) { 80 | english_to_latin = savedInstanceState.getBoolean("english_to_latin") 81 | searchWord(savedInstanceState.getString("search_term")!!) 82 | } else if (search_term != "") { 83 | searchWord(search_term) 84 | } 85 | } 86 | 87 | override fun onSaveInstanceState(outState: Bundle) { 88 | outState.putString("search_term", search_term) 89 | outState.putBoolean("english_to_latin", english_to_latin) 90 | super.onSaveInstanceState(outState) 91 | } 92 | 93 | private fun searchWord(search_term: String) { 94 | this.search_term = search_term 95 | 96 | val result: String 97 | try { 98 | result = words.executeWords(search_term, english_to_latin) 99 | } catch (ex: IOException) { 100 | val toast = Toast.makeText(context, "Failed to execute words!", Toast.LENGTH_SHORT) 101 | toast.show() 102 | return 103 | } 104 | 105 | val results = parse_words(result) 106 | binding.recyclerView.adapter = SearchAdapter(results) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Whitaker\'s Words 4 | Settings 5 | Latin-English 6 | English-Latin 7 | 8 | Open drawer 9 | Close drawer 10 | 11 | 12 | Words Settings 13 | Miscellaneous 14 | 15 | Trim output 16 | Removes least likely results. If results have been trimmed, output will be followed by an astrisk (*). Default: true 17 | Do unknowns only 18 | Show only unrecognized words. Default: false 19 | Ignore unknown names 20 | Ignore capitalized words longer than three letters if they are not in the dictionary, instead of printing UNKNOWN. Default: true 21 | Ignore unknown caps 22 | Ignore words in all caps if they are not in the dictionary, instead of printing UNKNOWN. Default: true 23 | Do compounds 24 | Look ahead for the verb TO_BE (or iri) when it finds a verb participle, with the expectation of finding a compound perfect tense or periphastic. Default: true 25 | Do fixes 26 | When a result for a word is not found, try again with various prefixes and suffixes. Generally useful, but sometimes yields false positives. Default: true 27 | Do tricks 28 | When a result is not found, try replacements like cl -> cul, vul -> vol, ads -> ass, inp -> imp, etc., to provide for recognized variants of classical spelling, though false positives can result. Default: true 29 | Do dictionary forms 30 | Output line with forms associated with dictionary entry (nom/gen for noun, etc.). Default: true 31 | Show age 32 | Show what during periods a word/inflection was in use. Default: true 33 | Show frequency 34 | Show how frequently a word/inflection was used. Default: true 35 | Do examples 36 | Provide examples for usage of case/tenses/etc. Produces lengthy output. Default: false 37 | Do only meanings 38 | Output meaning without inflection details. Default: false 39 | Do stems for unknown 40 | When unable to find a proper match in the dictionary, list the dictionary entries around the unknown. This will likely catch a substantive for which only the ADJ stem appears in dictionary, an ADJ for which there is only a N stem, etc. Default: false 41 | 42 | Search on keypress 43 | Displays result while you type, without needing to press search. Default: true 44 | 45 | 46 | About 47 | About Whitaker\'s Words 48 | 49 | The \'words\' program was created by Wiliam Whitaker. It is open 50 | source, under the following terms:\n\n 51 | 52 | \"All parts of the WORDS system, source code and data files, are made freely available to anyone who wishes to use them, for whatever purpose.\"\n\n 53 | 54 | An archieved version of Whitaker\'s website is available at 55 | http://archives.nd.edu/whitaker/words.htm.\n\n 56 | 57 | This app is Open Source. Other than Whitaker\'s code, it under the MIT 58 | License. The source code is available at 59 | https://github.com/ids1024/whitakers-words-android.\n\n 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 13 | 19 | 25 | 31 | 37 | 43 | 49 | 55 | 61 | 67 | 73 | 79 | 85 | 86 | 89 | 95 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/WhitakersWords.kt: -------------------------------------------------------------------------------- 1 | package com.ids1024.whitakerswords 2 | 3 | import android.content.res.Configuration 4 | import android.os.Bundle 5 | import android.view.MenuItem 6 | import androidx.appcompat.app.ActionBarDrawerToggle 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.appcompat.app.AppCompatDelegate 9 | import androidx.fragment.app.Fragment 10 | import androidx.preference.PreferenceManager 11 | import com.ids1024.whitakerswords.databinding.MainBinding 12 | 13 | class WhitakersWords : AppCompatActivity() { 14 | private var fragments = HashMap() 15 | private lateinit var action_bar_drawer_toggle: ActionBarDrawerToggle 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | val binding = MainBinding.inflate(layoutInflater) 20 | setContentView(binding.root) 21 | 22 | val preferences = PreferenceManager.getDefaultSharedPreferences(this) 23 | val nightMode = if (preferences.getBoolean("light_theme", false)) { 24 | AppCompatDelegate.MODE_NIGHT_NO 25 | } else { 26 | AppCompatDelegate.MODE_NIGHT_YES 27 | } 28 | AppCompatDelegate.setDefaultNightMode(nightMode) 29 | 30 | supportActionBar!!.setDisplayHomeAsUpEnabled(true) 31 | 32 | supportFragmentManager.addOnBackStackChangedListener { 33 | val fragment = supportFragmentManager.findFragmentById(R.id.content) 34 | 35 | val (title, item) = when (fragment) { 36 | is SearchFragment -> { 37 | if (fragment.english_to_latin) { 38 | Pair(R.string.app_name, R.id.action_english_to_latin) 39 | } else { 40 | Pair(R.string.app_name, R.id.action_latin_to_english) 41 | } 42 | } 43 | is SettingsFragment -> 44 | Pair(R.string.settings, R.id.action_settings) 45 | is AboutFragment -> 46 | Pair(R.string.about_long_title, R.id.action_about) 47 | else -> throw RuntimeException() // Unreachable 48 | } 49 | 50 | supportActionBar!!.title = resources.getString(title) 51 | binding.navView.setCheckedItem(item) 52 | } 53 | 54 | var cur_fragment = R.id.action_latin_to_english 55 | if (savedInstanceState != null) { 56 | for (k in savedInstanceState.keySet()) { 57 | if (k.startsWith("fragment_")) { 58 | val fragment = supportFragmentManager.getFragment(savedInstanceState, k)!! 59 | fragments[k.substring(9).toInt()] = fragment 60 | } 61 | } 62 | cur_fragment = savedInstanceState.getInt("cur_fragment", cur_fragment) 63 | } 64 | supportFragmentManager.beginTransaction() 65 | .replace(R.id.content, getFragment(cur_fragment)) 66 | .addToBackStack(null) 67 | .commit() 68 | 69 | binding.navView.inflateMenu(R.menu.navigation) 70 | binding.navView.setNavigationItemSelectedListener { item -> 71 | binding.drawerLayout.closeDrawers() 72 | val fragment = getFragment(item.itemId) 73 | supportFragmentManager.beginTransaction() 74 | .addToBackStack(null) 75 | .replace(R.id.content, fragment) 76 | .commit() 77 | 78 | true 79 | } 80 | 81 | action_bar_drawer_toggle = ActionBarDrawerToggle( 82 | this, binding.drawerLayout, R.string.open_drawer, 83 | R.string.close_drawer 84 | ) 85 | } 86 | 87 | override fun onSaveInstanceState(outState: Bundle) { 88 | val cur_fragment = supportFragmentManager.findFragmentById(R.id.content) 89 | for ((k, v) in fragments) { 90 | if (cur_fragment == v) { 91 | outState.putInt("cur_fragment", k) 92 | } 93 | supportFragmentManager.putFragment(outState, "fragment_$k", v) 94 | } 95 | super.onSaveInstanceState(outState) 96 | } 97 | 98 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 99 | return action_bar_drawer_toggle.onOptionsItemSelected(item) 100 | } 101 | 102 | override fun onConfigurationChanged(newConfig: Configuration) { 103 | super.onConfigurationChanged(newConfig) 104 | action_bar_drawer_toggle.onConfigurationChanged(newConfig) 105 | } 106 | 107 | private fun getFragment(id: Int): Fragment { 108 | var fragment = fragments[id] 109 | if (fragment == null) { 110 | fragment = when (id) { 111 | R.id.action_latin_to_english -> 112 | SearchFragment(false) 113 | R.id.action_english_to_latin -> 114 | SearchFragment(true) 115 | R.id.action_settings -> 116 | SettingsFragment() 117 | R.id.action_about -> 118 | AboutFragment() 119 | else -> 120 | throw RuntimeException() // Unreachable 121 | } 122 | 123 | fragments[id] = fragment 124 | } 125 | return fragment 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/ids1024/whitakerswords/WordsWrapper.kt: -------------------------------------------------------------------------------- 1 | // libwords.so is really an executable, named that way because of how Android does things. 2 | // See https://stackoverflow.com/questions/63800440/android-cant-execute-process-for-android-api-29-android-10-from-lib-arch 3 | 4 | package com.ids1024.whitakerswords 5 | 6 | import android.content.Context 7 | import android.util.Log 8 | import androidx.preference.PreferenceManager 9 | import java.io.BufferedReader 10 | import java.io.File 11 | import java.io.FileOutputStream 12 | import java.io.IOException 13 | import java.io.InputStreamReader 14 | import java.io.StringWriter 15 | import java.util.Locale 16 | 17 | private const val TAG = "words" 18 | 19 | private fun emptyDirectory(f: File) { 20 | val directoryContents = f.listFiles() 21 | if (directoryContents != null) { 22 | for (subFile in directoryContents) { 23 | if (!subFile.deleteRecursively()) { 24 | Log.w(TAG, "Unable to delete ${f.path}") 25 | } 26 | } 27 | } else { 28 | Log.w(TAG, "Unable to clear ${f.path}") 29 | } 30 | } 31 | 32 | /** 33 | * Wraps the `words` binary. This handles extraction from the apk and execution. 34 | */ 35 | class WordsWrapper(private val context: Context) { 36 | // The version number of the APK as specified in the manifest. 37 | private val apkVersion = context.packageManager 38 | .getPackageInfo(context.packageName, 0) 39 | .versionCode 40 | private val preferences = PreferenceManager.getDefaultSharedPreferences(context) 41 | 42 | init { 43 | copyFiles() 44 | } 45 | 46 | /** Deletes all files under the files directory. */ 47 | private fun deleteLegacyDataDirectoryContents() { 48 | emptyDirectory(context.filesDir) 49 | } 50 | 51 | /** Ensures the appropriate versioned cache directory is created. The 52 | * version number is derived from the APK version code. 53 | * 54 | * 55 | * Older directories and their contents from a prior APK version will be 56 | * removed automatically. 57 | */ 58 | private fun createAndCleanupCacheDirectories() { 59 | val versionedCacheDir = getFile("") 60 | 61 | if (versionedCacheDir.exists()) { 62 | return 63 | } 64 | 65 | // delete the entire contents of cache and then create the versioned directory 66 | emptyDirectory(context.cacheDir) 67 | versionedCacheDir.mkdirs() 68 | } 69 | 70 | private fun getFile(filename: String): File { 71 | return File(context.cacheDir, "$apkVersion/$filename") 72 | } 73 | 74 | @Throws(IOException::class) 75 | private fun copyFiles() { 76 | deleteLegacyDataDirectoryContents() 77 | createAndCleanupCacheDirectories() 78 | 79 | for (filename in context.assets.list("words")!!) { 80 | copyFile(filename) 81 | } 82 | 83 | updateConfigFile() 84 | } 85 | 86 | @Throws(IOException::class) 87 | private fun copyFile(filename: String) { 88 | val outputFile = getFile(filename) 89 | // if the file already exists, don't copy it again 90 | if (outputFile.exists()) { 91 | return 92 | } 93 | 94 | context.assets.open("words/$filename").use { ins -> 95 | FileOutputStream(outputFile).use { fos -> 96 | ins.copyTo(fos) 97 | } 98 | } 99 | } 100 | 101 | // TODO(tcj): Execute this is another thread to prevent UI deadlocking 102 | /** 103 | * Executes `words`. 104 | */ 105 | @Throws(IOException::class) 106 | fun executeWords(text: String, english_to_latin: Boolean): String { 107 | val wordspath = context.applicationInfo.nativeLibraryDir + "/libwords.so" 108 | val command = if (english_to_latin) { 109 | arrayOf(wordspath, "~E", text) 110 | } else { 111 | arrayOf(wordspath, text) 112 | } 113 | val process = Runtime.getRuntime().exec(command, null, getFile("")) 114 | 115 | val output = StringWriter() 116 | BufferedReader(InputStreamReader(process.inputStream)).use { ins -> 117 | ins.copyTo(output) 118 | } 119 | 120 | try { 121 | process.waitFor() 122 | 123 | val exitValue = process.exitValue() 124 | if (exitValue != 0) { 125 | Log.e(TAG, "words subprocess returned $exitValue") 126 | } 127 | } catch (ex: InterruptedException) { 128 | Thread.currentThread().interrupt() 129 | throw RuntimeException(ex) 130 | } 131 | 132 | return output.toString() 133 | } 134 | 135 | /** 136 | * Generates `WORDS.MOD` file from the app's preferences. 137 | */ 138 | @Throws(IOException::class) 139 | fun updateConfigFile() { 140 | val file = getFile("WORD.MOD") 141 | val fos = FileOutputStream(file) 142 | for (setting in arrayOf("trim_output", "do_unknowns_only", "ignore_unknown_names", "ignore_unknown_caps", "do_compounds", "do_fixes", "do_dictionary_forms", "show_age", "show_frequency", "do_examples", "do_only_meanings", "do_stems_for_unknown")) { 143 | if (preferences.contains(setting)) { 144 | val value = if (preferences.getBoolean(setting, false)) "Y" else "N" 145 | val line = setting.uppercase(Locale.US) + " " + value + "\n" 146 | fos.write(line.toByteArray()) 147 | } 148 | } 149 | fos.close() 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | --------------------------------------------------------------------------------